RecyclerView逐步搞懂

RecyclerView的布局流程设计
RecyclerView的ViewHolder复用流程
从一个神奇的bug开始的RecyclerView复用分析

notifyDataSetChanged()到底做了什么能让UI刷新呢?
notifyDataSetChanged是Adapter的方法,使用了观察者模式,RecyclerViewDataObserver是一个观察者,它是RecyclerView的内部类,实质上,RecyclerView是Adapter的观察者。notifyDataSetChanged就是通知观察者也就是RecyclerView数据有变化了,RecyclerView做了两件事,让所有的ViewHolder无效和调用requestLayout。

Recycler是RecyclerView的final内部类,是专门管理ViewHolder重用的。

我们都知道Adapter的onBindViewHolder方法,是给ViewHolder中的itemView设置数据的,那么,有没有想过为啥在这里设置数据就能最终显示出来呢?是谁调用了这个方法呢?方法中的ViewHolder实例是怎么来的呢?

UI最终能展示出来,肯定是和得和RecyclerView的onMeasure、onLayout和onDraw流程有关系才能显示出来,先说结论:onBindViewHolder是在onMeasure或onLayout中最终调用的。

RecyclerView的测量和布局步骤,都是委托给LayoutManager做的,LayoutManager去获取View,不是直接从Adapter中获取,而且通过Recycler,这样的话Recycler的重用机制就能发挥作用。Recycler简单来说,就是如果有能重用的ViewHolder,就用这个ViewHolder(这里需要注意的是,ViewHolder中itemView上面的数据都还在),在这个时机会调用Adapter的onBindViewHolder方法刷新itemView。

需要说明的是ViewHolder回收到pool中是根据ViewType来回收的,一个ViewType最多回收5个ViewHolder。

RecyclerView怎么实现聊天界面

我们知道正常情况下,RV都是从上往下布局的。但是,在聊天界面,消息从新到旧,是需要从下往上布局的。要实现,也比较简单,可以调用LinearLayoutManager的setReverseLayout方法,将布局反转成从下到上的布局。

可是,有个新问题,对于聊天消息不满一页的情况,消息虽然也是从下往上显示的,这个没问题,但是整体展示在屏幕的下半部分,这个显然是不符合预期的。
为了解决这个问题,可以调用LinearLayoutManager的setStackFromEnd(true)方法,让RV item是从RV的底部向顶部显示。这里需要说的是,这样的话,调用Adapter的onBindViewHolder方法position就是从大到小回调的了,不是从0到大回调的了。

但是,这样又会导致一个新问题,就是RV默认展示到了底部,这样的话,对于有多页消息的情况,首先看到的不是第1页最新消息。

为了解决这个问题,需要让RV滚动到0位置。

    public interface OnScrollCallback {
    
    
        void onScroll(int position);
    }

    static class MAdapter extends RecyclerView.Adapter<MAdapter.MViewHolder> {
    
    
        OnScrollCallback callback;
        public void setOnScrollCallback(OnScrollCallback callback1) {
    
    
            callback = callback1;
        }

        @NonNull
        @Override
        public MViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
    
    
            View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.layout_item, viewGroup, false);
            return new MViewHolder(view);
        }

        @Override
        public void onBindViewHolder(@NonNull MViewHolder mViewHolder, int i) {
    
    
            Log.e("lzy", "onBindViewHolder " + i);
            callback.onScroll(i);
            int value = i + 1;
            mViewHolder.TV.setText(value + "");
        }

        @Override
        public int getItemCount() {
    
    
            return 100;
        }

        static class MViewHolder extends RecyclerView.ViewHolder {
    
    
            TextView TV;
            public MViewHolder(@NonNull View itemView) {
    
    
                super(itemView);
                TV = itemView.findViewById(R.id.tv);
            }
        }
    }

    HashMap<Integer, Boolean> mHashMap = new HashMap<>();
    public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
    
    
        super.onViewCreated(view, savedInstanceState);

        binding.srf.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
    
    
            @Override
            public void onRefresh() {
    
    
                Log.e("lzy", "onRefresh");
                new Handler().postDelayed(new Runnable() {
    
    
                    @Override
                    public void run() {
    
    
                        binding.srf.setRefreshing(false);
                    }
                }, 2000);
            }
        });
        RecyclerView rv = binding.rv;
        linearLayoutManager = new CustomLinearLayoutManager(getActivity());
        linearLayoutManager.setReverseLayout(true);
        linearLayoutManager.setStackFromEnd(true);
        rv.setLayoutManager(linearLayoutManager);
        rv.scrollToPosition(0);
        MAdapter mAdapter = new MAdapter();
        rv.setAdapter(mAdapter);
        mAdapter.setOnScrollCallback(new OnScrollCallback() {
    
    
            @Override
            public void onScroll(int position) {
    
    
                int pos = position;
                Log.e("lzy", "onScrolled " + pos);
                Log.e("lzy", "(pos + 1) % 20 " + (pos + 1) % 20);
                if ((pos + 1) % 20 == 0 && mHashMap.get(pos) == null) {
    
    
                    Log.e("lzy", "onScrolled false");
                    mHashMap.put(pos, true);
                    linearLayoutManager.setCanScroll(false);
                    binding.rv.stopScroll();
                    binding.srf.setRefreshing(true);

                    new Handler().postDelayed(new Runnable() {
    
    
                        @Override
                        public void run() {
    
    
                            Log.e("lzy", "onScrolled true");
                            linearLayoutManager.setCanScroll(true);
                            binding.srf.setRefreshing(false);
                        }
                    }, 5000);
                }
            }
        });
    }

猜你喜欢

转载自blog.csdn.net/lizhongyisailang/article/details/126876784