2010-09-07 5 views
7

Ich habe eine ListView, und innerhalb jedes Listenelements habe ich einige TextViews und eine CheckBox. Wenn ich eine CheckBox ankreuze und mein onCheckedChangeListener feuert, funktioniert alles wie es soll. Es werden jedoch zufällige andere Kontrollkästchen aktiviert, sobald eine Option ausgewählt ist. Hier ist ein Beispiel. Wenn ich auf die erste CheckBox klicke: 8 wird überprüft. 15 wird überprüft. 21 wird überprüft. 27 wird überprüft. 33 wird überprüft. 41 wird überprüft. Dann, wenn ich den ganzen Weg nach oben scrollen, werden keine bis 6 überprüft. Der nächste ist 13.Android: Problem mit ListViews und CheckBoxen

Grundsätzlich ... was ist los?

Antwort

10

Es scheint, dass Sie die convertView wiederverwenden, die auf der getView()-Methode, die Sie implementieren, übergeben wird.

Android versucht, die gleiche Ansicht für verschiedene Elemente in einem ListView zu verwenden. Sie müssen entweder (1) das Kontrollkästchen innerhalb des zurückgegebenen Elements manuell deaktivieren (immer setChecked aufrufen, bevor Sie getView zurückgeben) oder (2) convertView nicht verwenden, sondern eine neue Ansicht von getView zurückgeben

(1) empfohlen wird, denke ich,

+0

OK, also ging ich mit Option eins. Kurz bevor ich die Ansicht in getView zurückgebe, mache ich "checkBox.setChecked (false);". Wenn ich jetzt ein Kontrollkästchen ankreuze, scrolle ich nach unten und scrolle nach oben, es wird nicht mehr überprüft. Ich weiß, dass das Problem bei meiner Implementierung liegt und nicht bei Ihrem Konzept. Was mache ich falsch? –

+0

Wenn der Benutzer das Kontrollkästchen aktiviert, müssen Sie das Kontrollkästchen des Elements aktiviert haben, irgendwo in Ihrem Code. Wenn Sie beispielsweise 40 Elemente in Ihrem ListView-Objekt haben, können Sie ein Array mit booleschen Werten zum Speichern verwenden, wenn ein Kontrollkästchen für i-th aktiviert ist. Dann auf "getView", CheckBox.setChecked (booleanArray [Position]). – yuku

+0

Vielleicht ist es, weil es so spät in der Nacht ist, aber ich kann nicht herausfinden, wie man die Position tatsächlich aufzeichnet. In der getView-Methode kann ich die Position haben, aber im OnClick, wo ich die Position tatsächlich speichern muss, habe ich keinen Zugriff mehr darauf. –

5

funktioniert gut für ichduersiees

public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) { 

     final ViewHolder holder; 
     final Season season = (Season) getGroup(groupPosition); 
     if (convertView == null) { 
      LayoutInflater vi = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE); 
      convertView = vi.inflate(R.layout.season, parent, false); 
      holder = new ViewHolder(); 
      holder.title = (TextView) convertView.findViewById(R.id.season_title); 
      holder.checkBox = (CheckBox) convertView.findViewById(R.id.season_check_box); 
      convertView.setTag(holder); 
     } else { 
      holder = (ViewHolder) convertView.getTag(); 
     } 

     holder.title.setText(season.getTitle()); 
     holder.checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { 
      @Override 
      public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { 
       season.setChecked(isChecked); 
       adapter.notifyDataSetChanged(); 
      } 
     }); 

     holder.checkBox.setChecked(season.isChecked()); // position is important! Must be before return statement! 
     return convertView; 
    } 

    protected class ViewHolder { 
     protected TextView title; 
     protected CheckBox checkBox; 
    } 
+0

Ich weiß nicht, welche Jahreszeit ist, aber für mich scheint das zu funktionieren. Für andere, die ein Problem haben (ich persönlich hatte keine Probleme mit den Checkboxen, aber der Durchstreich-Effekt erschien zufällig auf den Gegenständen). Also: endgültiger ViewHolder-Halter; UND der setOnCheckedChangeListener war die zwei Dinge, die Dinge funktionierten. Hoffe das hilft anderen! Und danke Georgi! – erdomester

+0

