使用 ListAdapter 时出现 ArrayIndexOutOfBoundsException

Posted

技术标签:

【中文标题】使用 ListAdapter 时出现 ArrayIndexOutOfBoundsException【英文标题】:ArrayIndexOutOfBoundsException when using ListAdapter 【发布时间】:2011-08-05 07:10:51 【问题描述】:

我将自定义ListAdapter 用于ListView。我没有动态更改任何内容(查看次数、查看类型、启用/禁用,我阅读了其他帖子,但他们从代码中更改了某些内容)。我必须执行以下操作才能触发异常。向下滚动到列表底部,然后再次向上滚动。在向上滚动动画开始时,我得到了这个异常。

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)

下面是完整的实现。

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;
  




我将来自here 的源附加到Android 2.3.3 库。我没有发现堆栈跟踪没有向我显示的任何其他内容。众所周知,ListView 回收视图。有一个集合包含这些回收的视图。当一个视图消失时,它被添加到那里(我认为)。这发生在at android.widget.AbsListView$RecycleBin.addScrapView(AbsListView.java:4540) 这里的某个地方它给了我一个例外,即某些东西超出了范围。我一点头绪都没有。但是我没有耐心调试另外 1000 行代码,所以我想我现在会使用 ScrollView。


似乎是这一行:(AbsListView.class:4540 mScrapViews[viewType].add(scrap); mScrapViews 的类型为 private ArrayList&lt;View&gt;[] mScrapViews;。它是 View 类型的列表数组。我可以尝试观看viewTypescrap 变量,但它显示errors_during_evaluation。可能太笨了,无法评估。

【问题讨论】:

能否贴出运行失败的部分代码? 当然我可以发布所有我不知道有什么问题的代码 尝试逐步调试您的代码。这是一个很好的起点:android.widget.ListView.onTouchEvent(ListView.java:3446) 【参考方案1】:

发现问题。视图类型必须从 0 开始,我的从 1 开始。

Android 正在使用我提供的视图类型在某处按索引访问数组。 SomeArraySomewhere[myViewType] 这个数组的大小可能等于视图类型的数量。所以这意味着我必须从 0 开始我的视图类型。

【讨论】:

谢谢!我也遇到了同样的问题,找不到原因 +1 表示解决方案。但是您到底在哪里找到了这个 SomeArraySomewhere[] ?在适配器的源代码中? 抱歉这是很久以前的事了,我不记得了【参考方案2】:

改变这个:

public int getViewTypeCount()

    return 2;

这对我有帮助:

public int getViewTypeCount()

    return 3;

说明:

最初我有两种视图类型,但后来在实现中,我添加了第三种。 我开始收到该异常,因为我忘记更新方法 getViewTypeCount() 以返回 3 而不是仅返回 2。

希望这可以帮助某人:)

【讨论】:

【参考方案3】:

position 可能是:

答:null b:higher then the amount of headerResources?

在这一行(getView):

String value = context.getText(headerResources[position]).toString();

先学会调试:这里是nice video tutorial

【讨论】:

【参考方案4】:

原因,很可能是因为 getItemViewType 方法返回了错误的值!列表视图中的每一行都是一个视图。浏览时 getItemViewType 达到的数量超过了 View 的数量。

怎么办?如何避免问题?

首先确定列表视图中首先显示的视图(行)。滚动时使用模块方程

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

@Override
public int getViewTypeCount() 
    return 10;

在这个例子中,有十个视图(10 行)。

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;



重要

一些用户在另一个关于***的问题上提到了该方法

public int getViewTypeCount() 和 public int getItemViewType(int position) 修复像 Tooglebutton 一样自动启用状态检查 true on scrooling..这是大错误。如果你不想在 scrool 上自动启用就做

toogleButton.setChecked(false);

关于 getView 覆盖方法。

【讨论】:

以上是关于使用 ListAdapter 时出现 ArrayIndexOutOfBoundsException的主要内容,如果未能解决你的问题,请参考以下文章

使用 ListAdapter 将项目添加到 ListView

如何使用 getFilter() 和 ListAdapter 过滤 ListView?

在没有唯一标识符的项目上使用“ListAdapter”areItemsTheSame

如何使用 Kotlin 在 ListAdapter 中使用 Filterable?

使用 ListAdapter 更新 recyclerview 中的一行

在 Recyclerview 中使用 getItemCount 和 ListAdapter