android listview 重用view導致的選擇混亂問題,androidlistview
20150526
listview是常用的控制項,經常用自訂的adapter,為了提高顯示效率,常利用view的重用方式防止重繪,但因為重用利用的是舊的view,常導致顯示的資料會由於position的位置出現錯亂。在一個app項目中我遇到過多次這個問題,包括帶Button的都能很好的解決,但今天遇到listview中的item有togglelbutton的情況,綁定的監聽器是togglebutton的CompoundButton.OnCheckedChangeListener(),竟然出現了問題,一直沒有解決,最後將item的監聽換成了View.OnClickListener()才解決問題。
一般,為了防止資料混亂,會在convertview判斷null的if-else之後再擷取list裡的顯示資料。getview的例子如下:
1 @Override 2 public View getView(int position, View convertView, ViewGroup parent) 3 { 4 // TODO Auto-generated method stub 5 final ViewHolder holder; 6 // 最佳化listview --去掉重用,防止togglebutton的點擊位置記錄出錯 7 if(convertView == null) 8 { 9 // 使用自訂的布局 10 holder = new ViewHolder(); 11 convertView = mInflater 12 .inflate(R.layout.list_invite_party_member, null); 13 // 初始化布局中的元素 14 holder.ivAvatar = (ImageView) convertView.findViewById(R.id.iv_avater); 15 holder.tvName = (TextView) convertView.findViewById(R.id.tv_name); 16 holder.tvTag = (TextView) convertView.findViewById(R.id.tv_tag); 17 holder.btnSelect = (ToggleButton) convertView 18 .findViewById(R.id.btn_select); 19 holder.linearLayout = (LinearLayout) convertView 20 .findViewById(R.id.rl_friend_item); 21 convertView.setTag(holder); 22 } else 23 { 24 holder = (ViewHolder)convertView.getTag(); 25 } 26 27 // 綁定資料 28 final int index = position; 29 UserBean bean = listFriend.get(index); 30 // 設定頭像 31 if (!TextUtils.isEmpty(bean.getUserAvatar())) 32 { 33 String avatarUrl = Constant.URL_USER_AVATER + bean.getUserAvatar(); 34 // 初始化非同步載入頭像對象 35 finalBitmap = FinalBitmap.create(context); 36 finalBitmap.configLoadingImage(R.drawable.user_head_02); 37 finalBitmap.display(holder.ivAvatar, avatarUrl); 38 } else 39 { 40 holder.ivAvatar.setImageResource(R.drawable.user_head_02); 41 } 42 43 if (!TextUtils.isEmpty(bean.getUserNickname())) 44 { 45 holder.tvName.setText(bean.getUserNickname()); 46 } else 47 { 48 holder.tvName.setText(bean.getUserPhone()); 49 } 50 if (!TextUtils.isEmpty(CommonUtils.getUserTags(bean))) 51 { 52 holder.tvTag.setText(CommonUtils.getUserTags(bean)); 53 holder.tvTag.setVisibility(View.VISIBLE); 54 } else 55 { 56 holder.tvTag.setVisibility(View.GONE); 57 } 58 if (TextUtils.equals(bean.getReserved01(), "1")) 59 { 60 // 已添加的場合顯示為 刪除 61 holder.btnSelect.setChecked(true); 62 } else 63 { 64 holder.btnSelect.setChecked(false); 65 } 66 holder.btnSelect.setOnClickListener(new View.OnClickListener() { 67 68 @Override 69 public void onClick(View v) { 70 // TODO Auto-generated method stub 71 ToggleButton view = (ToggleButton)v; 72 //boolean isCheckedOld = view.getText().toString().equals("添加")?true:false; 73 // 擷取最新的點擊後check狀態 74 boolean isChecked = view.isChecked(); 75 if (isChecked) 76 { 77 // 添加了該人,button顯示刪除 78 view.setChecked(true); 79 listFriend.get(index).setReserved01("1"); 80 81 } else 82 { 83 // 原來是被選中的,點擊後該人被刪除 84 // 刪除了該人,button顯示添加 85 view.setChecked(false); 86 listFriend.get(index).setReserved01("0"); 87 } 88 } 89 }); 90 /* holder.btnSelect 91 .setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() 92 { 93 94 @Override 95 public void onCheckedChanged(CompoundButton buttonView, 96 boolean isChecked) 97 { 98 99 LogUtil.d("isChecked= " + isChecked );100 LogUtil.d("index= " + index );101 // TODO Auto-generated method stub102 if (isChecked)103 {104 // 添加了該人,button顯示刪除105 listFriend.get(index).setReserved01("1");106 } else107 {108 // 刪除了該人,button顯示添加109 listFriend.get(index).setReserved01("0");110 }111 }112 });*/113 return convertView;114 }115 116 /**117 * 布局中的元素118 */119 class ViewHolder120 {121 ImageView ivAvatar;122 TextView tvName;123 TextView tvTag;124 ToggleButton btnSelect;125 LinearLayout linearLayout;126 }View Code
並且用final的index記住了資料的位置,在下面button監聽的動作中就可以獲得正確的資料了。
但是實驗發現(注釋掉的部分)利用此方法還是不可以,可能是CompoundButton.OnCheckedChangeListener()的問題吧。利用此監聽器監聽button的動作改變list相應資料會導致位置混亂。只好借用View.OnClickListener()來控制togglebutton的顯示了。倒也不算麻煩,本來togglebutton就一般是這兩種控制方式。還有要注意的是只要點擊的togglebutton,它的check狀態就會變,在View.OnClickListener()中也是一樣。
mark一下,所以利用adapter記住btn的狀態這件事還是很簡單的,就是沒有理解到CompoundButton的機制而導致的失敗,還好有View.OnClickListener()成功的先例,要不然每個view都繪製list資料很多的話也太不現實了。還有一個checkbox的item沒有嘗試,不行的話還是要用button或View.OnClickListener()來替代了。