RecyclerView中使用CheckBox出現(xiàn)勾選混亂的解決方法
熟悉RecyclerView的人應該都知道,RecyclerView使用了復用機制,當在RecyclerView中得每一項都添加一個CheckBox時,勾選當前頁面的幾個CheckBox會發(fā)現(xiàn)下面還有其他的CheckBox也被勾選了,今天我們就來討論一下如何解決這個問題。
首先當然是創(chuàng)建一個項目,然后在activity_main中添加一個RecyclerView控件,當然,在這之前,我們需要先添加RecyclerView的依賴,如下圖:
然后 開始編輯activity_main:
<?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="match_parent" android:orientation="vertical"> <android.support.v7.widget.RecyclerView android:id="@+id/id_recycler_view" android:layout_width="match_parent" android:layout_height="match_parent"> </android.support.v7.widget.RecyclerView> </LinearLayout>
接下來為這個RecyclerView創(chuàng)建一個item布局文件,命名為item_recyclerview,并添加一個CheckBox空間,代碼如下:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="60dp" android:gravity="center_vertical"> <CheckBox android:id="@+id/id_check_box" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginLeft="10dp" android:text="CheckBox"/> </LinearLayout>
接下來要編輯MainActivity了,包括從布局文件中找到剛才的RecyclerView控件,然后為其設置Adapter等,過程不再詳細敘述,編輯后的代碼如下:
public class MainActivity extends AppCompatActivity { private RecyclerView recyclerView; private MyAdapter myAdapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); recyclerView = (RecyclerView) findViewById(R.id.id_recycler_view); myAdapter = new MyAdapter(); recyclerView.setLayoutManager(new LinearLayoutManager(this)); recyclerView.setAdapter(myAdapter); } private class MyAdapter extends RecyclerView.Adapter { private List<String> content; @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view = LayoutInflater.from(MainActivity.this).inflate(R.layout.item_recyclerview, parent, false); return new MyViewHolder(view); } @Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { MyViewHolder myViewHolder = (MyViewHolder) holder; myViewHolder.checkBox.setText(content.get(position)); } @Override public int getItemCount() { content = new ArrayList<>(); for (int i = 0; i < 100; i++) { content.add("CheckBox" + i); } return content.size(); } } private class MyViewHolder extends RecyclerView.ViewHolder { private CheckBox checkBox; public MyViewHolder(View itemView) { super(itemView); checkBox = (CheckBox) itemView.findViewById(R.id.id_check_box); } } }
可以看到,我們?yōu)檫@個RecyclerView設置了100個item,每個item里面都含有一個CheckBox,這時候運行這個應用,勾選出現(xiàn)的屏幕上的某一個或者多個CheckBox之后,當你向下拉的時候,問題出現(xiàn)了,你會發(fā)現(xiàn)下面會有很多的CheckBox也被選中了。下面我們就來著手解決這個問題,其實要解決也很簡單,可以定義一個boolean類型的數(shù)組或者列表,用它來控制CheckBox的選中狀態(tài),當某個CheckBox被選中的時候?qū)⑵溥x中狀態(tài)記錄在數(shù)組或列表中,當某個CheckBox滾動到屏幕上的時候,再用數(shù)組或列表中對應的值把它的選中狀態(tài)改回來就好了,修改后的代碼如下:
public class MainActivity extends AppCompatActivity { private RecyclerView recyclerView; private MyAdapter myAdapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); recyclerView = (RecyclerView) findViewById(R.id.id_recycler_view); myAdapter = new MyAdapter(); recyclerView.setLayoutManager(new LinearLayoutManager(this)); recyclerView.setAdapter(myAdapter); } private class MyAdapter extends RecyclerView.Adapter { private List<String> content; private boolean[] flag = new boolean[100];//此處添加一個boolean類型的數(shù)組 @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view = LayoutInflater.from(MainActivity.this).inflate(R.layout.item_recyclerview, parent, false); return new MyViewHolder(view); } @Override public void onBindViewHolder(RecyclerView.ViewHolder holder, final int position) { MyViewHolder myViewHolder = (MyViewHolder) holder; myViewHolder.checkBox.setText(content.get(position)); myViewHolder.checkBox.setOnCheckedChangeListener(null);//先設置一次CheckBox的選中監(jiān)聽器,傳入?yún)?shù)null myViewHolder.checkBox.setChecked(flag[position]);//用數(shù)組中的值設置CheckBox的選中狀態(tài) //再設置一次CheckBox的選中監(jiān)聽器,當CheckBox的選中狀態(tài)發(fā)生改變時,把改變后的狀態(tài)儲存在數(shù)組中 myViewHolder.checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton compoundButton, boolean b) { flag[position] = b; } }); } @Override public int getItemCount() { content = new ArrayList<>(); for (int i = 0; i < 100; i++) { content.add("CheckBox" + i); } return content.size(); } } private class MyViewHolder extends RecyclerView.ViewHolder { private CheckBox checkBox; public MyViewHolder(View itemView) { super(itemView); checkBox = (CheckBox) itemView.findViewById(R.id.id_check_box); } } }
比較這兩段代碼,我們會發(fā)現(xiàn),首先我們定義了一個長度為100的數(shù)組,然后設置CheckBox的選中監(jiān)聽器,把null作為參數(shù)傳進去,然后用數(shù)組中的值設置對應CheckBox的選中狀態(tài),最后再一次設置CheckBox的選中監(jiān)聽器,把CheckBox的選中狀態(tài)儲存在數(shù)組中的相應位置中。再次運行,發(fā)現(xiàn)問題已解決。
下面我們來討論一下,如果要在RecyclerView的外面再添加一個CheckBox,用外面的CheckBox來控制RecyclerView中的CheckBox的全選和取消全選,要如何實現(xiàn)呢?
其實也很簡單,只要用這個CheckBox來控制之前所定義的數(shù)組的指就好了。
首先來修改一下activity_main,代碼如下:
<?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="match_parent" android:orientation="vertical"> <LinearLayout android:layout_width="match_parent" android:layout_height="60dp" android:gravity="center_vertical"> <CheckBox android:id="@+id/id_select_all" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginLeft="10dp" android:text="SelectAll"/> </LinearLayout> <android.support.v7.widget.RecyclerView android:id="@+id/id_recycler_view" android:layout_width="match_parent" android:layout_height="match_parent"> </android.support.v7.widget.RecyclerView> </LinearLayout>
我們在RecyclerView的外面添加了一個CheckBox,用這個CheckBox來控制RecyclerView中的CheckBox的全選,接下來修改MainActivity:
public class MainActivity extends AppCompatActivity { private CheckBox selectAll; private RecyclerView recyclerView; private MyAdapter myAdapter; private boolean []flag;//把flag數(shù)組定義為全局變量 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); selectAll = (CheckBox) findViewById(R.id.id_select_all); recyclerView = (RecyclerView) findViewById(R.id.id_recycler_view); flag = new boolean[100];//初始化flag myAdapter = new MyAdapter(); recyclerView.setLayoutManager(new LinearLayoutManager(this)); recyclerView.setAdapter(myAdapter); //設置外面CheckBox的選中監(jiān)聽器,把它的選中狀態(tài)賦值給其他的所有CheckBox,然后更新RecyclerView的Adapter selectAll.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton compoundButton, boolean b) { for (int i = 0; i < 100; i++) { flag[i] = b; } myAdapter.notifyDataSetChanged(); } }); } private class MyAdapter extends RecyclerView.Adapter { private List<String> content; // private boolean[] flag = new boolean[100]; @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view = LayoutInflater.from(MainActivity.this).inflate(R.layout.item_recyclerview, parent, false); return new MyViewHolder(view); } @Override public void onBindViewHolder(RecyclerView.ViewHolder holder, final int position) { MyViewHolder myViewHolder = (MyViewHolder) holder; myViewHolder.checkBox.setText(content.get(position)); myViewHolder.checkBox.setOnCheckedChangeListener(null); myViewHolder.checkBox.setChecked(flag[position]); myViewHolder.checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton compoundButton, boolean b) { flag[position] = b; } }); } @Override public int getItemCount() { content = new ArrayList<>(); for (int i = 0; i < 100; i++) { content.add("CheckBox" + i); } return content.size(); } } private class MyViewHolder extends RecyclerView.ViewHolder { private CheckBox checkBox; public MyViewHolder(View itemView) { super(itemView); checkBox = (CheckBox) itemView.findViewById(R.id.id_check_box); } } }
這里我們先把記錄CheckBox選中狀態(tài)的數(shù)組定義為全局變量,然后設置外面的CheckBox的監(jiān)聽器,把它的選中狀態(tài)賦值給其他的所有CheckBox,緊接著更新一下RecyclerView的Adapter就可以了
這里我們在討論一下RecyclerView的另外一個問題,就是當要刪除某個子項的時候會出現(xiàn)刪除紊亂的情況,為了說明這個問題,我們
先來嘗試實踐一下,修改item_recyclerview:
<?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="60dp" android:gravity="center_vertical"> <CheckBox android:id="@+id/id_check_box" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginLeft="10dp" android:text="CheckBox"/> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentRight="true" android:layout_marginRight="10dp" android:text="delete"/> </RelativeLayout>
我們增加了一個Button,接下來設置這個Button,當它被點擊的時候就刪除它所在位置的item,修改MainActivity如下,主要修改的是Adapter部分,其他部分的代碼就不貼了:
private class MyAdapter extends RecyclerView.Adapter { // private List<String> content; // private boolean[] flag = new boolean[100]; @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view = LayoutInflater.from(MainActivity.this).inflate(R.layout.item_recyclerview, parent, false); return new MyViewHolder(view); } @Override public void onBindViewHolder(RecyclerView.ViewHolder holder, final int position) { final MyViewHolder myViewHolder = (MyViewHolder) holder; myViewHolder.checkBox.setText(content.get(position)); myViewHolder.checkBox.setOnCheckedChangeListener(null); myViewHolder.checkBox.setChecked(flag[position]); myViewHolder.checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton compoundButton, boolean b) { flag[position] = b; } }); //設置監(jiān)聽器,當按鈕被點擊是,刪除它所在的item myViewHolder.button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { content.remove(position); notifyItemRemoved(position); } }); } @Override public int getItemCount() { // content = new ArrayList<>(); // for (int i = 0; i < 100; i++) { // content.add("CheckBox" + i); // } return content.size(); } } private class MyViewHolder extends RecyclerView.ViewHolder { private CheckBox checkBox; private Button button;//定義刪除按鈕 public MyViewHolder(View itemView) { super(itemView); checkBox = (CheckBox) itemView.findViewById(R.id.id_check_box); button = (Button) itemView.findViewById(R.id.id_delete); } }
這是點擊Button,我們會發(fā)現(xiàn),問題出現(xiàn)了,第一次可以正常刪除,第二次刪除的item卻是我們點擊的Button所在的下一個item,后面的刪除也會各種混亂,這是因為函數(shù)里面的傳入的參數(shù)position,它是在進行onBind操作時確定的,在刪除單項后,已經(jīng)出現(xiàn)在畫面里的項不會再有調(diào)用onBind機會,這樣它保留的position一直是未進行刪除操作前的postion值,對于尚未進入畫面的單項來說,它會使用新的position值(好吧這段是抄的,其實我也不太懂啥意思),解決方法如下:
myViewHolder.button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { content.remove(position); notifyItemRemoved(position); notifyItemRangeChanged(position, content.size());//對于被刪掉的位置及其后range大小范圍內(nèi)的view進行重新onBindViewHolder } });
只要加一行代碼就好了,這行代碼的作用就是對于被刪掉的位置及其后range大小范圍內(nèi)的view進行重新onBindViewHolder
此項目已上傳到githut:點擊打開鏈接
以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
札記:android手勢識別功能實現(xiàn)(利用MotionEvent)
現(xiàn)在手勢識別的應用已經(jīng)很廣泛了。本篇文章主要介紹了android手勢識別功能實現(xiàn),具有一定的參考價值,有興趣的可以了解一下。2016-11-11Android自定義View實現(xiàn)QQ運動積分轉(zhuǎn)盤抽獎功能
這篇文章主要為大家詳細介紹了Android自定義View實現(xiàn)QQ運動積分轉(zhuǎn)盤抽獎功能,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-10-10Android 滑動Scrollview標題欄漸變效果(仿京東toolbar)
這篇文章主要介紹了Android 滑動Scrollview標題欄漸變效果(仿京東toolbar),本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-01-01Android 自定義view實現(xiàn)進度條加載效果實例代碼
這篇文章主要介紹了Android 自定義view實現(xiàn)進度條加載效果實例代碼,需要的朋友可以參考下2017-08-08