Android进阶之路

再也不用担心面试问RecycleView了

2020-11-26  本文已影响0人  积木zz

嗨,大家好,最近去淘了一些关于RecycleView的面试真题,大家一起看看吧,这次的问题如果都弄懂了,下次面试再遇到RecycleView应该就没啥可担心的了。

讲一下RecyclerView的缓存机制,滑动10个,再滑回去,会有几个执行onBindView。缓存的是什么?cachedView会执行onBindView吗?

RecyclerView预取机制

这两个问题都是关于缓存的,我就一起说了。

1)首先说下RecycleView的缓存结构:

Recycleview有四级缓存,分别是mAttachedScrap(屏幕内),mCacheViews(屏幕外),mViewCacheExtension(自定义缓存),mRecyclerPool(缓存池)

2)四级缓存按照顺序需要依次读取。所以完整缓存流程是:

  1. 保存缓存流程:
  1. 获取缓存流程:

3)了解了缓存结构和缓存流程,我们再来看看具体的问题
滑动10个,再滑回去,会有几个执行onBindView?

所以我们假设从加载RecyclView开始盘的话(页面假设可以容纳7条数据):

4)所以这个问题就得出结论了(假设mCacheViews容量为默认值2):

但是但是,实际情况又有点不一样。因为Recycleview在v25版本引入了一个新的机制,预取机制

预取机制,就是在滑动过程中,会把将要展示的一个元素提前缓存到mCachedViews中,所以滑动10个元素的时候,第11个元素也会被创建,也就多走了一次bindview方法。但是滑回去的时候不影响,因为就算提前取了一个缓存数据,只是把bindview方法提前了,并不影响总的绑定item数量。

所以滑动的是新数据的情况下就会多一次调用bindview方法。

5)总结,问题怎么答呢?

如何实现RecyclerView的局部更新,用过payload吗,notifyItemChange方法中的参数?

关于RecycleView的数据更新,主要有以下几个方法:

可以看到,关于view的局部刷新就是notifyItemChanged(int, Object)方法,下面具体说说:

notifyItemChange有两个构造方法:

其中payload参数可以认为是你要刷新的一个标示,比如我有时候只想刷新itemView中的textview,有时候只想刷新imageview?又或者我只想某一个view的文字颜色进行高亮设置?那么我就可以通过payload参数来标示这个特殊的需求了。

具体怎么做呢?比如我调用了notifyItemChanged(14,"changeColor"),那么在onBindViewHolder回调方法中做下判断即可:

    @Override
    public void onBindViewHolder(ViewHolderholder, int position, List<Object> payloads) {
        if (payloads.isEmpty()) {
            // payloads为空,说明是更新整个ViewHolder
            onBindViewHolder(holder, position);
        } else {
            // payloads不为空,这只更新需要更新的View即可。
            String payload = payloads.get(0).toString();
            if ("changeColor".equals(payload)) {
                holder.textView.setTextColor("");
            }
        }
    }

RecyclerView嵌套RecyclerView滑动冲突,NestScrollView嵌套RecyclerView。

1)RecyclerView嵌套RecyclerView的情况下,如果两者都要上下滑动,那么就会引起滑动冲突。默认情况下外层的RecycleView可滑,内层不可滑。

之前说过解决滑动冲突的办法有两种:内部拦截法和外部拦截法
这里我提供一种内部拦截法,还有一些其他的办法大家可以自己思考下。

   holder.recyclerView.setOnTouchListener { v, event ->
            when(event.action){
                //当按下操作的时候,就通知父view不要拦截,拿起操作就设置可以拦截,正常走父view的滑动。
                MotionEvent.ACTION_DOWN,MotionEvent.ACTION_MOVE -> v.parent.requestDisallowInterceptTouchEvent(true)
                MotionEvent.ACTION_UP -> v.parent.requestDisallowInterceptTouchEvent(false)
            }
            false}

2)关于ScrclerView的滑动冲突还是同样的解决办法,就是进行事件拦截。
还有一个办法就是用Nestedscrollview代替ScrollViewNestedscrollview是官方为了解决滑动冲突问题而设计的新的View。它的定义就是支持嵌套滑动的ScrollView。

所以直接替换成Nestedscrollview就能保证两者都能正常滑动了。但是要注意设置RecyclerView.setNestedScrollingEnabled(false)这个方法,用来取消RecyclerView本身的滑动效果。

这是因为RecyclerView默认是setNestedScrollingEnabled(true),这个方法的含义是支持嵌套滚动的。也就是说当它嵌套在NestedScrollView中时,默认会随着NestedScrollView滚动而滚动,放弃了自己的滚动。所以给我们的感觉就是滞留、卡顿。所以我们将它设置为false就解决了卡顿问题,让他正常的滑动,不受外部影响。

说说RecyclerView性能优化。

void onItemsInsertedOrRemoved() {
   if (hasFixedSize) layoutChildren();
   else requestLayout();
}
new LinearLayoutManager(this) {
    @Override
    protected int getExtraLayoutSpace(RecyclerView.State state) {
        return size;
    }
};

拜拜

今天聊了不少,关于RecycleView重要的知识点应该都涉及到了,其中bindview的问题下次有机会我会再配合图片日志详细的说一下。

有一起学习的小伙伴可以关注下我的公众号——码上积木❤️❤️
每日三问知识点/面试题,积少成多。

上一篇 下一篇

猜你喜欢

热点阅读