1. 程式人生 > >RecyclerView複用ViewHolder導致控制元件有時顯示有時消失問題

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的複用機制。