原文地址: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中的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()
我们可以轻松的实现inflate
、addView
、removeView
。
自定义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).