ArrayIndexOutOfBoundsException z niestandardowym adapterem Android dla wielu widoków w ListView

185

Próbuję utworzyć niestandardowy adapter dla mojego ListView, ponieważ każdy element na liście może mieć inny widok (łącze, przełącznik lub grupę radiową), ale gdy próbuję uruchomić działanie korzystające z ListView, pojawia się błąd i aplikacja przestaje działać. Aplikacja jest przeznaczona na platformę Android 1.6.

Kod:

public class MenuListAdapter extends BaseAdapter {
 private static final String LOG_KEY = MenuListAdapter.class.getSimpleName();

 protected List<MenuItem> list;
 protected Context ctx;
 protected LayoutInflater inflater;

 public MenuListAdapter(Context context, List<MenuItem> objects) {
  this.list = objects;
  this.ctx = context;
  this.inflater = (LayoutInflater)this.ctx.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
 }

 @Override
 public View getView(int position, View convertView, ViewGroup parent) {
  Log.i(LOG_KEY, "Position: " + position + "; convertView = " + convertView + "; parent=" + parent);
  MenuItem item = list.get(position);
  Log.i(LOG_KEY, "Item=" + item );

        if (convertView == null)  {
            convertView = this.inflater.inflate(item.getLayout(), null);
        }

        return convertView;
 }

 @Override
 public boolean areAllItemsEnabled() {
  return false;
 }

 @Override
 public boolean isEnabled(int position) {
  return true;
 }

 @Override
 public int getCount() {
  return this.list.size();
 }

 @Override
 public MenuItem getItem(int position) {
  return this.list.get(position);
 }

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

 @Override
 public int getItemViewType(int position) {
  Log.i(LOG_KEY, "getItemViewType: " + this.list.get(position).getLayout());
  return this.list.get(position).getLayout();
 }

 @Override
 public int getViewTypeCount() {
  Log.i(LOG_KEY, "getViewTypeCount: " + this.list.size());
  return this.list.size();
 }

}

Błąd, który otrzymuję:

    java.lang.ArrayIndexOutOfBoundsException
  at android.widget.AbsListView$RecycleBin.addScrapView(AbsListView.java:3523)
  at android.widget.ListView.measureHeightOfChildren(ListView.java:1158)
  at android.widget.ListView.onMeasure(ListView.java:1060)
  at android.view.View.measure(View.java:7703)

Wiem, że aplikacja wraca getViewi wszystko wydaje się w porządku.

Będziemy wdzięczni za wszelkie pomysły na to, co może być przyczyną.

Dzięki,

-Dan

Dan
źródło
Cześć Dan, czy na pewno getCount ma całkowitą liczbę elementów, które będą wyświetlane na ListView ??? public int getCount () {return this.list.size (); }
Jorgesys,
4
Nie mogli wymyślić mniej opisowego komunikatu o błędzie ...
JoeyJ

Odpowiedzi:

515

Typ widoku elementu, z którego wracasz

getItemViewType()jest >= getViewTypeCount().

Romain Guy
źródło
24
Ten jest paskudny. Nie ma sposobu na debugowanie. Wielkie dzięki, Romain Guy za wyjaśnienie.
Yar
113
Inną rzeczą jest to, że ViewType musi zaczynać się od 0.
HelmiB
16
Do tej pory otrzymałeś 38 głosów za odpowiedzią, więc może to być idealna odpowiedź, ale proszę dodać więcej tekstu w celu wyjaśnienia problemu i sposobu jego rozwiązania .. ponieważ nadal nie mogę wiele z tego zrozumieć.
Chirag Patel
15
Dla tych, którzy są zakłopotani, ViewType musi zwrócić indeks zerowy. np. (0, 1, 2 ...). w moim przypadku próbowałem ponownie użyć R.layout ... dla ViewType. Myślę, że wewnętrznie wartość ViewType jest używana jako indeks puli recyklingu, a nie jako klucz.
Samuel
2
Dziękujemy za wyjaśnienie tego błędu w przeciwieństwie do programistów Androida, którzy jak zwykle nie zawracali sobie głowy wspominaniem tego ważnego titbitu w swojej dokumentacji.
WKS
17

Przyjęta odpowiedź jest poprawna. Oto, co robię, aby uniknąć problemu:

public enum FoodRowType {
    ONLY_ELEM,
    FIRST_ELEM,
    MID_ELEM,
    LAST_ELEM
}

@Override
public int getViewTypeCount() {
    return FoodRowType.values().length;
}

@Override
public int getItemViewType(int position) {
    return rows.get(position).getViewType();  //returns one of the above types
}
mtbomb
źródło
Mogłoby być miło tylko, gdyby getItemViewTypenie wracać do domu
2015
0

Problem pojawia się, gdy wartość getItemType jest nieprawidłowa. Ta wartość powinna być liczbą całkowitą i powinna wynosić od 0, aby uzyskać getViewTypeCount () - 1.

sunil jain
źródło
-3

Powód jest najprawdopodobniej dlatego, że metoda getItemViewType zwraca nieprawidłowe wartości! Każdy wiersz w widoku listy jest jednym widokiem. Podczas scroolowania getItemViewType osiąga więcej niż liczba wyświetleń.

Co robić? Jak uniknąć problemu?

Najpierw określ widok (wiersz) w widoku listy, który jest wyświetlany jako pierwszy. Podczas przewijania użyj równania modulatora

    @Override
    public int getItemViewType(int position) {
        return choseType(position);//function to use modular equation
    }
    @Override
    public int getViewTypeCount() {
        return 10;
    }

W tym przykładzie jest dziesięć widoków (10 rzędów).

private  int choseType(int position){
    if(position%10==0)
        return 0;
    else if(position%10==1)
        return 1;
    else if(position%10==2)
        return 2;
    else if(position%10==3)
        return 3;
    else if(position%10==4)
        return 4;
    else if(position%10==5)
        return 5;
    else if(position%10==6)
        return 6;
    else if(position%10==7)
        return 7;
    else if(position%10==8)
        return 8;
    else
        return 9;


}

Ważny

Niektórzy użytkownicy wspominali o innym pytaniu dotyczącym przepływu stosu tej metody

naprawiono publiczne int getViewTypeCount () i publiczne int getItemViewType (pozycja int), takie jak Tooglebutton automatycznie włącza sprawdzanie stanu true przy scroolowaniu .. to jest duże złe. Jeśli nie chcesz automatycznego enbale na scrool, po prostu zrób

toogleButton.setChecked(false);

w metodzie przesłaniania getView.

Beyaz
źródło