RecyclerView复用ViewHolder导致控件有时显示有时消失问题

本篇内容仅用来记录开发过程,提醒自己。本人目前实习,刚结束安卓三个月,能力有限,如有错误,欢迎指出。

    在开发项目过程中,使用Recycler有需求需要在不同情况下显示不同的控件。当时并不知道ViewHolder会被复用,并且只针对不同情况进行控件隐藏,但是并未显示,导致复用该ViewHolder的item的某些控件不显示。在空闲时间自己写了一个Demo进行了验证。
    以下是Demo内容
    首先写一个最简单的RecycleView例子

MainActivity.java

public class MainActivity extends AppCompatActivity {
    @BindView(R.id.rv_demo)
    RecyclerView rvDemo;
    @BindView(R.id.btn_demo)
    Button btnDemo;

    private AdapterDemo adapterDemo;
    private Set<AdapterDemo.MyViewHoder> viewHoderSet = new HashSet<>();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ButterKnife.bind(this);
        List<String> list = new ArrayList<>();
        list.add("0");
        list.add("1");
        list.add("2");
        list.add("3");
        list.add("4");
        list.add("5");
        list.add("6");
        list.add("7");
        list.add("8");
        list.add("9");
        list.add("10");
        list.add("11");
        list.add("12");
        list.add("13");
        list.add("14");
        list.add("15");
        list.add("16");
        list.add("17");
        list.add("18");
        list.add("19");
        list.add("20");
        list.add("21");
        list.add("22");
        adapterDemo = new AdapterDemo(list, this);
        rvDemo.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false));
        rvDemo.setAdapter(adapterDemo);

    }

    @OnClick(R.id.btn_demo)
    public void showViewHolder(){
        Iterator<AdapterDemo.MyViewHoder> iterator = viewHoderSet.iterator();
        Log.i("个数", "viewHolder " + viewHoderSet.size());
        while (iterator.hasNext()){
            Log.i("viewHolder", iterator.next().toString());
        }
    }

    public class AdapterDemo extends RecyclerView.Adapter<AdapterDemo.MyViewHoder> {
        private List<String> demoList;
        private Context mContext;
        public AdapterDemo(List<String> demoList, Context mContext) {
            this.demoList = demoList;
            this.mContext = mContext;
        }
        @Override
        public MyViewHoder onCreateViewHolder(ViewGroup parent, int viewType) {
            View v = LayoutInflater.from(mContext).inflate(R.layout.adapter_item, parent, false);
            return new MyViewHoder(v);
        }
        @Override
        public void onBindViewHolder(MyViewHoder holder, int position) {
            Log.e("测试", holder.toString() + " -- " + position);
            viewHoderSet.add(holder);
            if (position % 5 == 0)
                holder.tv_demo.setVisibility(View.GONE);
            else
                holder.tv_demo.setText(demoList.get(position));
        }
        @Override
        public int getItemCount() {
            return demoList.size();
        }
        class MyViewHoder extends RecyclerView.ViewHolder {
            ImageView iv_demo;
            TextView tv_demo;

            public MyViewHoder(View itemView) {
                super(itemView);
                iv_demo = itemView.findViewById(R.id.iv_demo);
                tv_demo = itemView.findViewById(R.id.tv_demo);
            }
        }
    }
}

MainActivity布局

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.qxb_810.myapplication.MainActivity"
    android:orientation="vertical">
    <Button
        android:text="打印viewHolder"
        android:id="@+id/btn_demo"
        android:layout_width="wrap_content"
        android:layout_height="40dp"
        android:layout_marginBottom="20dp"/>
    <android.support.v7.widget.RecyclerView
        android:id="@+id/rv_demo"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"></android.support.v7.widget.RecyclerView>
</LinearLayout>

RecycleView item 布局

<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:gravity="center_vertical"
    >

    <ImageView
        android:id="@+id/iv_demo"
        android:layout_width="60dp"
        android:layout_height="60dp"
        android:background="@color/colorAccent"/>
    <TextView
        android:id="@+id/tv_demo"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="60sp"
        android:text="555555"/>
</LinearLayout>
实现的功能很简单,就是在页面上显示如下图的页面,其中把position是5的倍数的ViewHolder的tv_Demo 控件隐藏掉。按钮的功能是为了能清除的看到生成了几个ViewHolder。效果如图

效果图

打印的Log:

这里写图片描述

目前为止一切正常,可以看到确实position为5的倍数的ViewHolder的TextView确实被隐藏掉了。然后向下滑动到最底层。
效果如图:

底部图

这里发现了postion为17和22的TextView也不见了,查看一下Log

log

发现第17和第22的ViewHolder是复用第0个和第5个的ViewHolder,而在代码处理中,第0个和第5个的ViewHolder中的TextView我们给隐藏掉了。
关键代码如下:

关键代码

当执行到postion = 17 的时候,判断一下postion不是5的倍数,执行赋值操作,但是这个ViewHolder在postion等于5的时候TextView被隐藏了,而且这里并没有在赋值操作时设置TextView可见,导致虽然赋值了,但是不可见也没用。想要改正也很简单,加一句可见语句就行。

改正

另外在打印ViewHolder生成个数的时候发现总共生成了12个ViewHolder,然而我们又23个item。(不同设备生成的可能不一样,我这里用PAD生成12个,用手机生成10个,另外如果加上可见语句则PAD生成11个)

viewHolder个数

因为这个问题导致在项目开发时利用RecycleView展示的时候数据时有时无,头疼了好几天,特地记录一下。RecyclerView为了性能考虑复用无可厚非,所以打算并且抽时间深度研究一下RecyclerView的复用机制。

猜你喜欢

转载自blog.csdn.net/qq_36882793/article/details/82049218