ViewGroup throwing NullPointerException in dispatchDragEvent

≡放荡痞女 提交于 2019-12-24 00:47:31

问题


I am in the process of coding a drag and drop library for GridViews. I am almost complete...however, I am getting an annoying NullPointerException upon ACTION_DROP every now and then. It points to the ViewGroup source code, lines 1147 and 1153, saying it is getting a null pointer when trying to recycle the drag event.

Some background on my process: I am basically embedding an OnDragListener into a custom adapter for a GridView. I have the user (programmer) implement an OnGetViewListener, when they set the GridView adapter so they can choose what Views to inflate, what images to include, etc. Then, in the DragDropAdapter (which is the GridView's adapter), when getView is called, it calles the designated OnGetViewListener to get the View the user wants. It creates an empty LinearLayout to stand as a container for the drag and drop. The user's View is added to the container, a drag listener is set to the container, the container's tag is set to the corresponding data collection for the GridView, and then the container is the View returned for getView().

The whole process of the drag and dropping GridView items is based off the container and its tags. When two GridView items are swapped (as a user is dragging an item), it basically removes the view from the cell being hovered over and adds it to the cell that the dragged view just left. This provides a visual "swapping" of items.

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

Now, on ACTION_DRAG_ENDED, the NullPointerException is being thrown because for some reason it cannot recycle the drag event, if I'm understanding the source code correctly.

Update: Here is the source where it throws the exception:

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

Any experts out there have any ideas?


回答1:


I have the same exception, because I'm removing a ViewGroup which contains draggable views when I handle case DragEvent.ACTION_DRAG_ENDED.

My solution: delay the execution of removeView method by using post(runnable) method.



来源:https://stackoverflow.com/questions/12714833/viewgroup-throwing-nullpointerexception-in-dispatchdragevent

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!