gpt4 book ai didi

Android - 自定义 ListView 适配器 - 多选删除 - Indexoutofbounds - 为什么?

转载 作者:塔克拉玛干 更新时间:2023-11-02 20:48:04 26 4
gpt4 key购买 nike

我有一个自定义 Listview,它使用适配器类来扩展 Item 类的 ArrayAdapter。我可以在 NONE、Single 和 Multi 的选择模式之间切换。这一切都很好。我现在要实现的是一种在多选模式下通过多选从 ListView (和适配器)中删除项目的方法。但是,在执行以下任一操作时,我得到 IndexOutOFBounds 异常;1)在单选模式下删除 ListView 中的最后一项(注意:最后一项删除之前的任何内容都可以)2)在多选选择模式下,我再次无法删除最后一项3) 再次在多选模式下,我可以删除最后一个项目之前的单个选定项目,但 2 个或更多选择会再次导致索引越界错误。

我添加了调试日志以显示被移除的位置和 getCheckItemPositions() 的大小以及我的 for 循环计数器(例如 i),最后是被移除项目的项目标题。如果我注释掉实际的 listadpter.remove(position) 行,那么日志输出似乎表明一切正常所以我现在怀疑问题出在我的适配器类 getView 方法中。但是我的大脑已经筋疲力尽,我被困住了。

MainActivity.class - 从按钮 View 对象调用的 removeItems 方法;

private void removeItems() {
final SparseBooleanArray checkedItems = listView.getCheckedItemPositions();
//final long[] checkedItemIds = listView.getCheckedItemIds();
final int checkedItemsCount = checkedItems.size();

Log.d("drp", "Adapter Count is: " + Integer.toString(mMyListViewAdapter.getCount()));
if (checkedItems != null) {
for (int i = checkedItemsCount-1; i >= 0 ; --i) {
// This tells us the item position we are looking at
// --
final int position = checkedItems.keyAt(i);
// This tells us the item status at the above position
// --
final boolean isChecked = checkedItems.valueAt(i);

if (isChecked) {
Item item = mMyListViewAdapter.getItem(position);
Log.d("drp", "removing : " + Integer.toString(position) + " of " +Integer.toString(checkedItemsCount) + "-" + Integer.toString(i) + " - Title: " + mMyListViewAdapter.getItem(position).getTitle());
mMyListViewAdapter.remove(item);

}
}
}
}

适配器类;

