当我们使用 ViewBinding 来绑定 RecyclerView 的 Item 会出现一个神奇的 bug 那就是使用 LinearLayout 作为 item 的 Root View 时设置它的 layout_width 属性时会完全失效。

我们先重现一下这个问题。

首先定义 item 布局,这里看到我使用的是 LinearLayout 作为根布局的。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:padding="15dp">

        <TextView
            android:id="@+id/i_job_name"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:text="这是名称" />

        <TextView
            android:background="#1000"
            android:id="@+id/i_job_id"
            android:padding="10dp"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:text="ID: xxx" />

    </LinearLayout>


</LinearLayout>

我通过预览布局看到,i_job_name 文本在左边并且利用 layout_weight 属性将 i_job_id 文本组件推到最右边。布局是没有问题的

ViewBinding 与 RecyclerView 的那点事,Item 使用 LinearLayout 导致宽度属性失效-天真的小窝

然后就是定义适配器,能看到我这里的 JobListAdapter 适配器的 ViewHolder 使用的是 ItemJobBinding 对象,就是通过上面的 item_job.xml 得到的 ViewBinding 对象。

然后就是常规在 onCreateViewHolder 中构建 binding 对象和实例化 ViewHolder。

public class JobListAdapter extends RecyclerView.Adapter<JobListAdapter.ViewHolder> {

        Activity activity;
        List<JobList.RowsBean> rows;

        public JobListAdapter(Activity activity, List<JobList.RowsBean> rows) {
            this.activity = activity;
            this.rows = rows;
        }

        @NonNull
        @Override
        public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
            ItemJobBinding binding = ItemJobBinding.inflate(activity.getLayoutInflater());
            return new ViewHolder(binding);
        }

        @Override
        public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
            ItemJobBinding binding = holder.binding;
            JobList.RowsBean rowsBean = rows.get(position);

            binding.iJobName.setText(rowsBean.professionName);
            binding.iJobId.setText(rowsBean.id + "");
        }

        @Override
        public int getItemCount() {
            return rows.size();
        }

        public class ViewHolder extends RecyclerView.ViewHolder {
            ItemJobBinding binding;

            public ViewHolder(@NonNull ItemJobBinding binding) {
                super(binding.getRoot());
                this.binding = binding;
            }
        }
    }

现在实例化 JobListAdapter 并且设置到 RecyclerView 上

JobListAdapter jobListAdapter = new JobListAdapter(getActivity(), rows);
binding.fJobListView.setLayoutManager(new LinearLayoutManager(getActivity()));
binding.fJobListView.setAdapter(jobListAdapter);

一切都看起来没有问题,在编译运行的过程中我甚至都在脑袋里 “预编译” 得到了这个不应该出意外的结果了。

可是 Bug 来的那么突然,我一点防备都没有,当我看到下面的界面时,第一反应是想可能是触发了某个魔法,我重新编译一下就好了。但是现实就是这么打脸,重新运行好几遍都是一样的结果,我又认真的检查了好几遍代码甚至清理 Build 缓存,它依然如此一般…

ViewBinding 与 RecyclerView 的那点事,Item 使用 LinearLayout 导致宽度属性失效-天真的小窝

后来通过一顿 Google 看到不少大佬说把 item 布局文件的根布局 LinearLayout 换成 RelativeLayout 就好了。下面是替换 RelativeLayout 之后的布局文件

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:padding="15dp">

        <TextView
            android:id="@+id/i_job_name"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:text="这是名称" />

        <TextView
            android:background="#1000"
            android:id="@+id/i_job_id"
            android:padding="10dp"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:text="ID: xxx" />

    </LinearLayout>


</RelativeLayout>

果然,显示正常了。

ViewBinding 与 RecyclerView 的那点事,Item 使用 LinearLayout 导致宽度属性失效-天真的小窝

按道理来说问题到这里就结束了,但是对于我这个万年线性布局的男孩来说,大部分时候能用 LinearLayout 就直接创建基于 LinearLayout 的布局文件了。当我布局好之后再来修改成 RelativeLayout 就需要在 RelativeLayout 里再套一层 LinearLayout 先不说对于页面渲染性能的影响,反正我写起来感觉也还是有点不舒服的,而且问题应该不是出在这个 LinearLayout 作为根布局上面。

我们将视角拉回来,如果问题不是出在 LinearLayout 上面(我尝试过给根 LinearLayout 设置固定宽度,也是一样没有任何作用)那问题还会出现在哪里呢?

继续往下,那就是 ViewBinding 了,于是我想到能不能在 ViewBinding.getRoot 之后重新设置一下 Root 的宽度呢?

事实证明,确实有效,下面是我修改之后 JobListAdapter 的代码

public class JobListAdapter extends RecyclerView.Adapter<JobListAdapter.ViewHolder> {

    Activity activity;
    List<JobList.RowsBean> rows;

    public JobListAdapter(Activity activity, List<JobList.RowsBean> rows) {
        this.activity = activity;
        this.rows = rows;
    }

    @NonNull
    @Override
    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        ItemJobBinding binding = ItemJobBinding.inflate(activity.getLayoutInflater());
        
        binding.getRoot().setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.MATCH_PARENT));
        
        return new ViewHolder(binding);
    }

    @Override
    public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
        ItemJobBinding binding = holder.binding;
        JobList.RowsBean rowsBean = rows.get(position);

        binding.iJobName.setText(rowsBean.professionName);
        binding.iJobId.setText(rowsBean.id + "");
    }

    @Override
    public int getItemCount() {
        return rows.size();
    }

    public class ViewHolder extends RecyclerView.ViewHolder {
        ItemJobBinding binding;

        public ViewHolder(@NonNull ItemJobBinding binding) {
            super(binding.getRoot());
            this.binding = binding;
        }
    }
}

没错就是在 onCreateViewHolder 实例化 ItemJobBinding 时重新设置了一下 RootView 的宽高。

ViewBinding 与 RecyclerView 的那点事,Item 使用 LinearLayout 导致宽度属性失效-天真的小窝

到这里,其实我发现这好像是 ViewBinding 使用 LinearLayout 作为根布局的一个通病。不只是在 RecyclerView 的 Item 中这样,在 Dialog 自定义布局的时候也同样出现了 LinearLayout 根布局萎缩的情况。

有时间的情况下我倒是想翻一翻 ViewBinding 的代码找找问题的根本了。

好啦,今天的踩坑博客就到这里啦。如果大家觉得还凑合的话不妨点个赞或者在我博客 https://bin.zmide.com/?p=997 留个言呢。