滚动时将新项目添加到列表时出现数组索引超出范围异常

Posted

技术标签:

【中文标题】滚动时将新项目添加到列表时出现数组索引超出范围异常【英文标题】:Getting ArrayIndex out of Bounds Exception when adding new items to list while scrolling 【发布时间】:2017-06-01 10:25:58 【问题描述】:

我有一个ListView,其中我通过在滚动列表时从 SQLite 中设置 3 个数据的限制从 SQLite 加载数据我使用 AsyncTask 从数据库中加载另外 3 个数据,但是当新数据是加载它显示一个错误:ArrayIndexOutOfBoundsException

适配器:

public class FarmerAdapter extends BaseAdapter implements Filterable
    Context context;
    ArrayList<Farmer> farmeritems;
    ArrayList<Farmer> mStringFilterList;
    ValueFilter valueFilter;

    public FarmerAdapter(Context context, ArrayList<Farmer> list) 

        this.context = context;
        farmeritems = list;
        mStringFilterList = list;
    


    @Override
    public int getCount() 

        return farmeritems.size();
    

    @Override
    public Object getItem(int position) 

        return farmeritems.get(position);
    

    @Override
    public long getItemId(int position) 

        return position;
    

    @Override
    public int getViewTypeCount() 

        return getCount();
    

    @Override
    public int getItemViewType(int position) 

        return position;
    

    @Override
    public View getView(int position, View convertView, ViewGroup arg2) 
        Farmer farmerdetails = farmeritems.get(position);

        if (convertView == null) 
            LayoutInflater inflater = (LayoutInflater) context
                    .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            convertView = inflater.inflate(R.layout.model_farmer, null);

        


        final TextView farmname = (TextView) convertView.findViewById(R.id.tv_farmer_name);
        final TextView farmmobno = (TextView) convertView.findViewById(R.id.tv_farmer_mobno);
        final TextView farmlocation = (TextView) convertView.findViewById(R.id.tv_farmer_location);
        final LinearLayout farmtrade = (LinearLayout) convertView.findViewById(R.id.li_farmer_trade);
        final LinearLayout farmadvance = (LinearLayout) convertView.findViewById(R.id.li_farmer_advance);
        final ImageView img_trade = (ImageView)convertView.findViewById(R.id.img_farmertrade);
        final ImageView img_advance = (ImageView)convertView.findViewById(R.id.img_farmeradvance);
        farmname.setText(farmerdetails.getFarmername());
        farmmobno.setText(farmerdetails.getFarmermobno());
        farmlocation.setText(farmerdetails.getFarmerlocation());
        farmtrade.setOnClickListener(new View.OnClickListener() 
            @Override
            public void onClick(View view) 
                img_trade.setColorFilter(ContextCompat.getColor(context, R.color.colorAccent));
                img_advance.clearColorFilter();
                Intent b = new Intent(context, Farmer_simpletrade_Activity.class);
                b.putExtra("fname", farmname.getText().toString());
                b.putExtra("fmobno", farmmobno.getText().toString());
                context.startActivity(b);
            
        );
        farmadvance.setOnClickListener(new View.OnClickListener() 
            @Override
            public void onClick(View view) 
                img_advance.setColorFilter(ContextCompat.getColor(context, R.color.colorAccent));
                img_trade.clearColorFilter();
                Intent c = new Intent(context, Farmer_simpleadvance_Activity.class);
                c.putExtra("farmername", farmname.getText().toString());
                c.putExtra("farmermobno", farmmobno.getText().toString());
                context.startActivity(c);
                ((Activity) context).finish();
            
        );
        convertView.setOnClickListener(new View.OnClickListener() 
            @Override
            public void onClick(View view) 
                //todo disable the comments for farmer individual transaction

                Intent a = new Intent(context, FarmerLedgerView_Activity.class);
                a.putExtra("farmername", farmname.getText().toString());
                a.putExtra("farmermobno", farmmobno.getText().toString());
                context.startActivity(a);
                ((Activity) context).finish();

            
        );
        return convertView;

    

    @Override
    public Filter getFilter() 
        if (valueFilter == null) 
            valueFilter = new ValueFilter();
        
        return valueFilter;
    

    private class ValueFilter extends Filter 
        @Override
        protected FilterResults performFiltering(CharSequence constraint) 
            FilterResults results = new FilterResults();

            if (constraint != null && constraint.length() > 0) 
                ArrayList<Farmer> filterList = new ArrayList<Farmer>();
                for (int i = 0; i < mStringFilterList.size(); i++) 
                    if ((mStringFilterList.get(i).getFarmername().toUpperCase())
                            .contains(constraint.toString().toUpperCase())) 

                        Farmer farmer = new Farmer(mStringFilterList.get(i)
                                .getFarmername(), mStringFilterList.get(i)
                                .getFarmermobno(), mStringFilterList.get(i)
                                .getFarmerlocation());

                        filterList.add(farmer);
                    
                
                results.count = filterList.size();
                results.values = filterList;
             else 
                results.count = mStringFilterList.size();
                results.values = mStringFilterList;
            
            return results;

        

        @Override
        protected void publishResults(CharSequence constraint,
                                      FilterResults results) 
            farmeritems = (ArrayList<Farmer>) results.values;
            notifyDataSetChanged();
        

    

    public void setTransactionList(ArrayList<Farmer> newList) 
        farmeritems = newList;
        notifyDataSetChanged();
    

