Android协调员:解决你还不知道你存在的问题

原文地址:https://hackernoon.com/coordinators-solving-a-problem-you-didnt-even-know-you-had-e86623f15ebf#.nm0brlclo

Square公司有一个大多人还不知道的库-Coordinators,不是一个很具有描述性的名字,甚至在Github上也没有太多的信息。

简单的生命周期,无论是安卓还是其他

简单的示例代码:

Class ExampleCoordinator extends Coordinator{
  @Override public void attach(View view){
    //附加监听器,加载状态,其他操作
  }
  
  @Override public void detach(View view){
    //解绑监听器,保存状态等
  }
}

你可以在任何View中绑定一个Coordinator

Coordinators.bind(view,coordinatorProvider);

Activity

每个人都知道Activity,他在App中代表单个界面,而且是你定义的特定意图的入口点。

如果要开启一个新的Activity,可以使用Intent,就像activity.startActivity(activity,OtherActivity.class)

他们也不能嵌套,虽然之前可以使用LocalActivityManager和ActivityGroup实现嵌套,但是在Api11之后已经过时了。

Activities通过使用setContentView()来显示View,通常假定他只能显示一种特定的布局,如果需要一个不同的界面,你就需要使用一个新的Activity代替。

Fragments

完整的Fragment和Activity的生命周期

Fragment是屏幕上的一个片段,技术上显示一个自定义的视图组,被Activity中的FragmentManager所管理,Fragment与FragmentController所关联,而FragmentController与宿主Activity相关联。

他们的设计主要是为了创建‘子屏幕功能’,继承所有重要的生命周期回调,比如onCreate()onActivityResult()onPermissionResult()、甚至是更重要的onSaveInstanceState(Bundle bundle).

他也有自己的生命周期回调方法,比如onAttach()onCreateView()onActivityCreated()onDestroyView()onDetach().

你可以通过getSupportFragmentManager().beginTransaction().add(R.id.container,new MyFragment()).commit();来创建一个Fragment。

大多数时候,你只需要访问你绑定的Activity,和onCreateView()onDestroyView().

如果有复杂的情况,你可以在一个Fragment中嵌套一个Fragment,就可以通过Fragment的getChildFragmentManager(),这有时会导致混乱,无论你使用getFragmentManager或者是getChildFragmentManager().

有人可能会问,我们在我们Activity的子视图中真的需要这些生命周期函数吗。

自定义Viewgroups

你知道Activity和Fragment中有什么共同点吗,他们显示Viewgroups,都是以xml格式声明的,并且他们还有一些不错的回调。

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();
    }
}


    

我们可以扩展我们的视图组(DrawableLayout、RelativeLayout、FrameLayout,LinearLayout等等),并选择我们自己定义的视图组作为需要继承的来源。

代替getSupportFragmentManager().beginTransaction().blah().commit()我们可以轻松的实现inflateaddViewremoveView

自定义View创建通过inflation,他有四个构造方法,需要通过继承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);
  }
}
```
你可以创建一个协调器,他可以是任何一个POJO类,并可以附加到任何自定义视图组,而无需扩展他。
使用协调器,你会收到一个```attach(View)```和```detach(View)```回调,代替原来Viewgroup的```onAttachToWindow()```和```onDetachedFromWindow()```回调。

然后将协调器实例作为标记存储,然后,协调器可以获得```Coordinator coordinator = (Coordinator)getTag(R.id.coordinator)```,这当然是由Coordinators类提供的。

这有什么好处?现在我们的类是一个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个构造函数,以便定义我们自己的“视图控制器”逻辑。
我们可以直接从Dagger直接注入我们新创建的协调器,而不需要void inject(MyCoordinator协调器)
我们不再需要一个Context来创建一个“视图”的实例,我们可以只创建一个Coordinator来代替。

### 结论


就个人而言,我喜欢协调员所采取的方向。我不完全确定onAttachedToWindow()和onDetachedFromWindow()回调本身是否真的足够,但在我们自己的类中定义一个attach(View)和detach(View)方法,然后将它与一个标签相关联并不难。

作为回报,我们收到一个POJO视图控制器,可以直接通过Dagger创建和注入,而无需创建4个构造函数,并在自己的XML文件中固定自定义视图组。

如果导航逻辑与活动/片段分离,并且移动到Presenter层,则新的协调器方法尤其适用。它使得更清洁的代码 - 应用程序状态转换不是视图应该管理的,毕竟。

这个是我的测试代码地址: [https://github.com/Zhuinden/simple-stack/tree/master/simple-stack-example-mvp](https://github.com/Zhuinden/simple-stack/tree/master/simple-stack-example-mvp).

你可能感兴趣的:(Android协调员:解决你还不知道你存在的问题)