你的位置:首页 > 操作系统

[操作系统]android listview 重用view导致的选择混乱问题


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