public class MyListViewAdapter extends ArrayAdapter<Item>  implements OnItemClickListener{

private LayoutInflater mInflator;

/**
* This is my view holder for getView method so don't need to call
* findViewById all the time which results in speed increase
*/
static class ViewHolder {

public TextView txtTitle;
public TextView txtDescription;
public TextView txtSessionCount;
public ImageView listThumbnailImage;
public ImageView listStatusIndicatorImage;
public InertCheckBox Checkbox;
}

/**
* Constructor from a list of items
*/
public MyListViewAdapter(Context context, List<Item> items) {
super(context, 0, items);
mInflator = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {
// This is how you would determine if this particular item is checked
// when the view gets created
// --
final ListView lv = (ListView) parent;
final boolean isChecked = lv.isItemChecked(position);
final int selectionMode = lv.getChoiceMode();

// The item we want to get the view for
// --
Item item = getItem(position);

// Re-use the view if possible (recycle)
// --
ViewHolder holder = null;
if (convertView == null) {
convertView = mInflator.inflate(R.layout.listview_row, null);
holder = new ViewHolder();
holder.txtTitle = (TextView) convertView.findViewById(R.id.title);
holder.txtDescription = (TextView) convertView.findViewById(R.id.description);
holder.txtSessionCount = (TextView) convertView.findViewById(R.id.session_count);
holder.listThumbnailImage = (ImageView) convertView.findViewById(R.id.list_image);
holder.listStatusIndicatorImage = (ImageView) convertView.findViewById(R.id.status);
holder.Checkbox = (InertCheckBox) convertView.findViewById(R.id.inertCheckBox);
convertView.setTag(holder);
} else {
holder = (ViewHolder)convertView.getTag();
}
holder.txtTitle.setText(item.getTitle());
holder.txtDescription.setText(item.getDescription());
holder.txtSessionCount.setText(item.getSessionCount());
holder.listThumbnailImage.setImageBitmap((Bitmap) item.getThumbnailImage());
switch (selectionMode) {
case ListView.CHOICE_MODE_NONE:
holder.Checkbox.setVisibility(InertCheckBox.GONE);
holder.listStatusIndicatorImage.setVisibility(ImageView.VISIBLE);
holder.listStatusIndicatorImage.setImageBitmap((Bitmap) item.getListIndicatorImage());
break;
//case ListView.CHOICE_MODE_SINGLE: case ListView.CHOICE_MODE_MULTIPLE:
default:
holder.listStatusIndicatorImage.setVisibility(ImageView.GONE);
holder.Checkbox.setVisibility(InertCheckBox.VISIBLE);
holder.Checkbox.setButtonDrawable(R.drawable.checkbox);
holder.Checkbox.setChecked(isChecked);
break;
}


return convertView;
}

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

@Override
public boolean hasStableIds() {
return true;
}

和 Item Class - 上半部分;

public class Item implements Comparable<Item> {

private long id;
private String title;
private String description;
private String session_count;
private Bitmap listImage;
private Bitmap statusImage;

public Item(long id, String title, String description, String session_count, Bitmap listImage, Bitmap statusImage) {
super();
this.id = id;
this.title = title;
this.description = description;
this.session_count = session_count;
this.listImage = listImage;
this.statusImage = statusImage;
}

public long getId() {
return id;
}

public void setId(long id) {
this.id = id;
}

public String getTitle() {
return title;
}

这是我的调试日志跟踪项目删除的视觉效果

07-23 22:59:14.910: D/drp(19104): Adapter Count is: 51
07-23 22:59:14.910: D/drp(19104): removing : 50 of 4-3 - Title: Test 50 - testing
07-23 22:59:14.910: D/drp(19104): removing : 49 of 4-2 - Title: Test 49 - testing
07-23 22:59:14.910: D/drp(19104): removing : 48 of 4-1 - Title: Test 48 - testing

如果我再次注释掉“mMyListViewAdapter.remove(item);” MainActivity 中的行没有崩溃,日志似乎表明它按预期工作。任何人都可以看到导致我的索引越界异常的错误吗?

此外,我正在使用 SDK 4.0.4 API 15。

非常感谢,

保罗。

加法——完整的日志输出

        07-25 00:21:53.235: D/AbsListView(25952): Get MotionRecognitionManager
07-25 00:21:53.270: D/dalvikvm(25952): GC_CONCURRENT freed 89K, 3% free 13027K/13383K, paused 1ms+2ms
07-25 00:21:53.430: D/dalvikvm(25952): GC_CONCURRENT freed 207K, 4% free 13232K/13703K, paused 3ms+2ms
07-25 00:21:53.630: D/CLIPBOARD(25952): Hide Clipboard dialog at Starting input: finished by someone else... !
07-25 00:21:54.930: D/dalvikvm(25952): GC_FOR_ALLOC freed 189K, 4% free 13331K/13767K, paused 10ms
07-25 00:21:54.930: I/dalvikvm-heap(25952): Grow heap (frag case) to 13.610MB for 408976-byte allocation
07-25 00:21:54.940: D/dalvikvm(25952): GC_FOR_ALLOC freed 6K, 4% free 13724K/14215K, paused 9ms
07-25 00:21:54.950: D/dalvikvm(25952): GC_FOR_ALLOC freed 0K, 4% free 13724K/14215K, paused 9ms
07-25 00:21:54.950: I/dalvikvm-heap(25952): Grow heap (frag case) to 13.994MB for 408976-byte allocation
07-25 00:21:54.960: D/dalvikvm(25952): GC_FOR_ALLOC freed 0K, 4% free 14124K/14663K, paused 9ms
07-25 00:21:54.970: D/dalvikvm(25952): GC_FOR_ALLOC freed 0K, 4% free 14124K/14663K, paused 9ms
07-25 00:21:54.975: I/dalvikvm-heap(25952): Grow heap (frag case) to 14.384MB for 408976-byte allocation
07-25 00:21:54.995: D/dalvikvm(25952): GC_CONCURRENT freed 0K, 4% free 14523K/15111K, paused 1ms+1ms
07-25 00:21:55.005: D/dalvikvm(25952): GC_FOR_ALLOC freed 0K, 4% free 14523K/15111K, paused 9ms
07-25 00:21:55.005: I/dalvikvm-heap(25952): Grow heap (frag case) to 14.774MB for 408976-byte allocation
07-25 00:21:55.020: D/dalvikvm(25952): GC_FOR_ALLOC freed 0K, 5% free 14923K/15559K, paused 9ms
07-25 00:21:55.030: D/dalvikvm(25952): GC_FOR_ALLOC freed <1K, 5% free 14923K/15559K, paused 9ms
07-25 00:21:55.030: I/dalvikvm-heap(25952): Grow heap (frag case) to 15.165MB for 408976-byte allocation
07-25 00:21:55.040: D/dalvikvm(25952): GC_FOR_ALLOC freed 0K, 5% free 15322K/16007K, paused 10ms
07-25 00:21:55.055: D/dalvikvm(25952): GC_FOR_ALLOC freed 0K, 5% free 15722K/16455K, paused 9ms
07-25 00:21:55.110: D/dalvikvm(25952): GC_FOR_ALLOC freed 157K, 5% free 16145K/16903K, paused 9ms
07-25 00:21:56.565: E/SKIA(25952): FimgApiStretch:stretch failed
07-25 00:21:56.690: E/SKIA(25952): FimgApiStretch:stretch failed
07-25 00:21:56.710: E/SKIA(25952): FimgApiStretch:stretch failed
07-25 00:22:00.545: D/drp(25952): Adapter Count is: 51
07-25 00:22:00.545: D/drp(25952): removing : 49 of 2-2 - Title: Test 49 - testing
07-25 00:22:00.545: D/drp(25952): removing : 48 of 2-1 - Title: Test 48 - testing
07-25 00:22:00.545: D/drp(25952): removing : 47 of 2-0 - Title: Test 47 - testing
07-25 00:22:00.550: D/AndroidRuntime(25952): Shutting down VM
07-25 00:22:00.550: W/dalvikvm(25952): threadid=1: thread exiting with uncaught exception (group=0x40c6f1f8)
07-25 00:22:00.560: E/AndroidRuntime(25952): FATAL EXCEPTION: main
07-25 00:22:00.560: E/AndroidRuntime(25952): java.lang.IndexOutOfBoundsException: Invalid index 48, size is 48
07-25 00:22:00.560: E/AndroidRuntime(25952): at java.util.ArrayList.throwIndexOutOfBoundsException(ArrayList.java:251)
07-25 00:22:00.560: E/AndroidRuntime(25952): at java.util.ArrayList.get(ArrayList.java:304)
07-25 00:22:00.560: E/AndroidRuntime(25952): at android.widget.ArrayAdapter.getItem(ArrayAdapter.java:337)
07-25 00:22:00.560: E/AndroidRuntime(25952): at au.drp.mylistview.MyListViewAdapter.getItemId(MyListViewAdapter.java:107)
07-25 00:22:00.560: E/AndroidRuntime(25952): at android.widget.AbsListView.confirmCheckedPositionsById(AbsListView.java:5956)
07-25 00:22:00.560: E/AndroidRuntime(25952): at android.widget.AbsListView.handleDataChanged(AbsListView.java:5999)
07-25 00:22:00.560: E/AndroidRuntime(25952): at android.widget.ListView.layoutChildren(ListView.java:1535)
07-25 00:22:00.560: E/AndroidRuntime(25952): at android.widget.AbsListView.onLayout(AbsListView.java:2254)
07-25 00:22:00.560: E/AndroidRuntime(25952): at android.view.View.layout(View.java:11467)
07-25 00:22:00.560: E/AndroidRuntime(25952): at android.view.ViewGroup.layout(ViewGroup.java:4237)
07-25 00:22:00.560: E/AndroidRuntime(25952): at android.widget.RelativeLayout.onLayout(RelativeLayout.java:925)
07-25 00:22:00.560: E/AndroidRuntime(25952): at android.view.View.layout(View.java:11467)
07-25 00:22:00.560: E/AndroidRuntime(25952): at android.view.ViewGroup.layout(ViewGroup.java:4237)
07-25 00:22:00.560: E/AndroidRuntime(25952): at android.widget.FrameLayout.onLayout(FrameLayout.java:431)
07-25 00:22:00.560: E/AndroidRuntime(25952): at android.view.View.layout(View.java:11467)
07-25 00:22:00.560: E/AndroidRuntime(25952): at android.view.ViewGroup.layout(ViewGroup.java:4237)
07-25 00:22:00.560: E/AndroidRuntime(25952): at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1644)
07-25 00:22:00.560: E/AndroidRuntime(25952): at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1502)
07-25 00:22:00.560: E/AndroidRuntime(25952): at android.widget.LinearLayout.onLayout(LinearLayout.java:1415)
07-25 00:22:00.560: E/AndroidRuntime(25952): at android.view.View.layout(View.java:11467)
07-25 00:22:00.560: E/AndroidRuntime(25952): at android.view.ViewGroup.layout(ViewGroup.java:4237)
07-25 00:22:00.560: E/AndroidRuntime(25952): at android.widget.FrameLayout.onLayout(FrameLayout.java:431)
07-25 00:22:00.560: E/AndroidRuntime(25952): at android.view.View.layout(View.java:11467)
07-25 00:22:00.560: E/AndroidRuntime(25952): at android.view.ViewGroup.layout(ViewGroup.java:4237)
07-25 00:22:00.560: E/AndroidRuntime(25952): at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1721)
07-25 00:22:00.560: E/AndroidRuntime(25952): at android.view.ViewRootImpl.handleMessage(ViewRootImpl.java:2678)
07-25 00:22:00.560: E/AndroidRuntime(25952): at android.os.Handler.dispatchMessage(Handler.java:99)
07-25 00:22:00.560: E/AndroidRuntime(25952): at android.os.Looper.loop(Looper.java:137)
07-25 00:22:00.560: E/AndroidRuntime(25952): at android.app.ActivityThread.main(ActivityThread.java:4514)
07-25 00:22:00.560: E/AndroidRuntime(25952): at java.lang.reflect.Method.invokeNative(Native Method)
07-25 00:22:00.560: E/AndroidRuntime(25952): at java.lang.reflect.Method.invoke(Method.java:511)
07-25 00:22:00.560: E/AndroidRuntime(25952): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:993)
07-25 00:22:00.560: E/AndroidRuntime(25952): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:760)