AsyncTask 从后台加载数据:

  private class LoadDataTask extends AsyncTask<Void, Void, Void> 
    @Override
    protected void onPreExecute() 

    

    @Override
    protected Void doInBackground(Void... params) 

        if (isCancelled()) 
            return null;
        

        // Simulates a background task
        try 
            Thread.sleep(1000);
            offSet=offSet+3;
            if (offSet > totalcount) 
               loadingMore=false;
             else 
                Log.e("OffsetNo", String.valueOf(offSet));
                databasehandler = new DatabaseHandler(getApplicationContext());

                farmerlabels = new ArrayList<Farmer>();
                String selectQuery = "SELECT * FROM farmercontactlabel ORDER BY farmername COLLATE NOCASE LIMIT " + offSet + "";
                SQLiteDatabase db = databasehandler.getReadableDatabase();
                Cursor cursor = db.rawQuery(selectQuery, null);
                if (cursor.moveToFirst()) 
                    do 
                        Farmer farmerdetails = new Farmer();
                        farmerdetails.setFarmername(cursor.getString(1));
                        farmerdetails.setFarmermobno(cursor.getString(2));
                        farmerdetails.setFarmerlocation(cursor.getString(3));
                        list.add(farmerdetails);

                     while (cursor.moveToNext());

                
                cursor.close();
                db.close();

            
         catch (InterruptedException e) 
            e.printStackTrace();
        



        return null;

    

    @Override
    protected void onPostExecute(Void result) 

        fadapter.setTransactionList(list);
        list_farmer.onLoadMoreComplete();

       // fadapter.notifyDataSetChanged();

        super.onPostExecute(result);
    

    @Override
    protected void onCancelled() 
        // Notify the loading more operation has finished
        list_farmer.onLoadMoreComplete();
    

数据库结构:

 String CREATE_FARMERS_TABLE = "CREATE TABLE " + FARMERCONTACT_LABELS + "("
            + FARMER_ID + " INTEGER,"
            + FARMER_NAME + " TEXT,"
            + FARMER_MOBNO + " NUMERIC PRIMARY KEY,"
            + FARMER_LOCATION + " TEXT" + ");";
    db.execSQL(CREATE_FARMERS_TABLE);

数据库:

 public ArrayList<Farmer> getAllfarmers(int offset) 
    ArrayList<Farmer> farmerlabels = new ArrayList<Farmer>();
    String selectQuery = "SELECT * FROM " + FARMERCONTACT_LABELS + " ORDER BY farmername COLLATE NOCASE LIMIT " + offset + "";
    SQLiteDatabase db = this.getReadableDatabase();
    Cursor cursor = db.rawQuery(selectQuery, null);
    if (cursor.moveToFirst()) 
        do 
            Farmer farmerdetails = new Farmer();
            farmerdetails.setFarmername(cursor.getString(1));
            farmerdetails.setFarmermobno(cursor.getString(2));
            farmerdetails.setFarmerlocation(cursor.getString(3));
            farmerlabels.add(farmerdetails);
         while (cursor.moveToNext());
    
    cursor.close();
    db.close();
    return farmerlabels;

错误:

java.lang.ArrayIndexOutOfBoundsException: length=3; index=3
at android.widget.AbsListView$RecycleBin.addScrapView(AbsListView.java:7103)
at android.widget.ListView.layoutChildren(ListView.java:1653)
at android.widget.AbsListView.onLayout(AbsListView.java:2230)
at android.view.View.layout(View.java:16001)
at android.view.ViewGroup.layout(ViewGroup.java:5181)
at android.widget.RelativeLayout.onLayout(RelativeLayout.java:1195)
at android.view.View.layout(View.java:16001)

【问题讨论】:

在您的 AsyncTask 中声明了 list 变量的位置,该变量在 list.add(farmerdetails); 中使用另外请指定您在 onLoadMoreComplete() 方法中所做的工作。 请说明您在哪里声明和初始化listoffset 【参考方案1】:

如果您将行数限制为 3,则最后一个索引将为 2。因此,如果您尝试通过索引 3 获取,您将获得 NullPointerException

【讨论】:

看看我编辑的问题,我在选择查询中也包含了表结构使用 cursor.getString(1) 获取农民名称,cursor.getString(2) 获取 mobileno 和 cursor.getString(3) 获取位置 异步任务完成后如何更新偏移值 "因此,如果您尝试通过索引 3 获取,您将得到 NullPointerException。"从编辑的答案中可以看出,表中有 4 个字段。所以getString(3) 不应该导致NullPointerException【参考方案2】:

