web-dev-qa-db-fra.com

ListView: setItemChecked fonctionne uniquement avec ArrayAdapter standard - ne fonctionne PAS lorsque vous utilisez ArrayAdapter personnalisé?

C'est vraiment bizarre.

Lorsque j'utilise l'ArrayAdapter standard pour un appel ListView, setItemChecked fonctionne correctement

Mais lorsque vous utilisez un ArrayAdapter personnalisé, ce n'est pas le cas.

Quelle serait la raison? Est-ce un bug? Ou est-ce que je manque quelque chose?

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

}
37
user387184

Votre disposition des lignes doit être Checkable pour que setItemChecked() fonctionne, auquel cas Android gérera l'appel de setChecked() sur votre Checkable lorsque l'utilisateur clique sur la ligne. Vous n’auriez pas besoin de configurer votre propre OnCheckedChangeListener.

Pour plus, voir:

47
CommonsWare

Vous devez bien sûr définir le mode de choix pour votre ListView, par exemple:

list.setChoiceMode(ListView.CHOICE_MODE_SINGLE);

Mais si vous utilisez également une présentation personnalisée pour votre élément de liste (R.layout.drawing_list_item), vous devez vous assurer que votre présentation implémente l'interface Checkable:

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

Ensuite, votre mise en page vérifiable sera définie comme dans l'exemple suivant:

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

Checkable est une courbe d'apprentissage que je préfère ne pas suivre maintenant. Vous pouvez définir et désélectionner manuellement la CheckBox dans le champ OnItemClickListener des activités. Conservez une variable transitoire booléenne isChecked dans la liste MyObject du 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);
}  

Pour rendre compte des utilisateurs qui cliquent sur le CheckBox lui-même; utilisez la même logique addItem (item)/removeItem (item) dans l'implémentation getView de votre adaptateur de tableau personnalisé, dans 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));
...
}

L'appel de CheckBox :: setChecked () est la clé ici.
Cela semble faire l'affaire tant que votre adaptateur RAID peut assumer la même activité et qu'il peut assumer la même définition XML d'élément de liste dans res/layout /. 

élément de liste vérifiable 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 vous devez vérifier initialement certaines de vos cases à cocher, définissez simplement vos membres isChecked dans votre MyObject avant de gonfler. 

1
stephen

Aucune des réponses n'a fonctionné pour moi. Mon problème était que seul l'appel initial à ExpandableListView.setItemChecked à partir de OnCreate/OnStart/OnPause ne fonctionnait pas comme si la vue n'était pas encore complètement initialisée. Afin de résoudre ce problème, je devais faire ce qui suit:

_expandableListView.Post(() =>
{
    _expandableListView.SetItemChecked(fragmentPosition, true);
});
0
Alex