2011-12-03 7 views
36

To jest naprawdę dziwne.ListView: setItemChecked działa tylko ze standardowym ArrayAdapter - NIE działa przy użyciu niestandardowego ArrayAdapter?

Gdy używam standardowego ArrayAdapter dla ListView wywołującego setItemChecked działa OK

Ale kiedy przy użyciu Custom Made ArrayAdapter nie.

Jaki byłby powód? Czy to błąd? Czy może czegoś brakuje?

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

} 

Odpowiedz

46

Twój układ wiersz musi być Checkable dla setItemChecked() do pracy, w którym to przypadku Android będzie zarządzać nazywając setChecked() na swój Checkable jako użytkownik klika wiersz. Nie trzeba konfigurować własnego OnCheckedChangeListener.

uzyskać więcej informacji, zobacz:

+27

Niewiarygodne - dlaczego to musi być tak skomplikowane i trudne do odkrycia? Robię też programowanie na iPhone'ie, a do tej pory naprawdę preferowałem Androida, to jest prawdziwe cofnięcie. Dziękuję bardzo ! – user387184

+3

Popatrzyłem teraz na przykładowy kod i zdecydowałem się nie stosować tego rozwiązania. Zamiast tego buduję tablicę CheckBoxów w moim ArrayAdapter. Mając to pod ręką, mogę z łatwością zrobić to, czego potrzebuję. Jeszcze raz bardzo dziękuję za wskazanie mnie w tym kierunku! – user387184

+0

zaktualizowany link do samouczka marvinlabs http://blog.marvinlabs.com/2010/10/29/custom-listview-ability-check-items/ –

1

Rejestrowalne jest uczenie bież ve Wolę nie brać teraz. Możesz ręcznie ustawić i zresetować CheckBox w działaniu "OnItemClickListener. Zachowaj zmienną binarną isChecked na liście MyObject w 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); 
} 

Aby uwzględnić użytkowników, którzy kliknęli na samą stronę CheckBox; użyj tej samej logiki addItem (element)/removeItem (element) w implementacji niestandardowego adaptera tablicy getView, w CheckBox 's .

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

Wywołanie CheckBox :: setChecked() jest tutaj kluczowe.
Wydaje się, że działa to tak długo, jak długo adapter karty może przyjąć to samo działanie, a adapter tablicy może przyjąć tę samą definicję elementu xml w res/layout /.

dostępne do kontroli element listy 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> 

Jeśli trzeba sprawdzić niektóre CheckBoxs początkowo, po prostu ustawić IsChecked członków w MyObject przed pompowania.

9

Na pewno trzeba ustawić tryb wyboru dla ListView, na przykład:

list.setChoiceMode(ListView.CHOICE_MODE_SINGLE); 

Ale jeśli używasz również niestandardowy układ dla elementu listy (R.layout.drawing_list_item), potem trzeba zrobić że Twój układ realizuje Rejestrowalne interfejs:

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

Wtedy twój dostępne do kontroli układu zostaną zdefiniowane jak w poniższym przykładzie:

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

Żadna z odpowiedzi nie zadziałała. Mój problem polegał na tym, że tylko początkowe wywołanie funkcji ExpandableListView.setItemChecked z OnCreate/OnStart/OnPause nie działało tak, jakby widok nie był jeszcze w pełni zainicjowany. Aby rozwiązać ten problem, musiałem wykonać następujące czynności:

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