listview是常用的控件,经常用自定义的adapter,为了提高显示效率,常利用view的重用方式防止重绘,但因为重用利用的是旧的view,常导致显示的数据会由于position的位置出现错乱。在一个app项目中我遇到过多次这个问题,包括带Button的都能很好的解决,但今天遇到listview中的item有togglelbutton的情况,绑定的监听器是togglebutton的CompoundButton.OnCheckedChangeListener(),竟然出现了问题,一直没有解决,最后将item的监听换成了View.OnClickListener()才解决问题。

一般,为了防止数据混乱,会在convertview判断null的if-else之后再获取list里的显示数据。getview的例子如下:

android recyclerview 复用的item animation失效 listview复用导致错乱_ide

android recyclerview 复用的item animation失效 listview复用导致错乱_自定义_02

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     }

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()来替代了。

MissR:stay