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 }
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