Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Android 练手之旅——RecyclerView(三)Observable #14

Open
soapgu opened this issue Mar 16, 2021 · 0 comments
Open

Android 练手之旅——RecyclerView(三)Observable #14

soapgu opened this issue Mar 16, 2021 · 0 comments
Labels
Demo Demo 安卓 安卓

Comments

@soapgu
Copy link
Owner

soapgu commented Mar 16, 2021

引言

上回解決了ItemSource和ItemTemplate的设置和绑定问题。
但是没有解决数据动态更新的问题

RecyclerView.Adapter的更新机制

  • 全局更新 notifyDataSetChanged()
  • 数据新增 notifyItemRangeInserted(positionStart, itemCount)
  • 数据移除 notifyItemRangeRemoved(positionStart, itemCount)
  • 数据更新 notifyItemRangeChanged(positionStart, itemCount)
  • 数据移动 notifyItemMoved(fromPosition, toPosition)

部分参考DiffUtil and data binding with RecyclerView

必须在合适的场景使用合适的api(比如notifyDataSetChanged虽然一招鲜吃遍天下鲜,肯定性能低下)

ShadowAdapter 的Observable对象改造

 private static class WeakReferenceOnListChangedCallback<T> extends ObservableList.OnListChangedCallback<ObservableList<T>> {
        final WeakReference<ShadowAdapter<T>> adapterRef;

        WeakReferenceOnListChangedCallback(ShadowAdapter<T> adapter) {
            this.adapterRef = new WeakReference<>(adapter);
        }

        @Override
        public void onChanged(ObservableList sender) {
            ShadowAdapter<T> adapter = adapterRef.get();
            if (adapter == null) {
                return;
            }
            //Utils.ensureChangeOnMainThread();
            adapter.notifyDataSetChanged();
        }

        @Override
        public void onItemRangeChanged(ObservableList sender, final int positionStart, final int itemCount) {
            ShadowAdapter<T> adapter = adapterRef.get();
            if (adapter == null) {
                return;
            }
            //Utils.ensureChangeOnMainThread();
            adapter.notifyItemRangeChanged(positionStart, itemCount);
        }

        @Override
        public void onItemRangeInserted(ObservableList sender, final int positionStart, final int itemCount) {
            ShadowAdapter<T> adapter = adapterRef.get();
            if (adapter == null) {
                return;
            }
            //Utils.ensureChangeOnMainThread();
            adapter.notifyItemRangeInserted(positionStart, itemCount);
        }

        @Override
        public void onItemRangeMoved(ObservableList sender, final int fromPosition, final int toPosition, final int itemCount) {
            ShadowAdapter<T> adapter = adapterRef.get();
            if (adapter == null) {
                return;
            }
            //Utils.ensureChangeOnMainThread();
            for (int i = 0; i < itemCount; i++) {
                adapter.notifyItemMoved(fromPosition + i, toPosition + i);
            }
        }

        @Override
        public void onItemRangeRemoved(ObservableList sender, final int positionStart, final int itemCount) {
            ShadowAdapter<T> adapter = adapterRef.get();
            if (adapter == null) {
                return;
            }
            //Utils.ensureChangeOnMainThread();
            adapter.notifyItemRangeRemoved(positionStart, itemCount);
        }
    }

新增一个OnListChangedCallback的类,里面完成所有相关的数据变化通知

 @Override
    public void onAttachedToRecyclerView(@NonNull RecyclerView recyclerView) {
        super.onAttachedToRecyclerView(recyclerView);
        if (this.recyclerView == null && items instanceof ObservableList) {
            callback = new WeakReferenceOnListChangedCallback<>(this);
            ((ObservableList<T>) items).addOnListChangedCallback(callback);
        }
        this.recyclerView = recyclerView;
    }

    @Override
    public void onDetachedFromRecyclerView(@NonNull RecyclerView recyclerView) {
        super.onDetachedFromRecyclerView(recyclerView);
        if (this.recyclerView != null && items instanceof ObservableList) {
            ((ObservableList<T>) items).removeOnListChangedCallback(callback);
            callback = null;
        }
        this.recyclerView = null;
    }

在ShadowAdapter文件中实现加载和卸载RecyclerView重载回调
在加载的时候吧钩子加上,在卸载的时候把钩子去掉

