我有一个ArrayAdapter用于列表视图,其中包含多个按钮。对于一个切换按钮,我希望基于条件具有默认状态,并且还允许用户切换按钮。
但是,当用户单击第1行上的按钮时,实际上会选择第3行的按钮。我不确定为什么会这样。以下是我的getView
方法中包含评论的相关代码片段。
我的切换按钮的布局
<ToggleButton android:id="@+id/color_toggle"
android:layout_width="50px"
android:layout_height="50px"
android:focusable="false"
android:textOn="" android:textOff="" android:layout_alignParentLeft="true"
android:layout_marginRight="10dp"
/>
class Color {
int id;
int something;
}
List<Color> colorsList;
class ColorHolder {
TextView colorNameText;
ToggleButton toggleButton;
}
public View getView(final int position, final View convertView, final ViewGroup parent) {
View rowView = convertView;
Color c = colorsList.get(position);
if (null == rowView) {
rowView = this.inflater.inflate(R.layout.list_item_color, parent, false);
holder = new ColorHolder();
holder.colorNameText = (TextView) rowView.findViewById(R.id.color_name);
holder.toggleButton = (ToggleButton) rowView.findViewById(R.id.color_toggle);
rowView.setTag(holder);
}
else {
holder = (ColorHolder)rowView.getTag();
}
holder.toggleButton.setTag(c.getId());
final ColorHolder thisRowHolder = holder;
holder.toggleButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (thisRowHolder.toggleButton.isChecked()) {
thisRowHolder.toggleButton.setBackgroundDrawable(//normal button);
thisRowHolder.toggleButton.setChecked(false);
for (int i = 0; i < colorList.size(); i++) {
if (colorList.get(i) == (Integer)v.getTag()) {
colorList.get(i).setSomething(0);
break;
}
}
adapter.notifyDataSetChanged();
}
else {
thisRowHolder.toggleButton.setBackgroundDrawable(//enabled button);
thisRowHolder.toggleButton.setChecked(true);
for (int i = 0; i < colorList.size(); i++) {
if (colorList.get(i) == (Integer)v.getTag()) {
colorList.get(i).setSomething(1);
break;
}
}
adapter.notifyDataSetChanged();
}
}
});
if (c.getSomething()>0) {
holder.toggleButton.setBackgroundDrawable(//enabled button);
holder.toggleButton.setChecked(true);
}
else {
holder.toggleButton.setBackgroundDrawable(//normal button);
holder.toggleButton.setChecked(false);
}
return rowView;
}
问题
我做错了什么?为什么即使我在第一行切换按钮,第三行中的其他按钮也会切换。
我读到这是因为listView回收,是否有办法修复它?基于类似的问题,我尝试了一些无用的策略:1)将onClickListener
放在if
子句中。 2)而不是在int
中设置setTag
而不是设置holder
并在holder
中使用onClickListener
更新
我已根据收到的建议更新了问题中的所有代码。
答案 0 :(得分:1)
希望这有助于。
活动代码
public class DemoActivity extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
ColorInfo[] clr= new ColorInfo[20];
for(int i=0;i<20;i++){
clr[i] = new ColorInfo();
}
((ListView)findViewById(R.id.list)).setAdapter(new MyAdapter(this, 0, clr));
}
private static class MyAdapter extends ArrayAdapter<ColorInfo> implements OnClickListener{
LayoutInflater inflater;
public MyAdapter(Context context, int textViewResourceId,
ColorInfo[] objects) {
super(context, textViewResourceId, objects);
inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if(convertView == null){
convertView = inflater.inflate(R.layout.row, null);
holder = new ViewHolder();
holder.tgl = (ToggleButton) convertView.findViewById(R.id.toggle);
convertView.setTag(holder);
}
holder = (ViewHolder) convertView.getTag();
holder.tgl.setTag(position);
holder.tgl.setOnClickListener(this);
holder.tgl.setChecked(getItem(position).isChecked);
return convertView;
}
private static class ViewHolder{
ToggleButton tgl;
}
public void onClick(View v) {
int pos = (Integer) v.getTag();
ColorInfo cinfo = getItem(pos);
cinfo.isChecked = !cinfo.isChecked;
}
}
private static class ColorInfo{
boolean isChecked=false;
}
}
main.xml中
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<ListView
android:id="@+id/list"
android:layout_width="fill_parent"
android:layout_height="wrap_content" >
</ListView>
</LinearLayout>
row.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<ToggleButton android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/toggle"
/>
</LinearLayout>
答案 1 :(得分:0)
您正在为ViewHolder使用成员变量,而不是最终的本地变量。因此,您的OnClickListener引用了最新的holder
实例,它将与最近创建或回收的列表项相对应。
请改为:
//Lock in this reference for the OnClickListener
final ColorHolder thisRowHolder = holder;
holder.favButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (thisRowHolder.toggleButton.isChecked()) {
thisRowHolder.toggleButton.setBackgroundDrawable(getResources().getDrawable(...);
thisRowHolder.toggleButton.setChecked(false);
}
else {
thisRowHolder.toggleButton.setBackgroundDrawable(getResources().getDrawable(...));
thisRowHolder.toggleButton.setChecked(true);
}
}
});
...
修改强>
也注意到这一点。在这两行中:
holder.colorNameText = (TextView) itemView.findViewById(R.id.color_name);
holder.toggleButton = (ToggleButton) itemView.findViewById(R.id.color_toggle);
您正在某个成员变量itemView
中查找视图,但您需要在rowView
中找到它们,以便获取此特定行的实例。所有视图持有者都在查看同一个ToggleButton实例,它甚至可能不在屏幕上。
编辑2:
你还缺少一件事。您需要存储切换按钮的状态并重新应用它们。因此,在OnClickListener中,当您调用setChecked()
时,还必须更新colorsList
中的支持数据。看起来你已经在ToggleButton的ID中缓存了对正确列表元素的引用,所以应该很容易。然后将这段代码移出if / else块,然后将其放入,因此切换按钮始终更新为最新数据:
if (c.getSomething()>0) {
holder.toggleButton.setBackgroundDrawable(getResource().getDrawable(...)));
holder.setChecked(false);
}
else {
holder.toggleButton.setBackgroundDrawable(getResource().getDrawable(...)));
holder.setChecked(true);
}
答案 2 :(得分:0)
您的问题是listview回收视图
您必须为listview的每一行存储切换按钮的状态。 Eg.Create类存储有关每行的信息,假设ColorInfo包含color和isChecked boolean。 所以而不是
Color c = colorsList.get(position);
它将是
ColorInfo colorInfo = colorsList.get(position);
和getview
togglebutton.setCheck(colorInfo.isCheck)
在切换按钮的onClick监听器中,您将该位置的ColorInfo对象的状态更改为toggleChecked true或false以及notifyDatasetChanged,这将解决您的问题。