上一篇我们介绍了只用Databinding的方式快速实现了一个主从联动的组合自定义控件,今天我们要实现的是一个无限扩展的组织树控件。 先看效果:
效果介绍
不像网上的一下demo中看不中用,我的开源库开箱即用,可以直接用于生产环境,高内聚,低耦合,支持各个层级样式自定义,可以自由的加载不同的xml,修改viewHolder,支持异步加载子节点。完美的状态保持,activity重建也不会丢失状态。
设计思路
- 首先对数据进行建模,每一个层级抽象为一个Node,每一个Node包含若干个子Node,类似N叉树的形式。最顶端的Node为Root node。
public interface Nodeextends Checkable { boolean isLeaf(); boolean isLeafParent(); boolean isRoot(); LiveData
> getItems();}复制代码
注意,这里定义子节点是为了支持异步加载
- 自定义容器viewGroup,这个容器在适当的时间创建自身
public class KanaView extends FrameLayout { private KanaPresenter mPresenter; public KanaView(@NonNull Context context) { super(context); init(); } public KanaView(@NonNull Context context, @Nullable AttributeSet attrs) { super(context, attrs); init(); } public KanaView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } private void init() { } public void setPresenter(KanaPresenter presenter) { if (mPresenter != null && !mPresenter.equals(presenter)) { mPresenter.destroy(); } mPresenter = presenter; } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); if (mPresenter != null) { mPresenter.destroy(); } }}复制代码
业务逻辑包含在Presenter里
public class KanaPresenter implements Presenter { private final LifecycleOwner lifecycleOwner; private final KanaView mKanaView; private final RecyclerView rv; private final Node rootNode; private final Observer> listObserver; private final KanaPresenterFactory kanaPresenterFactory; public KanaPresenter(KanaView parent, LifecycleOwner lifecycleOwner, Node root, KanaPresenterFactory factory) { ViewGroup container = parent.findViewById(R.id.container); if (container == null) { LayoutInflater.from(parent.getContext()).inflate(R.layout.hof_kana_view, parent, true); } container = parent.findViewById(R.id.container); kanaPresenterFactory = factory; this.lifecycleOwner = lifecycleOwner; rv = container.findViewById(R.id.rv1); mKanaView = container.findViewById(R.id.kana); mKanaView.removeAllViews(); this.rootNode = root; if (rootNode.isLeafParent()) { mKanaView.setVisibility(View.GONE); ViewGroup.LayoutParams layoutParams = rv.getLayoutParams(); layoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT; rv.setLayoutParams(layoutParams); //return; } listObserver = obtainListObserver(mKanaView, rv, rootNode); rootNode.getItems().observe(lifecycleOwner, listObserver); } public void destroy() { rootNode.getItems().removeObserver(listObserver); } /** * 继承这个方法如果你想对adapter进行设置 * * @return */ protected Observer
> obtainListObserver(KanaView mKanaView, RecyclerView rv, Node rootNode) { return nodes -> { Context context = mKanaView.getContext(); MOTypedRecyclerAdapter mAdapter = new MOTypedRecyclerAdapter(); rv.setLayoutManager(new LinearLayoutManager(context)); for (int i = 0; i < rv.getItemDecorationCount(); i++) { rv.removeItemDecorationAt(i); } DividerItemDecoration decor = new DividerItemDecoration(context, DividerItemDecoration.VERTICAL); if (rootNode.isRoot()) { decor.setDrawable(ContextCompat.getDrawable(context, android.R.drawable.divider_horizontal_bright)); } else { decor.setDrawable(ContextCompat.getDrawable(context, R.drawable.hof_inset_left_divider)); } rv.addItemDecoration(decor); mAdapter.addDelegate(obtainDelegate(rootNode, this)); rv.setAdapter(mAdapter); mAdapter.setDataSet(nodes); for (Node node : nodes) { if (!node.isLeaf() && node.isChecked().get()) { born(node); break; } } }; } /** * 继承这个方法如果你想改变item的呈现方式 * * @param root * @param presenter * @return */ protected MOTypedRecyclerAdapter.AdapterDelegate obtainDelegate(Node root, KanaPresenter presenter) { return new MOTypedRecyclerAdapter.AdapterDelegate() { @Override public RecyclerView.ViewHolder onCreateViewHolder(MOTypedRecyclerAdapter adapter, ViewGroup parent) { ViewDataBinding binding; if (root.isRoot()) { binding = DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()), R.layout.hof_list_item_tree_root, parent, false); } else if (root.isLeafParent()) { binding = DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()), R.layout.hof_list_item_tree_leaf, parent, false); } else { binding = DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()), R.layout.hof_list_item_tree_middle, parent, false); } return new BindingViewHolder<>(binding); } @Override public void onBindViewHolder(MOTypedRecyclerAdapter moTypedRecyclerAdapter, RecyclerView.ViewHolder viewHolder, Object o) { ((BindingViewHolder) viewHolder).setItem(BR.item, o); ((BindingViewHolder) viewHolder).setItem(BR.presenter, presenter); ((BindingViewHolder) viewHolder).executePendingBindings(); } @Override public boolean isDelegateOf(Class clazz, Object item, int position) { return Node.class.isAssignableFrom(clazz); } }; } protected void onLeafClick(Node node) { node.toggleChecked(); } public void onItemClick(Node node) { if (node.isLeaf()) { onLeafClick(node); return; } if (node.isChecked().get()) { return; } node.setChecked(true); born(node); } private void born(Node node) { mKanaView.setPresenter(kanaPresenterFactory.create(mKanaView, lifecycleOwner, node, kanaPresenterFactory)); }}复制代码
注意这里使用了Factory的设计模式,因为Presenter的创建是根据用户的点击操作和运行状态,我们不直接创建Presenter的实例,而是创建一个Factory,让Factory在适当的时机根据当前运行状态创建对应的Presenter,这样我们可以复写Factory的方式来动态管理我们的Presenter,这也是Java 里Ioc的思想体现
public interface KanaPresenterFactory { KanaPresenter create(KanaView mKanaView, LifecycleOwner lifecycleOwner, Node node, KanaPresenterFactory factory);}复制代码
如何使用
参加demo 我们首先将我们的的Item继承Node
public class MyNode implements Node { private final String name; private final LiveData> items; private final int deep; private final ObservableBoolean checked; private final Node parent; public MyNode(Node parent, String name, int deep) { this.parent = parent; this.name = name; this.deep = deep; this.checked = new ObservableBoolean(); this.items = new MediatorLiveData<>(); LiveData
> source = LiveDataReactiveStreams.fromPublisher(s -> { List ret = new ArrayList<>(); for (int i = 0; i < 20; i++) { ret.add(new MyNode(this, name+"-" + i, deep + 1)); } s.onNext(ret); }); ((MediatorLiveData >) this.items).addSource(source, nodes -> { ((MediatorLiveData
>) this.items).setValue(nodes); ((MediatorLiveData
>) this.items).removeSource(source); }); } public String getName() { return name; } @NonNull @Override public String toString() { return name; } @Override public boolean isLeaf() { return deep >= 5; } @Override public boolean isLeafParent() { return deep >= 4; } @Override public boolean isRoot() { return parent == null; } @Override public LiveData
> getItems() { return items; } @Override public void setChecked(boolean value) { if (checked.get() == value) { return; } notifyParent(checked.get(), value); checked.set(value); } private void notifyParent(boolean pre, boolean now) { if (parent == null) { return; } ((MyNode) parent).onChildCheckChange(this, pre, now); } public void onChildCheckChange(Node child, boolean pre, boolean now) { List value = getItems().getValue(); if (value == null) { return; } if (now && !isLeafParent()) { for (Node node : value) { if (node != child && node.isChecked().get()) { node.setChecked(false); } } } } @Override public ObservableBoolean isChecked() { return checked; } @Override public void toggleChecked() { setChecked(!checked.get()); }}复制代码
然后创建我们的数据源:
public class DataSource { private static LiveDatasData; private static LiveData
sKana; private static LiveData sKana2; public static LiveData get() { if (sData == null) { sData = build(); } return sData; } private static LiveData
build() { LiveData
ret = new MutableLiveData<>(); List list = new ArrayList(); for (int i = 0; i < 20; i++) { Group e = new Group("head" + i); int num = 3 + ((int) (Math.random() * 7)); for (int j = 0; j < num; j++) { e.addChild(new Item("child" + j)); } list.add(e); } ((MutableLiveData
) ret).setValue(list); return ret; } public static LiveData
getKanaNodes() { if (sKana == null) { sKana = buildKana(); } return sKana; } public static LiveData getKanaNodes2() { if (sKana2 == null) { sKana2 = buildKanaOld(); } return sKana2; } private static LiveData buildKana() { LiveData ret = new MutableLiveData<>(); CityNode root = new CityNode(null, "root", 0, CityRepo.getInstance(App.sApp).getMap()); ((MutableLiveData ) ret).setValue(root); return ret; } private static LiveData buildKanaOld() { LiveData ret = new MutableLiveData<>(); ((MutableLiveData ) ret).setValue(new MyNode(null, "root", 0)); return ret; }}复制代码
然后再Activity中只需要简单的几行代码
KanaPresenterFactory factory = (mKanaView, lifecycleOwner, node, factory1) -> new KanaPresenter(mKanaView, lifecycleOwner, node, factory1); DataSource.getKanaNodes2().observe(this, node -> { binding.kana.setPresenter(factory.create(binding.kana, this, node, factory)); });复制代码
项目地址
more
Github | 简书 | 掘金 | JCenter | dockerHub |
---|---|---|---|---|