Ihre awesome Yarr..ich tat eine Menge von R & D.This ist die beste Lösung –

+0

@Georgy Gobozov bitte zu diesem Thema sowie https://StackOverflow.com/questions/44561788/Checkbox-checked-unched-state-changes -auf-Scroll-Listenansicht-Basisadapter # –

0

ich war auch einen ähnlichen König von Problem konfrontiert, so dass nach vielen Lesen ich dieses Problem wie folgt gelöst:.

@Override 
    public View getView(int position, View convertView, ViewGroup parent) { 
     ViewHolder holder = null; 
     if (convertView == null) { 
      convertView = mInflater.inflate(R.layout.listview, null); 
      holder = new ViewHolder(); 
      holder.nameView = (TextView)convertView.findViewById(R.id.textView1); 
      holder.numberView = (TextView)convertView.findViewById(R.id.textView2); 
      holder.cb = (CheckBox)convertView.findViewById(R.id.checkBox1); 
      convertView.setTag(holder);     
     } else { 
      holder = (ViewHolder)convertView.getTag(); 
     } 
     holder.nameView.setText(mData.get(position).toString()); 
     holder.numberView.setText(mNumber.get(position).toString()); 
     holder.cb.setChecked(false); 
     holder.cb.setTag(position); 



     if(selected.indexOf(mNumber.get(position).toString()) >= 0) // Check whether this row checkbox was checked, for that we previously stored the textview text in selected variable 
     { 

     holder.cb.setChecked(true); 
     } 

     return convertView; 
    } 

} 

Hier, was ich tue das auf getView() Ich habe alle Kontrollkästchen deaktiviert und erneut manuell überprüft, die ich überprüft werden muss entsprechend der Textview es entspricht. Wenn der Benutzer also nach dem Aktivieren des ersten Kontrollkästchens nach unten scrollt, werden alle Kontrollkästchen in der Ansicht deaktiviert, und wenn er erneut nach oben scrollt, werden auch alle Kontrollkästchen deaktiviert, aber dann wird die zuvor angeklickte Checkbox erneut überprüft.

0

Das Problem kann leicht gelöst werden, indem man CheckBox Staaten hält. Ein selbst definiert und erklärte vollständigen Beispielklassencode für das gleiche ist wie below_

public class AreaDataAdapter extends ArrayAdapter<String> { 
    private List<String> areaList; 
    private Activity context; 
    ArrayList<Boolean> positionArray; 

    public AreaDataAdapter(Context context, int textViewResourceId, 
      List<String> offersAreaList) { 
     super(context, textViewResourceId, offersAreaList); 
     // TODO Auto-generated constructor stub 
     this.context=context; 
     this.areaList = offersAreaList; 

     positionArray = new ArrayList<Boolean>(offersAreaList.size()); 
      for(int i =0;i<offersAreaList.size();i++){ 
       positionArray.add(false); 
      } 

    } 

    public AreaDataAdapter(Activity context, List<String> offersAreaList) { 
     super(ShowOffersActivity.this, R.layout.filterlist_row, offersAreaList); 
     this.context=context; 
     this.areaList=offersAreaList; 

     positionArray = new ArrayList<Boolean>(offersAreaList.size()); 
      for(int i =0;i<offersAreaList.size();i++){ 
       positionArray.add(false); 
      } 

    } 

