IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> NestedScrollView嵌套RecyclerView导致无法复用问题 -> 正文阅读

[移动开发]NestedScrollView嵌套RecyclerView导致无法复用问题

前段时间,在和同事合作的时候看到了一段代码,scrollview 嵌套 recyclerview,然后adapter中没有处理view的复用,然后我就开始好奇这里不会出现bug吗?不过之前写代码的时候一直没注意过这种嵌套,那么,先实验一下。

activity_main.xml

    <androidx.core.widget.NestedScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical">

            <TextView
                android:id="@+id/tv_title"
                android:layout_width="match_parent"
                android:layout_height="110dp"
                android:background="#EEE"
                android:gravity="center"
                android:text="textview" />

            <androidx.recyclerview.widget.RecyclerView
                android:id="@+id/recycler"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"/>
        </LinearLayout>
    </androidx.core.widget.NestedScrollView>

MyAdapter.java

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> {

    @NonNull
    @Override
    public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        return new MyViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_recyccler, parent, false));
    }

    @Override
    public void onBindViewHolder(@NonNull MyViewHolder holder, int position) {
        Log.d("VODD", "item: " + position);
        holder.tvItem.setText(String.format("Item:%1$d", position));
    }

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

    protected static class MyViewHolder extends RecyclerView.ViewHolder {

        TextView tvItem;

        public MyViewHolder(@NonNull View itemView) {
            super(itemView);
            tvItem = itemView.findViewById(R.id.tv_item);
        }
    }
}

代码贴在上面了,然后看log日志,毫不意外的发现,所有的view都在第一时间绘制出来了,包括屏幕外面的,因此recyclerview 完全没有复用呵呵。

2021-08-28 17:12:15.646 20497-20497/com.vodlee.nestedscrollrecycler D/VODD: item: 0
2021-08-28 17:12:15.649 20497-20497/com.vodlee.nestedscrollrecycler D/VODD: item: 1
2021-08-28 17:12:15.651 20497-20497/com.vodlee.nestedscrollrecycler D/VODD: item: 2
2021-08-28 17:12:15.653 20497-20497/com.vodlee.nestedscrollrecycler D/VODD: item: 3
2021-08-28 17:12:15.655 20497-20497/com.vodlee.nestedscrollrecycler D/VODD: item: 4
2021-08-28 17:12:15.658 20497-20497/com.vodlee.nestedscrollrecycler D/VODD: item: 5
2021-08-28 17:12:15.660 20497-20497/com.vodlee.nestedscrollrecycler D/VODD: item: 6
2021-08-28 17:12:15.662 20497-20497/com.vodlee.nestedscrollrecycler D/VODD: item: 7
2021-08-28 17:12:15.664 20497-20497/com.vodlee.nestedscrollrecycler D/VODD: item: 8
2021-08-28 17:12:15.666 20497-20497/com.vodlee.nestedscrollrecycler D/VODD: item: 9
2021-08-28 17:12:15.668 20497-20497/com.vodlee.nestedscrollrecycler D/VODD: item: 10
2021-08-28 17:12:15.671 20497-20497/com.vodlee.nestedscrollrecycler D/VODD: item: 11
2021-08-28 17:12:15.673 20497-20497/com.vodlee.nestedscrollrecycler D/VODD: item: 12
2021-08-28 17:12:15.675 20497-20497/com.vodlee.nestedscrollrecycler D/VODD: item: 13
2021-08-28 17:12:15.677 20497-20497/com.vodlee.nestedscrollrecycler D/VODD: item: 14
2021-08-28 17:12:15.679 20497-20497/com.vodlee.nestedscrollrecycler D/VODD: item: 15
2021-08-28 17:12:15.681 20497-20497/com.vodlee.nestedscrollrecycler D/VODD: item: 16
2021-08-28 17:12:15.683 20497-20497/com.vodlee.nestedscrollrecycler D/VODD: item: 17
2021-08-28 17:12:15.686 20497-20497/com.vodlee.nestedscrollrecycler D/VODD: item: 18
2021-08-28 17:12:15.688 20497-20497/com.vodlee.nestedscrollrecycler D/VODD: item: 19

那么这个地方为什么没有进行复用?我先把recyclerView 的 onMeasure 方法打印一下看看