BindingAdapter改造

BindingAdapter潜在性能问题

偶然调试过程中发现BindingAdapter的静态函数会被反复调用,并不符合我的预期
发现我每次列表加元素也会执行,这就有点不可理解了。加了断点

找到绑定自动生成代码ActivitySearchBindingImpl extends ActivitySearchBinding

private boolean onChangeDatacontextStringItems(androidx.databinding.ObservableList<java.lang.String> DatacontextStringItems, int fieldId) {
        if (fieldId == BR._all) {
            synchronized(this) {
                    mDirtyFlags |= 0x2L;
            }
            return true;
        }
        return false;
    }

这个回调,任意元素的变化都会调用mDirtyFlags 会赋值为2.
然后看调用的地方

@Override
    protected void executeBindings() {
        long dirtyFlags = 0;
        synchronized(this) {
            dirtyFlags = mDirtyFlags;
            mDirtyFlags = 0;
        }
        com.soapdemo.photohunter.viewmodels.SearchViewModel datacontext = mDatacontext;
        androidx.databinding.ObservableList<java.lang.String> datacontextStringItems = null;

        if ((dirtyFlags & 0x7L) != 0) {



                if (datacontext != null) {
                    // read datacontext.stringItems
                    datacontextStringItems = datacontext.getStringItems();
                }
                updateRegistration(1, datacontextStringItems);
        }
        // batch finished
        if ((dirtyFlags & 0x4L) != 0) {
            // api target 1

            this.buttonAdd.setOnClickListener(mCallback4);
            this.buttonClear.setOnClickListener(mCallback3);
        }
        if ((dirtyFlags & 0x7L) != 0) {
            // api target 1

            com.soapdemo.photohunter.util.BindingAdapters.setItems(this.dataList, datacontextStringItems, R.layout.text_row_item, (java.lang.String)null);
        }
    }

((dirtyFlags & 0x7L) != 0) 让我想到Linux文件的权限777,
二进制的三位任意的变化都会触发绑定函数执行。虽然没法完全理解框架的意图。我的函数需要做如下改造

@BindingAdapter(value = {"itemsSource","itemTemplate","variableName"},requireAll = false)
    public static <T> void setItems(RecyclerView recyclerView , List<T> itemsSource , @LayoutRes int itemTemplate, String variableName ){
        ShadowAdapter<T> adapter;

        @SuppressWarnings("unchecked")
        ShadowAdapter<T> oldAdapter = (ShadowAdapter<T>)recyclerView.getAdapter();
        if( oldAdapter == null ){
            ItemTemplate template = ItemTemplate.of( itemTemplate, variableName );
            adapter = new ShadowAdapter<>(template);
        }
        else {
            adapter = oldAdapter;
        }
        adapter.setItems(itemsSource);
        if (oldAdapter != adapter) {
            recyclerView.setAdapter(adapter);
        }
    }
public void setItems(List<T> items) {
        if (this.items == items) {
            return;
        }
        // If a recyclerview is listening, set up listeners. Otherwise wait until one is attached.
        // No need to make a sound if nobody is listening right?
        if (recyclerView != null) {
            if (this.items instanceof ObservableList) {
                ((ObservableList<T>) this.items).removeOnListChangedCallback(callback);
                callback = null;
            }
            if (items instanceof ObservableList) {
                callback = new WeakReferenceOnListChangedCallback<>(this);
                ((ObservableList<T>) items).addOnListChangedCallback(callback);
            }
        }
        this.items = items;
        notifyDataSetChanged();
    }

1 adapter 增加判断是否已经赋值
2 判断items是否重复赋值

Challenge for end

  1. UI端已经完成了虚拟化,但是数据层的虚拟化怎么完成
  2. 怎么完成一个无限的列表
  3. 怎么做到一边加载一边翻滚,配合动画效果等
    这些问题有些难度,目前这阶段有点超纲了,等学习完成其他的目标后再来回顾这些问题

代码参考出处

一个很复杂的绑定库
binding-collection-adapter

@soapgu soapgu added Demo Demo 安卓 安卓 labels Mar 17, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Demo Demo 安卓 安卓
Projects
None yet
Development

No branches or pull requests

1 participant