2011-12-03 15 views
36

Esto es realmente extraño.ListView: setItemChecked solo funciona con ArrayAdapter estándar - NO funciona cuando se usa ArrayAdapter personalizado.

Cuando utilizo el ArrayAdapter estándar para un ListView llamando setItemChecked funciona bien

Pero cuando se utiliza un encargo a ArrayAdapter no lo hace.

¿Cuál sería el motivo? ¿Es esto un error? ¿O me estoy perdiendo algo?

public class Test_Activity extends Activity { 

    /** Called when the activity is first created. */ 
    private List<Model> list; 
    private ListView lView; 

    public void onCreate(Bundle icicle) { 
    super.onCreate(icicle); 
    // Create an array of Strings, that will be put to our ListActivity 
    setContentView(R.layout.main); 
    lView = (ListView) findViewById(R.id.ListView01); 
    lView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE); 

    list = getModel(); 

    //with this adapter setItemChecked works OK 
    lView.setAdapter(new ArrayAdapter<Model>(this, 
     android.R.layout.simple_list_item_multiple_choice, list)); 


    //************************** 
    //PROBLEM: with this adapter it does not check any items on the screen 
    // ArrayAdapter<Model> adapter = new Test_Class1(this, list); 
    // lView.setAdapter(adapter); 



    } 

    private List<Model> getModel() { 
     List<Model> list = new ArrayList<Model>(); 
     list.add(get("0")); 
     list.add(get("1")); 
     list.add(get("2")); 
     list.get(1).setSelected(true); 
     Model m = list.get(1); 
     list.add(get("3")); 
     list.add(get("4")); 
     list.add(get("5")); 
     list.add(get("6")); 
     list.add(get("7")); 
     // Initially select one of the items 
     return list; 
    } 

    private Model get(String s) { 
     return new Model(s); 
    } 

    @Override 
    public boolean onCreateOptionsMenu(Menu menu) { 
     MenuInflater inflater = getMenuInflater(); 
     inflater.inflate(R.menu.results_screen_option_menu, menu); 
     return true; 
    } 

    /** 
    * @category OptionsMenu 
    */ 
    @Override 
public boolean onOptionsItemSelected(MenuItem item) { 
    switch (item.getItemId()) { 
    case R.id.select_all: { 

     int size = lView.getAdapter().getCount(); 

     for (int i = 0; i <= size; i++) { 

      //************************** PROBLEM 
     lView.setItemChecked(i, true); // selects the item only for standard ArrayAdapter 

    Log.i("xxx", "looping " + i); 
     } 
    } 
     return true; 
    case R.id.select_none: 
     return true; 
    } 
    return false; 
    } 
} 

// ----------------------------------------- -------------------

public class Test_Class1 extends ArrayAdapter<Model> { 

    private final List<Model> list; 
    private final Activity context; 

public Test_Class1(Activity context, List<Model> list) { 
    super(context, R.layout.rowbuttonlayout2, list); 
    this.context = context; 
    this.list = list; 
    } 

    static class ViewHolder { 
    protected TextView text; 
    protected CheckBox checkbox; 
    } 

    @Override 
public View getView(int position, View convertView, ViewGroup parent) { 
    View view = null; 
    Log.i("xxx", "-> getView " + position); 
    if (convertView == null) { 
     LayoutInflater inflator = context.getLayoutInflater(); 
     view = inflator.inflate(R.layout.rowbuttonlayout, null); 
     final ViewHolder viewHolder = new ViewHolder(); 
     viewHolder.text = (TextView) view.findViewById(R.id.label); 
     viewHolder.checkbox = (CheckBox) view.findViewById(R.id.check); 
     viewHolder.checkbox 
      .setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { 

      @Override 
      public void onCheckedChanged(CompoundButton buttonView, 
       boolean isChecked) { 
       Model element = (Model) viewHolder.checkbox 
        .getTag(); 
       Log.i("xxx", "-> onCheckedChanged"); 
       element.setSelected(buttonView.isChecked()); 
       Log.i("xxx", "<- onCheckedChanged"); 

      } 
      }); 
     view.setTag(viewHolder); 
     viewHolder.checkbox.setTag(list.get(position)); 
    } else { 
     view = convertView; 
     ((ViewHolder) view.getTag()).checkbox.setTag(list.get(position)); 
    } 
    ViewHolder holder = (ViewHolder) view.getTag(); 
    holder.text.setText(list.get(position).getName()); 
    Log.i("xxx", "holder.checkbox.setChecked: " + position); 

    holder.checkbox.setChecked(list.get(position).isSelected()); 
    Log.i("xxx", "<- getView " + position); 

    return view; 
    } 

} 

Respuesta

46

su diseño fila tiene que ser Checkable para setItemChecked() a trabajar, en cuyo caso androide gestionará llamando setChecked() en su Checkable como el usuario hace clic en la fila. No necesitaría configurar su propio OnCheckedChangeListener.

Para más información, ver:

+27

increíble - ¿por qué esto tiene que ser tan complicado y difícil de averiguar? También hago programación de iPhone, mientras que hasta ahora prefería Android, esto es un retroceso real. Muchas gracias ! – user387184

+3

Miré el código de muestra en detalle ahora y decidí no seguir esta solución. En su lugar, construyo una matriz de CheckBoxes en mi ArrayAdapter. Con esto a mano, puedo fácilmente hacer el tipo de cosas que necesito. ¡Muchas gracias de nuevo por apuntarme en la dirección! – user387184

+0

enlace actualizado al tutorial de marvinlabs http://blog.marvinlabs.com/2010/10/29/custom-listview-ability-check-items/ –

1