2021-08-28 17:18:44.308 23468-23468/com.vodlee.nestedscrollrecycler D/VODD: widthSpec: 1073742904
2021-08-28 17:18:44.308 23468-23468/com.vodlee.nestedscrollrecycler D/VODD: heightSpec: 0
2021-08-28 17:18:44.318 23468-23468/com.vodlee.nestedscrollrecycler D/VODD: widthSpec: 1073742904
2021-08-28 17:18:44.318 23468-23468/com.vodlee.nestedscrollrecycler D/VODD: heightSpec: 0

通过log,发现 heightSpec 竟然是0?

为什么会是0呢,咱们看 MeasureSpec 的代码

        private static final int MODE_SHIFT = 30;
        public static final int UNSPECIFIED = 0 << MODE_SHIFT;
        public static final int EXACTLY     = 1 << MODE_SHIFT;
        public static final int AT_MOST     = 2 << MODE_SHIFT;
        
        public static int makeMeasureSpec(@IntRange(from = 0, to = (1 << MeasureSpec.MODE_SHIFT) - 1) int size,
                                          @MeasureSpecMode int mode) {
            if (sUseBrokenMakeMeasureSpec) {
                return size + mode;
            } else {
                return (size & ~MODE_MASK) | (mode & MODE_MASK);
            }
        }

也就是只有父布局测量子布局时,传递的 mode 为 UNSPECIFIED 并且 size 为0的时候,才会出现 heightSpec 是0的情况。

而其他情况下,recyclerView 高度为固定值、match_parent 或 wrap_content 的时候,都不会出现0的情况。

这样,就会导致 recyclerView 无法得知当前屏幕展示多少个就够了。


那么问题如何解决?

方案一:通过 getItemViewType(int position) 方法,在 RecyclerView 中创建不同的 viewholder


    @Override
    public int getItemViewType(int position) {
        return position == 0 ? 0 : 1;
    }

    @NonNull
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        if (viewType == 0) {
            return new HeaderViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_recyccler, parent, false));
        }
        return new MyViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_recyccler, parent, false));
    }

    @Override
    public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
        Log.d("VODD", "item: " + position);
        if (getItemViewType(position) == 0) {
            ((HeaderViewHolder)holder).tvItem.setText("This is Header.");
        } else {
            ((MyViewHolder)holder).tvItem.setText(String.format("Item:%1$d", position));
        }
    }

Log日志如下

2021-08-28 23:22:07.298 13234-13234/com.vodlee.nestedscrollrecycler D/VODD: item: 0
2021-08-28 23:22:07.302 13234-13234/com.vodlee.nestedscrollrecycler D/VODD: item: 1
2021-08-28 23:22:07.304 13234-13234/com.vodlee.nestedscrollrecycler D/VODD: item: 2
2021-08-28 23:22:07.306 13234-13234/com.vodlee.nestedscrollrecycler D/VODD: item: 3
2021-08-28 23:22:07.308 13234-13234/com.vodlee.nestedscrollrecycler D/VODD: item: 4

方案二:通过 CoordinatorLayout + AppBarLayout 方法实现顶部布局


    <androidx.coordinatorlayout.widget.CoordinatorLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <com.google.android.material.appbar.AppBarLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

            <TextView
                android:layout_width="match_parent"
                android:layout_height="100dp"
                android:background="#CCC"
                android:gravity="center"
                android:text="This is Header"
                app:layout_scrollFlags="scroll" />

        </com.google.android.material.appbar.AppBarLayout>

        <com.vodlee.nestedscrollrecycler.MyRecyclerView
            android:id="@+id/recycler"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:layout_behavior="@string/appbar_scrolling_view_behavior" />
    </androidx.coordinatorlayout.widget.CoordinatorLayout>

Log日志如下

2021-08-28 23:26:17.478 13973-13973/com.vodlee.nestedscrollrecycler D/VODD: item: 0
2021-08-28 23:26:17.481 13973-13973/com.vodlee.nestedscrollrecycler D/VODD: item: 1
2021-08-28 23:26:17.482 13973-13973/com.vodlee.nestedscrollrecycler D/VODD: item: 2
2021-08-28 23:26:17.484 13973-13973/com.vodlee.nestedscrollrecycler D/VODD: item: 3
2021-08-28 23:26:17.486 13973-13973/com.vodlee.nestedscrollrecycler D/VODD: item: 4
  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2021-08-30 12:09:58  更:2021-08-30 12:11:54 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2025年1日历 -2025/1/31 5:44:22-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码