I'm using a custom ListAdapter for a ListView. I don't change anything dynamically (view count, view types, enabled/disabled, I read other posts but they changed something from code). I have to do the following to trigger the exception. Scroll down to the bottom of the list and after that scroll up again. At the start of the scroll-up animation I get this exception.
04-14 09:41:43.907: ERROR/AndroidRuntime(400): FATAL EXCEPTION: main 04-14 09:41:43.907: ERROR/AndroidRuntime(400): java.lang.ArrayIndexOutOfBoundsException 04-14 09:41:43.907: ERROR/AndroidRuntime(400): at android.widget.AbsListView$RecycleBin.addScrapView(AbsListView.java:4540) 04-14 09:41:43.907: ERROR/AndroidRuntime(400): at android.widget.AbsListView.trackMotionScroll(AbsListView.java:3370) 04-14 09:41:43.907: ERROR/AndroidRuntime(400): at android.widget.AbsListView.onTouchEvent(AbsListView.java:2233) 04-14 09:41:43.907: ERROR/AndroidRuntime(400): at android.widget.ListView.onTouchEvent(ListView.java:3446) 04-14 09:41:43.907: ERROR/AndroidRuntime(400): at android.view.View.dispatchTouchEvent(View.java:3885) 04-14 09:41:43.907: ERROR/AndroidRuntime(400): at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:903) 04-14 09:41:43.907: ERROR/AndroidRuntime(400): at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:942) 04-14 09:41:43.907: ERROR/AndroidRuntime(400): at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:942) 04-14 09:41:43.907: ERROR/AndroidRuntime(400): at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:942) 04-14 09:41:43.907: ERROR/AndroidRuntime(400): at com.android.internal.policy.impl.PhoneWindow$DecorView.superDispatchTouchEvent(PhoneWindow.java:1691) 04-14 09:41:43.907: ERROR/AndroidRuntime(400): at com.android.internal.policy.impl.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1125) 04-14 09:41:43.907: ERROR/AndroidRuntime(400): at android.app.Activity.dispatchTouchEvent(Activity.java:2096) 04-14 09:41:43.907: ERROR/AndroidRuntime(400): at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchTouchEvent(PhoneWindow.java:1675) 04-14 09:41:43.907: ERROR/AndroidRuntime(400): at android.view.ViewRoot.deliverPointerEvent(ViewRoot.java:2194) 04-14 09:41:43.907: ERROR/AndroidRuntime(400): at android.view.ViewRoot.handleMessage(ViewRoot.java:1878) 04-14 09:41:43.907: ERROR/AndroidRuntime(400): at android.os.Handler.dispatchMessage(Handler.java:99) 04-14 09:41:43.907: ERROR/AndroidRuntime(400): at android.os.Looper.loop(Looper.java:123) 04-14 09:41:43.907: ERROR/AndroidRuntime(400): at android.app.ActivityThread.main(ActivityThread.java:3683) 04-14 09:41:43.907: ERROR/AndroidRuntime(400): at java.lang.reflect.Method.invokeNative(Native Method) 04-14 09:41:43.907: ERROR/AndroidRuntime(400): at java.lang.reflect.Method.invoke(Method.java:507) 04-14 09:41:43.907: ERROR/AndroidRuntime(400): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:839) 04-14 09:41:43.907: ERROR/AndroidRuntime(400): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:597) 04-14 09:41:43.907: ERROR/AndroidRuntime(400): at dalvik.system.NativeStart.main(Native Method) Down below is the complete implementation.
public class CompanyDetailsAdapter implements ListAdapter { private Context context; private LayoutInflater inflater; private static final class ViewType { private static final int HEADER = 1; private static final int TEXT = 2; private static final int ADDRESS = 3; private static final int REVIEWS = 4; private static final int PRODUCTS = 5; } //all the cells in the order they appear private static final class ViewPositions { private static final int CompanyNameHeader = 0; private static final int CompanyName = 1; private static final int ContactHeader = 2; private static final int Phone = 3; private static final int Website = 4; private static final int AddressHeader = 5; private static final int Address = 6; private static final int DescriptionHeader = 7; private static final int Description = 8; private static final int ReviewsHeader = 9; private static final int Reviews = 10; private static final int ProductsHeader = 11; private static final int Products = 12; } //list of cells by types private static final int[] viewTypes = { ViewType.HEADER, //0 ViewType.TEXT, //1 ViewType.HEADER, //2 ViewType.TEXT, //3 ViewType.TEXT, //4 ViewType.HEADER, //5 ViewType.ADDRESS, //6 ViewType.HEADER, //7 ViewType.TEXT, //8 ViewType.HEADER, //9 ViewType.REVIEWS, //10 ViewType.HEADER, //11 ViewType.PRODUCTS //12 }; //headers private static final int[] headerPositions = { ViewPositions.CompanyNameHeader, ViewPositions.ContactHeader, ViewPositions.AddressHeader, ViewPositions.DescriptionHeader, ViewPositions.ReviewsHeader, ViewPositions.ProductsHeader }; //header resources for position where needed private static final int[] headerResources = { R.string.companyName, 0, R.string.contact, 0, 0, R.string.address, 0, R.string.description, 0, R.string.reviews, 0, R.string.products, 0 }; //regular texts private static final int[] textPositions = { ViewPositions.CompanyName, ViewPositions.Phone, ViewPositions.Website, ViewPositions.Description, }; public CompanyDetailsAdapter(Context context) { this.context = context; this.inflater = LayoutInflater.from(this.context); } public int getCount() { return viewTypes.length; } public Object getItem(int position) { return null; } public long getItemId(int position) { return position+1; } public int getItemViewType(int position) { return viewTypes[position]; } public View getView(int position, View convertView, ViewGroup parent) { if (arrayContains(headerPositions, position)) { LinearLayout layout = null; TextView txtHeader = null; if (convertView == null) { layout = (LinearLayout)inflater.inflate(R.layout.header_listview,parent, false); } else { layout = (LinearLayout)convertView; } txtHeader = (TextView)layout.findViewById(R.id.txtHeader); String value = context.getText(headerResources[position]).toString(); txtHeader.setText(value); return layout; } else if (arrayContains(textPositions, position)) { TextView txtText = null; LinearLayout layout = null; Company c = Company.getSelectedCompany(); if (convertView == null) { layout = (LinearLayout)inflater.inflate(R.layout.default_text,parent, false); } else { layout = (LinearLayout)convertView; } txtText = (TextView)layout.findViewById(R.id.txtNormalText); switch (position) { case ViewPositions.CompanyName: txtText.setText(c.getName()); break; case ViewPositions.Phone: txtText.setText(c.getPhone()); break; case ViewPositions.Website: txtText.setText(c.getWebsiteString()); break; case ViewPositions.Description: txtText.setText(c.getDescription()); break; } return layout; } else { View v = null; //LinearLayout layout = null; Company company = Company.getSelectedCompany(); switch (position) { case ViewPositions.Address: if (convertView == null) v = (LinearLayout)inflater.inflate(R.layout.adress, parent,false); else v = (LinearLayout)convertView; TextView txtAddress = (TextView)v.findViewById(R.id.txtAddress); txtAddress.setText(String.format("%s %s %s", company.getCity(), company.getStreet(), company.getPostalCode())); break; case ViewPositions.Products: if (convertView == null) v = (LinearLayout)inflater.inflate(R.layout.products, parent,false); else v = (LinearLayout)convertView; v = (LinearLayout)inflater.inflate(R.layout.products, parent,false); TextView txtNumProducts = (TextView)v.findViewById(R.id.txtNumProducts); txtNumProducts.setText(String.valueOf(company.getNrProducts())); break; case ViewPositions.Reviews: if (convertView == null) v = (LinearLayout)inflater.inflate(R.layout.reviews, parent,false); else v = (LinearLayout)convertView; v = (LinearLayout)inflater.inflate(R.layout.reviews, parent,false); TextView txtNumReviews = (TextView)v.findViewById(R.id.txtNumReviews); txtNumReviews.setText(String.valueOf(company.getNrReviews())); RatingBar rating = (RatingBar)v.findViewById(R.id.ratAvg); rating.setRating(company.getAvgRating()); break; } return v; } } public int getViewTypeCount() { return 5; } public boolean hasStableIds() { return true; } public boolean isEmpty() { return false; } public void registerDataSetObserver(DataSetObserver arg0) { } public void unregisterDataSetObserver(DataSetObserver arg0) { } public boolean areAllItemsEnabled() { return false; } public boolean isEnabled(int position) { if (arrayContains(headerPositions, position)) return false; return true; } private Boolean arrayContains(int[] array, int element) { for (int i=0; i<array.length; i++) { if (array[i] == element) return true; } return false; } } I attached the sources to the Android 2.3.3 library from here. I didn't find out anything else that the stack trace didn't show me. As you all know the ListView recycles views. There is a collection of some kind that holds these recycled views. When a view dissappears it is added there (I think). This happens at at android.widget.AbsListView$RecycleBin.addScrapView(AbsListView.java:4540) Somewhere here it gives me an exception that something is out of bounds. I don't have the slightest clue what. But I don't have the patience to debug another 1000 lines of code so I think I will use a ScrollView for now.
It seems it is this line: (AbsListView.class:4540 mScrapViews[viewType].add(scrap); mScrapViews is of type private ArrayList<View>[] mScrapViews;. It is an array of lists of type View. I can try to watch viewType and scrap variables but it says errors_during_evaluation. Probably too dumb to evaluate.