上一篇咱們介紹了只用Databinding的方式快速實現了一個主從聯動的組合自定義控件,今天咱們要實現的是一個無限擴展的組織樹控件。 先看效果:java
不像網上的一下demo中看不中用,個人開源庫開箱即用,能夠直接用於生產環境,高內聚,低耦合,支持各個層級樣式自定義,能夠自由的加載不一樣的xml,修改viewHolder,支持異步加載子節點。完美的狀態保持,activity重建也不會丟失狀態。node
public interface Node<T> extends Checkable {
boolean isLeaf();
boolean isLeafParent();
boolean isRoot();
LiveData<List<T>> getItems();
}
複製代碼
注意,這裏定義子節點是爲了支持異步加載android
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裏git
public class KanaPresenter implements Presenter {
private final LifecycleOwner lifecycleOwner;
private final KanaView mKanaView;
private final RecyclerView rv;
private final Node rootNode;
private final Observer<List<? extends Node>> 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<List<? extends Node>> 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的思想體現github
public interface KanaPresenterFactory {
KanaPresenter create(KanaView mKanaView, LifecycleOwner lifecycleOwner, Node node, KanaPresenterFactory factory);
}
複製代碼
參加demo 咱們首先將咱們的的Item繼承Nodedocker
public class MyNode implements Node {
private final String name;
private final LiveData<List<? extends Node>> 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<List<Node>> source = LiveDataReactiveStreams.fromPublisher(s -> {
List<Node> ret = new ArrayList<>();
for (int i = 0; i < 20; i++) {
ret.add(new MyNode(this, name+"-" + i, deep + 1));
}
s.onNext(ret);
});
((MediatorLiveData<List<? extends Node>>) this.items).addSource(source, nodes -> {
((MediatorLiveData<List<? extends Node>>) this.items).setValue(nodes);
((MediatorLiveData<List<? extends Node>>) 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<List<? extends Node>> 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<? extends Node> 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<List> sData;
private static LiveData<Node> sKana;
private static LiveData<Node> sKana2;
public static LiveData<List> get() {
if (sData == null) {
sData = build();
}
return sData;
}
private static LiveData<List> build() {
LiveData<List> 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<List>) ret).setValue(list);
return ret;
}
public static LiveData<Node> getKanaNodes() {
if (sKana == null) {
sKana = buildKana();
}
return sKana;
}
public static LiveData<Node> getKanaNodes2() {
if (sKana2 == null) {
sKana2 = buildKanaOld();
}
return sKana2;
}
private static LiveData<Node> buildKana() {
LiveData<Node> ret = new MutableLiveData<>();
CityNode root = new CityNode(null, "root", 0, CityRepo.getInstance(App.sApp).getMap());
((MutableLiveData<Node>) ret).setValue(root);
return ret;
}
private static LiveData<Node> buildKanaOld() {
LiveData<Node> ret = new MutableLiveData<>();
((MutableLiveData<Node>) ret).setValue(new MyNode(null, "root", 0));
return ret;
}
}
複製代碼
而後再Activity中只須要簡單的幾行代碼dom
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));
});
複製代碼
github異步
Github | 簡書 | 掘金 | JCenter | dockerHub |
---|---|---|---|---|
Github | 簡書 | 掘金 | JCenter | dockerHub |