2014-02-16 1 views
2

У меня есть ArrayAdapter для представления списка, в котором есть несколько кнопок. Для одной кнопки переключения я хочу иметь состояние по умолчанию, основанное на условии, и позволить пользователям также переключать кнопку.OnClickListener в 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 и использовать его в holderonClickListener

обновление

Я обновил весь код в вопросе с предложениями, которые я получил.

+1

это происходит bcoz ListView перерабатывает взгляды – Raghunandan

+0

Я читал, что ... не существует никакого способа почини это? – Anthony

+0

проверьте это, если это поможет http://stackoverflow.com/questions/20611123/listview-subobject-clickable-confilct/20612237#20612237 – Raghunandan

ответ

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

... 

Edit:

заметил Также это. В этих двух строках:

holder.colorNameText = (TextView) itemView.findViewById(R.id.color_name); 
holder.toggleButton = (ToggleButton) itemView.findViewById(R.id.color_toggle); 

Вы найти точку в какой-либо переменной-члена itemView, но вам нужно будет найти их в rowView, так что вы получаете экземпляры для этой конкретной строки. Все ваши наблюдатели видят тот же экземпляр ToggleButton, который может даже не отображаться на экране.

Edit 2:

Еще одна вещь, которую вы пропустили. Вам нужно сохранить состояние переключающих кнопок и повторно применить их. Поэтому в вашем OnClickListener, когда вы вызываете setChecked(), вы также должны обновить данные резервного копирования в colorsList. Похоже, вы уже кэшировали ссылку на правильный элемент списка в своем идентификаторе ToggleButton, поэтому это должно быть легко.Затем переместите этот блок кода из вашего, если/другое блок и поместить его после этого, поэтому кнопка переключения всегда обновляется последним данным:

if (c.getSomething()>0) { 
    holder.toggleButton.setBackgroundDrawable(getResource().getDrawable(...))); 
    holder.setChecked(false); 
    } 
    else { 
    holder.toggleButton.setBackgroundDrawable(getResource().getDrawable(...))); 
    holder.setChecked(true); 
    } 
+0

hmmm теперь я не вижу, что кнопки переключаются вообще, хотя сообщения журнала отображаются в 'if'' else' – Anthony

+0

См. Обновленный ответ. – Tenfour04

+0

Извините, что «ItemView» был опечаткой. предполагалось сказать 'c.XXX'. Я исправил его сейчас – Anthony

0

Ваша проблема является видом утилизации ListView

Вы должны сохранить состояние кнопки переключения для каждой строки списка. Eg.Create класс, который хранит информацию о каждой строке, предположим, что ColorInfo содержит цвет и isChecked boolean. поэтому вместо

Color c = colorsList.get(position); 

будет

ColorInfo colorInfo = colorsList.get(position); 

и GetView

togglebutton.setCheck(colorInfo.isCheck) 

и в OnClick слушателю коленчатых кнопки вы изменить состояние объекта ColorInfo для этой позиции в toggleChecked true или false и notifyDatasetChanged, это решит вашу проблему.

+0

hmmm, поэтому я должен добавить логическое свойство типа 'isCheck' в мой Colo POJO? – Anthony

+0

Да, как уже упоминалось, вам нужно сохранить состояние строки, поэтому listview может указывать состояние каждой строке при ее рисовании – Pankaj

+0

Можете ли вы увидеть мой обновленный код. Я сохраняю состояние в своем POJO, а затем обновляю список, вызвав notifyDatasetChanged. Тем не менее у меня такая же проблема ... – Anthony

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> 
Смежные вопросы