アダプタ クラスを使用して Item クラスの ArrayAdapter を拡張するカスタム Listview があります。NONE、Single、Multi の選択モードを切り替えることができます。これはすべてうまくいきます。私が今実装しようとしているのは、複数選択モードのときに複数選択でリスト ビュー (およびアダプター) からアイテムを削除する方法です。ただし、次のいずれかを実行すると IndexOutOFBounds Exceptions が発生します。1) SINGLE 選択モードでリストビューの最後の項目を削除します (注: 最後の項目の前にあるものはすべて削除できます) 2) 複数選択選択モードでもう一度最後の項目を削除できません 3) 再び複数選択モードで前に単一選択項目を削除できます最後の項目を除いて 2 つ以上の選択を行うと、再び範囲外のインデックス エラーが発生します。
デバッグ ログを追加して、削除される位置と getCheckItemPositions() のサイズ、および for ループ カウンター (例: i)、最後に削除されるアイテムのアイテム タイトルを表示しました。実際の listadpter.remove(position) 行をコメントアウトすると、ログ出力はすべて正常に動作していることを示しているように見えるので、問題がアダプタ クラスの getView メソッドにあるのではないかと疑っています。しかし、私の脳は疲れ果てて行き詰まっています。
MainActivity.class - ボタン ビュー オブジェクトから呼び出される 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;
}
そしてアイテムクラス - 前半;
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 の行はクラッシュせず、ログは期待どおりに動作していることを示しているようです。Index Out Of Bounds Exception が発生するエラーを誰かが見ることができますか?
また、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)