setTransactionList 方法中,您将现有数据集(List) 替换为新对象,然后调用notifyDataSetChanged()。可能在调用之间发生异常,因此您替换了数据集,但 recyclerview 尝试通过旧对象的索引访问数据。

我建议你添加方法:

public void addAllTransaction(List<Farmer> farmerList) 
  farmeritems.addAll(farmerList);
  notifyDataSetChanged();

并使用addAllTransaction 而不是setTransactionList

另外,最好使用notifyItemInserted 而不是notifyDataSetChanged

另外检查这个问题:ArrayIndexOutOfBoundsException when populating RecyclerView

【讨论】:

【参考方案3】:

如果您在从游标对象中检索数据时遇到错误,那么您应该使用列名而不是传递 1、2 3。 您正在使用硬编码列索引来获取值,而不是您应该使用列名来获取列索引,如下代码:

 Farmer farmerdetails = new Farmer();
            farmerdetails.setFarmername(cursor.getString(cursor.getColumnIndex(FARMER_NAME)));
            farmerdetails.setFarmermobno(cursor.getString(cursor.getColumnIndex(FARMER_MOBNO )));
            farmerdetails.setFarmerlocation(cursor.getString(cursor.getColumnIndex(FARMER_LOCATION )));
            farmerlabels.add(farmerdetails);

这样,如果指定的列名在表中可用,您永远不会得到错误的列索引。

【讨论】:

【参考方案4】:

可能farmeritems 列表正在 FarmerAdapter 类之外进行操作。防止这种情况的一种方法是创建一个新的 ArrayList 而不是保存它的引用。

您必须将构造函数中的代码更改为:

farmeritems = new ArrayList&lt;Farmer&gt;(list);

将方法publishResults中的代码更改为:

farmeritems = new ArrayList&lt;Farmer&gt;((ArrayList&lt;Farmer&gt;) results.values);

最后将方法setTransactionList中的代码更改为:

farmeritems = new ArrayList&lt;Farmer&gt;(newList);

希望它能解决你的问题。

【讨论】:

【参考方案5】:

看看你的代码

public void setTransactionList(ArrayList<Farmer> newList) 
    farmeritems = newList;
    notifyDataSetChanged();

你在做什么:

在您的 setTransactionList 中,您正在重新引用您的 farmeritemsi.e。调用farmeritems = newList 后,您的farmeritems 的引用为newList,它只有3 个项目。

你应该做什么:

public void setTransactionList(ArrayList<Farmer> newList) 
    farmeritems.addAll(newList);
    notifyDataSetChanged();

这将在您的farmeritems 中添加newList(3 项)。现在您的farmeritems 已更新。

【讨论】:

【参考方案6】:

我对 ListView 和 RecyclerView 都使用 Adapter 的最佳实践, 我将数据分为两种类型。一个临时使用,另一个最终使用。

示例:

List<Product> productsFinal = new ArrayList<>();
List<Product> productsTemp = new ArrayList<>();

当我想更改或修改数据时,我使用productsTemp

productsTemp.add(new Product());productsTemp.remove(0);

当我完成数据修改后,是时候将数据分配给适配器使用的productsFinal。然后我可以清除productsTemp中的数据以节省内存。

ProductAdapter adapter = new ProductAdapter(productsFinal);
productsView.setAdapter(adapter);
...
productsFinal.addAll(productsTemp);
productsTemp.clear();
adapter.notifyDataSetChanged();

希望这个简单的解决方案将有助于解决用户滚动列表时更新新数据的问题。

【讨论】:

【参考方案7】:

试试这个:

if (cursor.moveToFirst()) 
    do 
        Farmer farmerdetails = new Farmer();
        farmerdetails.setFarmername(cursor.getString(0));
        farmerdetails.setFarmermobno(cursor.getString(1));
        farmerdetails.setFarmerlocation(cursor.getString(2));
        farmerlabels.add(farmerdetails);
     while (cursor.moveToNext());

您的数组限制为 3,这意味着如果您尝试获取 item[3] 将抛出 ArrayIndexOutOfBoundsException,因为它实际上是第 4 项。项目由 n-1 (0,1,2) 的系统计数 3 个项目。

【讨论】:

以上是关于滚动时将新项目添加到列表时出现数组索引超出范围异常的主要内容,如果未能解决你的问题,请参考以下文章

从数组中删除重复项时出现超出范围异常

将类添加到列表 C# 时,索引超出了数组的范围

为啥在遍历列表矩阵时出现列表索引超出范围错误?

在 ASP.NET Core 中呈现 RDLC 报告时出现索引超出范围异常

为啥在使用 readlines() 读取文件时出现列表索引超出范围错误?

如何重新创建“索引超出数组范围”将项目添加到字典?