最佳答案

AbsListView 中似乎存在错误导致这个问题。 AbsListView 的任何子类都可能发生这种情况,包括 ListViewGridView

在单选和多选模式下,ListView 通过验证 confirmCheckedPositionsById 中的选中项集来响应对其适配器的 notifyDataSetChanged() 调用()。由于此时已从数据集中删除了所选项目,因此适配器将抛出异常。值得注意的是,仅当适配器的 hasStableIds() 方法返回 true 时才会出现此问题。

笼统地说,这是相关路径:

  1. 您在 ListView 中选择一个或多个项目
  2. ListView 更新其选定项目的列表
  3. 您点击删除按钮
  4. 项目已从您的数据集中删除
  5. 您在适配器上调用 notifyDataSetChanged(),它会通知其观察者数据集已更改。 ListView 是这些观察者之一。
  6. 下次重绘 ListView 时,它会看到适配器的通知并调用 handleDataChanged()。此时,ListView 仍然认为我们现在删除的项目被选中并且在数据集中
  7. handleDataChanged() 方法调用 confirmCheckedPositionsById(),后者会尝试使用陈旧位置在适配器上调用 getItemId() .如果被删除的项目恰好在列表的末尾附近,这个位置很可能超出数组范围,适配器将抛出 IndexOutOfBoundsException

两种可能的解决方法如下:

  • 如其他答案中所述,每次数据集更改时创建一个新适配器。除非手动保存和恢复,否则这会导致丢失当前滚动位置的不幸影响。

  • 在调用 notifyDataSetChanged 之前,通过在 ListView(或 GridView)上调用 clearChoices() 来清除所选项目() 在适配器上。所选项目无论如何都会被删除,因此丢失当前选择状态不太可能成为问题。此方法将保留滚动位置,并应防止在更新列表时出现闪烁。

关于Android - 自定义 ListView 适配器 - 多选删除 - Indexoutofbounds - 为什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11613517/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com