    public View getView(final int position, View convertView,ViewGroup parent) { 
     View row=convertView; 
     FilterViewHolder holder; 
     if (row==null) { 
      LayoutInflater inflater=getLayoutInflater(); 

      row=inflater.inflate(R.layout.filterlist_row, parent, false); 
      holder = new FilterViewHolder(); 
      holder.filterCheckBox = (CheckBox)row.findViewById(R.id.filter_checkbox); 
      holder.filterCheckBox.setTypeface(fontFace); 

      row.setTag(holder); 
     } else { 
      //holder = (View) row; 
      holder = (FilterViewHolder) row.getTag(); 

      /* When a listview recycles views , it recycles its present state as well as listeners attached to it. 
      * if the checkbox was checked and has a onCheckedChangeListener set, both will remain a part of 
      * recycled view based on position. So it is our responsibility to reset all states and remove 
      * previous listeners. 
      * The listener was removed as below:- 
      */ 
      holder.filterCheckBox.setOnCheckedChangeListener(null); 

     } 

      holder.filterCheckBox.setText(areaList.get(position)); 
      holder.filterCheckBox.setFocusable(false); 
      holder.filterCheckBox.setChecked(positionArray.get(position)); 
      holder.filterCheckBox.setText(areaList.get(position)); 

      holder.filterCheckBox.setOnCheckedChangeListener(new OnCheckedChangeListener() { 

       @Override 
       public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { 
        // TODO Auto-generated method stub 
        if (isChecked) { 
         //change state of concern item in 'positionArray' array to 'True' 
         positionArray.set(position, true); 
         //add the checked item in to area list which used to filter offers. 
         filterOffersByThisAreaList.add(areaList.get(position)); 

        } else { 
         //change state of concern item in 'positionArray' array to 'True' 
         positionArray.set(position, true); 
         //remove the unchecked item in to area list which used to filter offers. 
         filterOffersByThisAreaList.remove(areaList.get(position)); 

        } 
       } 
      }); 
     return row; 
    } 

} 

Individuelle Row (filterlist_row) für die ListView diesen CheckBox s enthält, als below_

<?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="wrap_content" 
android:orientation="vertical" > 

<CheckBox 
    android:id="@+id/filter_checkbox" 
    style="@style/CodeFont" 
    android:button="@drawable/bg_custom_checkbox" 
    android:text="Indian" /> 

<View 
    android:layout_width="match_parent" 
    android:layout_height="1dp" 
    android:background="#c9c9c3" /> 

</LinearLayout> 

CheckBox backgroung gestalten ist als below_

<?xml version="1.0" encoding="utf-8"?> 
<selector xmlns:android="http://schemas.android.com/apk/res/android"> 
<item android:drawable="@drawable/checkbox_s" android:state_checked="true"></item> 
<item android:drawable="@drawable/checkbox_ns" android:state_checked="false"></item> 

</selector> 
0

Ich hatte die gleiche Art von Problem mit meiner Sicht wechseln. Nach langem Suchen und Lesen stelle ich fest, dass dieses Problem auf eine Art Optimierung der von Android beworbenen Ansichten zurückzuführen ist. Es versucht, die Sichtenobjekte wiederzuverwenden. Wenn Sie also auf einen Schalter oder eine Checkbox klicken, lösen Sie den Callback der Änderung auch in einer anderen Ansicht aus. Die Optimierung funktioniert wirklich, aber wenn Sie nicht aufpassen oder das Verhalten (wie ich) nicht kennen, passieren einige ziemlich seltsame Ergebnisse.

Wie auch immer, ich diese einfache Lösung finden:

Die beste Praxis die convertView auf dem getView weitergegeben wiederzuverwenden ist() -Methode. Auf diese Weise optimieren Sie die Ram-Menge, die von der Listenansicht verwendet wird. Also speichere ich meine Ansichten in einem ViewHolder und programmiere das onCheckedChanged-Ereignis. Der Trick ist, bevor gesetzt, wenn der Schalter aktiviert ist oder nicht (in Ihrem Fall das Kontrollkästchen aktivieren) und den CheckedChange Listener definieren, setze ich einfach den Callback mit Null zurück, das stellt sicher, dass das Ereignis eines anderen Schalters nicht ausgelöst wird. Hier

ist das Snippet:

... 
viewHolder.switchView.setOnCheckedChangeListener(null); 
viewHolder.switchView.setChecked(myObject.getStatus() > 0); 
... 
viewHolder.switchView.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { 
      @Override 
      public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { 
       ... 
      } 
     }); 

myObject ist ein letztes Objekt, das die Cursor-Daten gespeichert hat.

Und unabhängig davon, ob der Schalter aktiviert ist oder nicht, müssen Sie die setChecked-Methode aufrufen.

Hoffe es hilft jemandem!

0
@Override 
public int getViewTypeCount() { 
    return getCount(); 
} 

@Override 
public int getItemViewType(int position) { 
    return position; 
} 

@Override 
public int getCount() { 
    return names.length; 
} 

@Override 
public long getItemId(int position) { 
    return 0; 
} 

fügen Sie diese Methode in benutzerdefinierten Adapter hinzu. es funktioniert für mich