comprobable es un perro de aprendizaje Veo que prefiero no tomar ahora. Puede establecer y desarmar el CheckBox manualmente en las actividades 'OnItemClickListener. Mantenga una variable transitoria booleana isChecked en la lista MyObject en el ArrayAdapter<MyObject>.

public void onItemClick(AdapterView<?> av, View v, int position, long arg3) { 
    final ListView listView = (ListView) findViewById(android.R.id.list); 
    MyObject item = (MyObject) listView.getItemAtPosition(position); 
    item.isChecked = !item.isChecked; 
    if(item.isChecked) 
     addItem(item); 
    else 
     removeItem(item); 
} 

Para tener en cuenta a los usuarios hacer clic en el CheckBox sí; utilice la misma lógica addItem (elemento)/removeItem (elemento) en la implementación getView del adaptador de matriz personalizado, en CheckBox 's OnClickListener.

public View getView(int position, View convertView, ViewGroup viewGroup) { 
     CheckBox cb = null; 
     if (convertView == null) { 
      if(inflater == null) //cache to avoid reconstructing for each view 
      inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE); 
      convertView =inflater.inflate(R.layout.list_item_text_right_checkmark, null); 

      CheckBox cb = (CheckBox) convertView.findViewById(R.id.check); 
      cb.setChecked((list.get(position).isChecked)); 
      cb.setTag(list.get(position); 
      public void onClick(View v) { 
       if(v instanceof CheckBox) { 
        CheckBox cb = (CheckBox) v; 
        if(cb.isChecked()) //verify boolean logic here!!! 
         activityRef.get().addItem(cb.getTag()); //WeakReference to activity 
        else 
         activityRef.get().removeItem(cb.getTag()); 

       } 
      }); 
    } else cb = (CheckBox) convertView.findViewById(R.id.check); 
    cb.setChecked((list.get(position).isChecked)); 
... 
} 

Llamar CheckBox :: setChecked() es la clave aquí.
Esto parece ser el truco, siempre y cuando el adaptador de matriz pueda asumir la misma actividad, y el adaptador de matriz puede asumir la misma definición de elemento de lista xml en res/layout /.

comprobable lista de elementos XML:

<?xml version="1.0" encoding="utf-8"?> 
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent" 
    android:orientation="vertical" > 

    <TextView 
     android:id="@+id/text" 
     android:layout_width="wrap_content" 
     android:layout_height="wrap_content" 
     android:layout_toLeftOf="@+id/check" 
     android:gravity="center_vertical" 
     android:paddingLeft="8dp" 
     android:textAppearance="?android:attr/textAppearanceMedium" /> 

    <CheckBox 
    android:id="@+id/check" 
    android:layout_width="wrap_content" 
    android:layout_height="wrap_content" 
    android:layout_alignParentRight="true" 
    android:focusable="false"/> 
</RelativeLayout> 

Si es necesario comprobar algunas de sus checkboxs inicialmente, acaba de establecer sus miembros IsChecked en su MiObjeto antes de inflar.

9

Para asegurarse de que tenga que configurar el modo de elección para su ListView, por ejemplo:

list.setChoiceMode(ListView.CHOICE_MODE_SINGLE); 

Pero si usted también está usando un diseño personalizado para su elemento de la lista (R.layout.drawing_list_item), entonces usted tiene que hacer Asegúrese de que su diseño implementa la interfaz comprobable:

public class CheckableRelativeLayout extends RelativeLayout implements Checkable { 
    private boolean isChecked = false; 

    public CheckableRelativeLayout(Context context, AttributeSet attrs) { 
     super(context, attrs); 
    } 

    public boolean isChecked() { 
     return isChecked; 
    } 

    public void setChecked(boolean isChecked) { 
     this.isChecked = isChecked; 
     changeColor(isChecked); 
    } 

    public void toggle() { 
     this.isChecked = !this.isChecked; 
     changeColor(this.isChecked); 
    } 

    private void changeColor(boolean isChecked){ 
     if (isChecked) { 
      setBackgroundColor(getResources().getColor(android.R.color.holo_blue_light)); 
     } else { 
      setBackgroundColor(getResources().getColor(android.R.color.transparent)); 
     } 
    } 
} 

Luego, su diseño comprobable se define como el siguiente ejemplo:

<?xml version="1.0" encoding="utf-8"?> 
<it.moondroid.banking.CheckableRelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    android:id="@+id/drawer_item_layout" 
    android:layout_width="match_parent" 
    android:layout_height="wrap_content" 
    android:padding="10dp" > 

    <ImageView 
     android:id="@+id/agenzie_item_icon" 
     android:layout_width="30dp" 
     android:layout_height="30dp" 
     android:layout_alignParentLeft="true" 
     android:layout_centerVertical="true" 
     android:background="@android:color/darker_gray" 
     android:focusable="false" /> 

    <TextView 
     android:id="@+id/agenzie_item_name" 
     android:layout_width="wrap_content" 
     android:layout_height="wrap_content" 
     android:layout_centerVertical="true" 
     android:layout_toRightOf="@+id/agenzie_item_icon" 
     android:focusable="false" 
     android:paddingLeft="10dp" 
     android:text="item" 
     android:textAppearance="?android:attr/textAppearanceSmall" 
     android:textColor="#FFFFFF" /> 

</it.moondroid.banking.CheckableRelativeLayout> 
0

Ninguna de las respuestas funcionó para mí. Mi problema era que solo la llamada inicial a ExpandableListView.setItemChecked desde OnCreate/OnStart/OnPause no funcionaba como si la vista aún no se hubiera inicializado completamente. Para resolver esto, tenía que hacer lo siguiente:

_expandableListView.Post(() => 
{ 
    _expandableListView.SetItemChecked(fragmentPosition, true); 
}); 
Cuestiones relacionadas