博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
使用Databinding轻松快速打造仿携程app筛选控件(二)
阅读量:7297 次
发布时间:2019-06-30

本文共 10539 字,大约阅读时间需要 35 分钟。

上一篇我们介绍了只用Databinding的方式快速实现了一个主从联动的组合自定义控件,今天我们要实现的是一个无限扩展的组织树控件。 先看效果:

效果介绍

不像网上的一下demo中看不中用,我的开源库开箱即用,可以直接用于生产环境,高内聚,低耦合,支持各个层级样式自定义,可以自由的加载不同的xml,修改viewHolder,支持异步加载子节点。完美的状态保持,activity重建也不会丢失状态。

设计思路

  • 首先对数据进行建模,每一个层级抽象为一个Node,每一个Node包含若干个子Node,类似N叉树的形式。最顶端的Node为Root node。
public interface Node
extends 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 LiveData
sData; 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

转载于:https://juejin.im/post/5c469d926fb9a049e12a8829

你可能感兴趣的文章
myeclipse安装pydev
查看>>
【桌面虚拟化】之五PCoIP
查看>>
linux 监控CPU memory disk process 脚本
查看>>
Nginx启动脚本/etc/init.d/nginx
查看>>
Visual Studio 2017 离线安装方式
查看>>
枚举出局域网上所有网络资源
查看>>
Android深入浅出之Binder机制
查看>>
动态磁盘的管理
查看>>
zookeeper 集群安装(单点与分布式成功安装)
查看>>
python中list,dirt方法说明
查看>>
lamp环境一键部署(yum)
查看>>
一个IT大学生来深圳2年半的经历感受
查看>>
VMware View 5.0 桌面虚拟化方案介绍视频
查看>>
理解Spring中的事务抽象
查看>>
java 设计模式 建造者模式
查看>>
mysql备份和恢复工作记录
查看>>
我的友情链接
查看>>
vFrank考VCDX的过程
查看>>
jQuery input同步发sims
查看>>
memcached起步
查看>>