2

我正在爲GridViews編寫一個拖放庫的過程。我差不多完成了......但是,我現在每隔一段時間就會在ACTION_DROP中出現一個惱人的NullPointerException異常。它指向ViewGroup源代碼,第1147和1153行,說它試圖回收拖動事件時獲得空指針。ViewGroup在dispatchDragEvent中拋出NullPointerException異常

我的進程的一些背景:我基本上是將OnDragListener嵌入到GridView的自定義適配器中。我有用戶(程序員)實現一個OnGetViewListener,當他們設置GridView適配器,以便他們可以選擇什麼視圖來膨脹,要包含什麼圖像等等。然後,在DragDropAdapter(這是GridView的適配器)中,當調用getView它調用指定的OnGetViewListener來獲取用戶想要的視圖。它創建一個空的LinearLayout作爲拖放的容器。將用戶的視圖添加到容器,將一個拖動監聽器設置爲容器,將容器的標記設置爲GridView的相應數據集合,然後該容器就是爲getView()返回的視圖。

拖放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   } 

任何專家在那裏有什麼想法?

+0

你可以發佈更多的代碼,具體如何定義和設置mContext? – nmw

+0

mContext只是從設置適配器的活動傳遞到適配器的構造函數中。 – dennisdrew

回答

3

我有同樣的例外,因爲我在處理案例DragEvent.ACTION_DRAG_ENDED時刪除了包含可拖動視圖的ViewGroup

我的解決方法:使用post(runnable)方法延遲執行removeView方法。

相關問題