GridView のドラッグ アンド ドロップ ライブラリをコーディング中です。私はほぼ完成しています...しかし、時々ACTION_DROPで迷惑なNullPointerExceptionが発生します。これは ViewGroup ソース コードの 1147 行目と 1153 行目を指しており、ドラッグ イベントをリサイクルしようとしたときに null ポインターを取得していることを示しています。
私のプロセスの背景: 基本的に、OnDragListener を GridView のカスタム アダプターに埋め込んでいます。ユーザー (プログラマー) に OnGetViewListener を実装してもらい、GridView アダプターを設定して、インフレートするビュー、含める画像などを選択できるようにします。次に、DragDropAdapter (GridView のアダプター) で、getView が呼び出されたときに、指定された OnGetViewListener を呼び出して、ユーザーが必要とするビューを取得します。空の LinearLayout を作成して、ドラッグ アンド ドロップのコンテナーとして使用します。ユーザーのビューがコンテナーに追加され、ドラッグ リスナーがコンテナーに設定され、コンテナーのタグが GridView の対応するデータ コレクションに設定されます。コンテナーは、getView() に対して返されるビューです。
GridView 項目のドラッグ アンド ドロップのプロセス全体は、コンテナーとそのタグに基づいています。2 つの GridView アイテムが交換されると (ユーザーがアイテムをドラッグしているため)、基本的には、ホバーされているセルからビューが削除され、ドラッグされたビューが残ったセルに追加されます。これにより、アイテムの視覚的な「交換」が提供されます。
public class DragDropAdapter extends BaseAdapter {
...
@Override
public View getView(int position, View convertView, ViewGroup parent) {
LinearLayout containerView = new LinearLayout(mContext);
containerView.setLayoutParams(new GridView.LayoutParams(
GridView.LayoutParams.MATCH_PARENT,
GridView.LayoutParams.MATCH_PARENT));
View itemView = mGetViewListener.getView(position, convertView, parent);
itemView.setTag(mItems.get(position));
containerView.setTag(mItems.get(position));
containerView.addView(itemView);
containerView.setOnDragListener(new ItemDragListener());
return containerView;
}
...
View mExitedView = null;
public class ItemDragListener implements OnDragListener {
public ItemDragListener() {
}
private void swapCards(int startPosition, View viewToSwap) {
if (mExitedView == null) {
mExitedView = mGridView.getChildAt(startPosition);
}
ViewGroup viewToSwapContainer = (ViewGroup) viewToSwap;
ViewGroup exitedViewContainer = (ViewGroup) mExitedView;
View childViewToSwap = viewToSwapContainer.getChildAt(0);
View childViewExited = exitedViewContainer.getChildAt(0);
int enteredPosition = ItemCoordinatesHelper
.getGridPosition(viewToSwap);
int exitedPosition = ItemCoordinatesHelper
.getGridPosition(mExitedView);
Object itemToSwap = viewToSwap.getTag();
Object exitedItem = mExitedView.getTag();
viewToSwapContainer.setVisibility(View.INVISIBLE);
viewToSwapContainer.setTag(exitedItem);
viewToSwapContainer.removeAllViews();
if (childViewExited.getParent() != null)
((ViewGroup) childViewExited.getParent()).removeAllViews();
viewToSwapContainer.addView(childViewExited);
exitedViewContainer.setTag(itemToSwap);
exitedViewContainer.removeAllViews();
if (childViewToSwap.getParent() != null)
((ViewGroup) childViewToSwap.getParent()).removeAllViews();
exitedViewContainer.addView(childViewToSwap);
exitedViewContainer.setVisibility(View.VISIBLE);
}
...
@Override
public boolean onDrag(View v, DragEvent event) {
// TODO Auto-generated method stub
View heldView = (View) event.getLocalState();
int startPosition = ItemCoordinatesHelper.getGridPosition(heldView);
switch (event.getAction()) {
case DragEvent.ACTION_DRAG_STARTED:
break;
case DragEvent.ACTION_DRAG_ENTERED:
swapCards(startPosition, v);
break;
case DragEvent.ACTION_DRAG_EXITED:
mExitedView = v;
break;
case DragEvent.ACTION_DROP:
View view = (View) event.getLocalState();
v.setVisibility(View.VISIBLE);
view.setVisibility(View.VISIBLE);
break;
case DragEvent.ACTION_DRAG_ENDED:
commitChangesToAdapter();
mExitedView = null;
break;
default:
break;
}
return true;
}
}
}
現在、ACTION_DRAG_ENDED で NullPointerException がスローされています。これは、ソース コードを正しく理解している場合、何らかの理由でドラッグ イベントをリサイクルできないためです。
更新:例外をスローするソースは次のとおりです。
@Override
1100 public boolean dispatchDragEvent(DragEvent event) {
1101 boolean retval = false;
1102 final float tx = event.mX;
1103 final float ty = event.mY;
1104
1105 ViewRootImpl root = getViewRootImpl();
1106
1107 // Dispatch down the view hierarchy
1108 switch (event.mAction) {
1109 case DragEvent.ACTION_DRAG_STARTED: {
1110 // clear state to recalculate which views we drag over
1111 mCurrentDragView = null;
1112
1113 // Set up our tracking of drag-started notifications
1114 mCurrentDrag = DragEvent.obtain(event);
1115 if (mDragNotifiedChildren == null) {
1116 mDragNotifiedChildren = new HashSet<View>();
1117 } else {
1118 mDragNotifiedChildren.clear();
1119 }
1120
1121 // Now dispatch down to our children, caching the responses
1122 mChildAcceptsDrag = false;
1123 final int count = mChildrenCount;
1124 final View[] children = mChildren;
1125 for (int i = 0; i < count; i++) {
1126 final View child = children[i];
1127 child.mPrivateFlags2 &= ~View.DRAG_MASK;
1128 if (child.getVisibility() == VISIBLE) {
1129 final boolean handled = notifyChildOfDrag(children[i]);
1130 if (handled) {
1131 mChildAcceptsDrag = true;
1132 }
1133 }
1134 }
1135
1136 // Return HANDLED if one of our children can accept the drag
1137 if (mChildAcceptsDrag) {
1138 retval = true;
1139 }
1140 } break;
1141
1142 case DragEvent.ACTION_DRAG_ENDED: {
1143 // Release the bookkeeping now that the drag lifecycle has ended
1144 if (mDragNotifiedChildren != null) {
1145 for (View child : mDragNotifiedChildren) {
1146 // If a child was notified about an ongoing drag, it's told that it's over
1147 child.dispatchDragEvent(event); //<-- NULL POINTER HERE
1148 child.mPrivateFlags2 &= ~View.DRAG_MASK;
1149 child.refreshDrawableState();
1150 }
1151
1152 mDragNotifiedChildren.clear();
1153 mCurrentDrag.recycle(); //<-- NULL POINTER HERE
1154 mCurrentDrag = null;
1155 }
そこにいる専門家は何かアイデアを持っていますか?