Android-自定义ListView适配器-多选删除-Indexoutofbounds-为什么? [英] Android - Custom ListView Adapter - Multi Selection remove - Indexoutofbounds - why?

查看:60
本文介绍了Android-自定义ListView适配器-多选删除-Indexoutofbounds-为什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have a custom Listview using a adapter class to extend ArrayAdapter of a Item class. I have the ability to change between choice modes of NONE,Single and Multi. This all works fine. What I am trying to implement now is a method to removes items from the list view (and adapter) with multiple selection when in the multi choice mode. However I get IndexOutOFBounds Exceptions when doing either of the following; 1) remove last item in listview in SINGLE choice mode (Note: anything before last item remove ok) 2) In multi selection choice mode I once again can not remove last item 3) again in multi selection mode I can remove single selected items before last item but 2 or more selections result in index out of bounds errors again.

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

I added debug log to show position being removed and size of getCheckItemPositions() and my for loop counter (e.g. i) and finally the item title of the item being removed. If I comment out the actual listadpter.remove(position) line then the log output seems to indicator all is working fine So I am now suspecting the issue falls into my adapter class getView method. But my brain is exhausted and I am stuck.

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

MainActivity.class - removeItems method called from a button view object;

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;
}

和物品分类-前半部分;

And Item Class - first half;

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;
}

此处显示了我的调试日志跟踪项的删除情况

Here is visual of my debug log tracking item removals

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中的行不会崩溃,并且日志似乎表明其按预期工作.谁能看到导致我的索引超出范围异常的错误?

Again if I comment out the "mMyListViewAdapter.remove(item);" line in MainActivity not crashes and log seems to indicated its working as expected. Can anyone see my error that results in my Index Out Of Bounds Exception?

我也正在使用SDK 4.0.4 API 15.

Also I am using 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)

推荐答案

confirmCheckedPositionsById中的错误(acj答案中的项目#7)导致getItemId在陈旧的位置被调用.但是,它将以正确的位置再次调用以刷新布局.遇到此问题时,我像这样更新了自定义适配器的getItemId

The bug in confirmCheckedPositionsById (bullet #7 in acj's answer) causes getItemId to get called on a stale position. However, it will get called again with the correct position to refresh the layout. When I ran into this problem I updated the custom Adapter's getItemId like so

@Override
public long getItemId(int position) {
    return position < getCount() ? getItem(position).getId() : -1;
}

这篇关于Android-自定义ListView适配器-多选删除-Indexoutofbounds-为什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