我正在为 GridViews 编写拖放库。我几乎完成了......但是,我时不时地在 ACTION_DROP 上得到一个恼人的 NullPointerException。它指向 ViewGroup 源代码,第 1147 和 1153 行,表示它在尝试回收拖动事件时得到一个空指针。
我的过程的一些背景知识:我基本上是将 OnDragListener 嵌入到 GridView 的自定义适配器中。我让用户(程序员)实现一个 OnGetViewListener,当他们设置 GridView 适配器时,他们可以选择要膨胀的视图、要包含的图像等。然后,在 DragDropAdapter(这是 GridView 的适配器)中,当调用 getView ,它调用指定的 OnGetViewListener 来获取用户想要的 View。它创建一个空的 LinearLayout 作为拖放的容器。将用户的View添加到容器中,给容器设置一个拖拽监听器,将容器的标签设置为GridView对应的数据集合,然后容器就是getView()返回的View。
拖放 GridView 项目的整个过程是基于容器及其标签的。当交换两个 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 }
有没有高手有什么想法?