2

我正在为 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            }

有没有高手有什么想法?

4

1 回答 1

3

我有同样的例外,因为我在ViewGroup处理 case 时删除了包含可拖动视图的 a DragEvent.ACTION_DRAG_ENDED

removeView我的解决方案:通过使用method来延迟方法的执行post(runnable)

于 2015-01-08T04:05:24.350 回答