2011-12-03 18 views
36

Questo è davvero strano.ListView: setItemChecked funziona solo con ArrayAdapter standard - NON funziona quando si utilizza l'ArrayAdapter personalizzato?

Quando uso l'ArrayAdapter standard per un ListView chiamando setItemChecked funziona bene

Ma quando si utilizza una misura ArrayAdapter non è così.

Quale sarebbe il motivo? è un insetto? O mi sta sfuggendo qualcosa?

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

} 

risposta

46

il layout fila deve essere Checkable per setItemChecked() al lavoro, in questo caso Android gestirà chiamando setChecked() sul tuo Checkable come l'utente fa clic sulla riga. Non è necessario impostare il proprio OnCheckedChangeListener.

Per di più, si veda:

+27

Incredibile - perché questo deve essere così complicato e difficile da scoprire? Faccio anche la programmazione per iPhone, mentre fino ad ora preferivo davvero Android, questo è un vero e proprio contraccolpo. Grazie mille ! – user387184

+3

Ho esaminato il codice di esempio in dettaglio ora e ho deciso di non seguire questa soluzione. Invece io costruisco un array di CheckBox nel mio ArrayAdapter. Con questo a portata di mano posso quindi facilmente fare il tipo di cose che ho bisogno. Grazie ancora per avermi indicato nella direzione! – user387184

+0

link aggiornato al tutorial di marvinlabs http://blog.marvinlabs.com/2010/10/29/custom-listview-ability-check-items/ –

1

ispezionabile è un cane di apprendimento Io preferisco non prenderlo adesso. È possibile impostare e disinserire manualmente il CheckBox nell'attività OnItemClickListener. Mantenere una variabile transitoria booleana isChecked nell'elenco MyObject nello 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); 
} 

Per rappresentano gli utenti cliccando sul CheckBox stesso; utilizzare la stessa logica addItem (elemento)/removeItem (elemento) nell'implementazione getView dell'adattatore di array personalizzato, nello .

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

Chiamare CheckBox :: setChecked() è la chiave qui.
Questo sembra fare il trucco finché l'adattatore di array può assumere la stessa attività e l'adattatore di array può assumere la stessa definizione di xml della voce di elenco in res/layout /.

controllabile elemento della lista 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> 

Se avete bisogno di controllare alcuni dei vostri CheckBoxs inizialmente, è sufficiente impostare i vostri membri IsChecked nel vostro MyObject prima di gonfiaggio.

9

Di sicuro è necessario impostare la modalità scelta per il vostro ListView, per esempio:

list.setChoiceMode(ListView.CHOICE_MODE_SINGLE); 

Ma se si sta utilizzando anche un layout personalizzato per il vostro elemento della lista (R.layout.drawing_list_item), allora si deve fare sicuro che il layout implementa l'interfaccia ispezionabile:

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

Poi il layout verificabile sarà definito come il seguente esempio:

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

Nessuna delle risposte ha funzionato per me. Il mio problema era che solo la chiamata iniziale a ExpandableListView.setItemChecked da OnCreate/OnStart/OnPause non funzionava come se la vista non fosse ancora completamente inizializzata. Per risolvere questo, ho dovuto fare quanto segue:

_expandableListView.Post(() => 
{ 
    _expandableListView.SetItemChecked(fragmentPosition, true); 
}); 
Problemi correlati