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 stub 102 if (isChecked) 103 { 104 // 添加了该人,button显示删除 105 listFriend.get(index).setReserved01("1"); 106 } else 107 { 108 // 删除了该人,button显示添加 109 listFriend.get(index).setReserved01("0"); 110 } 111 } 112 });*/ 113 return convertView; 114 } 115 116 /** 117 * 布局中的元素 118 */ 119 class ViewHolder 120 { 121 ImageView ivAvatar; 122 TextView tvName; 123 TextView tvTag; 124 ToggleButton btnSelect; 125 LinearLayout linearLayout; 126 }
并且用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()来替代了。