Square公司有一個大多人還不知道的庫-Coordinators
,不是一個很具有描述性的名字瞳氓,甚至在Github上也沒有太多的信息梧躺。
簡單的生命周期,無論是安卓還是其他
簡單的示例代碼:
Class ExampleCoordinator extends Coordinator{
@Override public void attach(View view){
//附加監(jiān)聽器哼蛆,加載狀態(tài),其他操作
}
@Override public void detach(View view){
//解綁監(jiān)聽器圈驼,保存狀態(tài)等
}
}
你可以在任何View
中綁定一個Coordinator
Coordinators.bind(view,coordinatorProvider);
Activity
每個人都知道Activity人芽,他在App中代表單個界面,而且是你定義的特定意圖的入口點绩脆。
如果要開啟一個新的Activity萤厅,可以使用Intent橄抹,就像activity.startActivity(activity,OtherActivity.class)
他們也不能嵌套,雖然之前可以使用LocalActivityManager和ActivityGroup實現(xiàn)嵌套惕味,但是在Api11之后已經(jīng)過時了楼誓。
Activities通過使用setContentView()
來顯示View,通常假定他只能顯示一種特定的布局,如果需要一個不同的界面名挥,你就需要使用一個新的Activity代替疟羹。
Fragments
Fragment是屏幕上的一個片段,技術(shù)上顯示一個自定義的視圖組禀倔,被Activity中的FragmentManager所管理榄融,F(xiàn)ragment與FragmentController所關(guān)聯(lián),而FragmentController與宿主Activity相關(guān)聯(lián)救湖。
他們的設(shè)計主要是為了創(chuàng)建‘子屏幕功能’愧杯,繼承所有重要的生命周期回調(diào),比如onCreate()
鞋既、onActivityResult()
力九、onPermissionResult()
、甚至是更重要的onSaveInstanceState(Bundle bundle)
.
他也有自己的生命周期回調(diào)方法邑闺,比如onAttach()
跌前、onCreateView()
、onActivityCreated()
陡舅、onDestroyView()
抵乓、onDetach()
.
你可以通過getSupportFragmentManager().beginTransaction().add(R.id.container,new MyFragment()).commit();
來創(chuàng)建一個Fragment。
大多數(shù)時候蹭沛,你只需要訪問你綁定的Activity臂寝,和onCreateView()
和onDestroyView()
.
如果有復(fù)雜的情況,你可以在一個Fragment中嵌套一個Fragment摊灭,就可以通過Fragment的getChildFragmentManager()
,這有時會導(dǎo)致混亂咆贬,無論你使用getFragmentManager
或者是getChildFragmentManager()
.
有人可能會問,我們在我們Activity的子視圖中真的需要這些生命周期函數(shù)嗎帚呼。
自定義Viewgroups
你知道Activity和Fragment中有什么共同點嗎掏缎,他們顯示Viewgroups,都是以xml格式聲明的煤杀,并且他們還有一些不錯的回調(diào)眷蜈。
public class MyCustomViewGroup extends RelativeLayout {
public MyCustomViewGroup(Context context) {
super(context);
init(context);
}
public MyCustomViewGroup(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public MyCustomViewGroup(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
@TargetApi(21)
public MyCustomViewGroup(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init(context);
}
private void init(Context context) {
if(!isInEditMode()) {
// ...
}
}
@OnClick(R.id.button)
public void doSomething() {
// ...
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
ButterKnife.bind(this);
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
// pretty much `onStart()`
}
@Override
protected void onDetachedFromWindow() {
// pretty much `onStop()`
super.onDetachedFromWindow();
}
}
<?xml version="1.0" encoding="utf-8"?>
<fully.qualified.MyCustomViewGroup xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="Go to next"/> <!-- centerInParent works because -->
<!-- custom viewgroup is also RelativeLayout -->
</fully.qualified.MyCustomViewGroup>
我們可以擴展我們的視圖組(DrawableLayout、RelativeLayout沈自、FrameLayout酌儒,LinearLayout等等),并選擇我們自己定義的視圖組作為需要繼承的來源枯途。
代替getSupportFragmentManager().beginTransaction().blah().commit()
我們可以輕松的實現(xiàn)inflate
忌怎、addView
籍滴、removeView
。
自定義View創(chuàng)建通過inflation榴啸,他有四個構(gòu)造方法孽惰,需要通過繼承Android特定的類來工作。
Coordinators
為了脫離自定義視圖組的限制鸥印,我們可以將所有的試圖控制器邏輯移出Viewgroup本身勋功,并將其作為標記附加進去。
下面是他的工作原理:
public final class Coordinators{
private Coordinators(){
}
public static void bind(View view,CoordinatorProvider provider){
View.OnAttachStateListener binding = new Binding(coordinator,view);
view.addOnAttachStateChangeListener(binding);
}
}
```
```
final class Binding implements View.OnAttachStateChangeListener{
Binding(Coordinator coordinator,View view){
this.coordinator = coordinator;
this.view = view;
}
@Override public void onViewAttachedToWindow(@NonNull View v){
coordinator.attach(view);
view.setTag(R.id.coordinator,null);
}
@Override public void onViewDetachedFromWindow(@NonNull View view){
coordinator.detach(view);
view.setTag(R.id.coordinator,null);
}
}
```
你可以創(chuàng)建一個協(xié)調(diào)器库说,他可以是任何一個POJO類狂鞋,并可以附加到任何自定義視圖組,而無需擴展他璃弄。
使用協(xié)調(diào)器要销,你會收到一個```attach(View)```和```detach(View)```回調(diào),代替原來Viewgroup的```onAttachToWindow()```和```onDetachedFromWindow()```回調(diào)夏块。
然后將協(xié)調(diào)器實例作為標記存儲,然后纤掸,協(xié)調(diào)器可以獲得```Coordinator coordinator = (Coordinator)getTag(R.id.coordinator)```,這當然是由Coordinators類提供的脐供。
這有什么好處?現(xiàn)在我們的類是一個POJO借跪,我們可以直接使用Dagger注入這個類政己,而不必指定一個void inject(MyCustomViewGroup group)方法。
```
public class TasksCoordinator
extends Coordinator {
@Inject // <-- !
public TasksCoordinator() {
}
@Inject
TasksPresenter tasksPresenter;
@OnClick(R.id.noTasksAdd)
void openAddNewTask() {
tasksPresenter.openAddNewTask();
}
@BindView(R.id.noTasks)
View noTasksView;
@BindView(R.id.noTasksIcon)
ImageView noTaskIcon;
// ...
@Override
protected final void attach(View view) {
this.unbinder = ButterKnife.bind(this, view);
presenter.takeCoordinator(this);
}
@Override
protected final void detach(View view) {
presenter.dropCoordinator(this);
unbinder.unbind();
}
}
```
有了這些掏愁,我們獲得了以下好處:
我們不需要繼承一個視圖組和定義4個構(gòu)造函數(shù)歇由,以便定義我們自己的“視圖控制器”邏輯。
我們可以直接從Dagger直接注入我們新創(chuàng)建的協(xié)調(diào)器果港,而不需要void inject(MyCoordinator協(xié)調(diào)器)
我們不再需要一個Context來創(chuàng)建一個“視圖”的實例沦泌,我們可以只創(chuàng)建一個Coordinator來代替。
### 結(jié)論
就個人而言辛掠,我喜歡協(xié)調(diào)員所采取的方向谢谦。我不完全確定onAttachedToWindow()和onDetachedFromWindow()回調(diào)本身是否真的足夠,但在我們自己的類中定義一個attach(View)和detach(View)方法萝衩,然后將它與一個標簽相關(guān)聯(lián)并不難回挽。
作為回報,我們收到一個POJO視圖控制器猩谊,可以直接通過Dagger創(chuàng)建和注入千劈,而無需創(chuàng)建4個構(gòu)造函數(shù),并在自己的XML文件中固定自定義視圖組牌捷。
如果導(dǎo)航邏輯與活動/片段分離墙牌,并且移動到Presenter層涡驮,則新的協(xié)調(diào)器方法尤其適用。它使得更清潔的代碼 - 應(yīng)用程序狀態(tài)轉(zhuǎn)換不是視圖應(yīng)該管理的憔古,畢竟遮怜。
這個是我的測試代碼地址: [https://github.com/Zhuinden/simple-stack/tree/master/simple-stack-example-mvp](https://github.com/Zhuinden/simple-stack/tree/master/simple-stack-example-mvp).