2017-09-05 160 views
0

有沒有什麼辦法可以攔截/裝飾視圖的觸摸事件,而不需要擴展View或包裝在一些ViewGroup(它可以攔截子事件)?攔截點擊/觸摸事件,無需重寫ViewGroup或查看方法

假設我有ExpandableListView它處理項目點擊事件。如果我在適配器返回的充氣項目視圖上設置了OnClickListenerOnTouchListener,則ExpandableListView不會執行相應的操作 - 由於事件被項目偵聽器使用而進行組擴展。

我不想使用ExpandableListView#setOnItemClickListener的原因是,我想在適配器中修飾點擊事件而不使用ExpandableListView依賴關係。

回答

0

我發現這個問題的工作解決方案。

解決方案:收集OnTouchListener中的事件克隆,然後將它們分派到父視圖。

private final Queue<MotionEntry> consumedEvents = new LinkedList<>(); 
private final AtomicBoolean isDispatching = new AtomicBoolean(false); 
... 
    groupView.setOnTouchListener(new OnTouchListener() { 
     @Override 
     public boolean onTouch(View v, MotionEvent e) { 
      // we don't want to handle re-dispatched event... 
      if (isDispatching.get()) { 
       return false; 
      } 
      // create clone as event might be changed by parent 
      MotionEvent clone = MotionEvent.obtain(e); 
      MotionEntry entry = new MotionEntry(v, clone); 
      consumedEvents.add(entry); 

      // consume ACTION_DOWN in order to receive subsequent motion events 
      // like ACTION_MOVE, ACTION_CANCEL/ACTION_UP... 
      if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { 
       return true; 
      } 
      // we do not want to handle canceled motion... 
      if (event.getActionMasked() == MotionEvent.ACTION_CANCEL) { 
       consumedEvents.clear(); 
       return false; 
      } 
      // at this moment we have intercepted whole motion 
      // = re-dispatch to parent in order to apply default handling... 
      if (event.getActionMasked() == MotionEvent.ACTION_UP) { 
       dispatchEvents(); 
      } 
      return true; 
     } 
    }); 
... 

調度方法:

private void dispatchEvents() { 
    isDispatching.set(true); 
    while (!consumedEvents.isEmpty()) { 
     MotionEntry entry = consumedEvents.poll(); 

     ViewGroup parent = (ViewGroup) entry.view.getParent(); 
     if (parent == null || entry.view.getVisibility() != View.VISIBLE) { 
      continue; // skip dispatching to detached/invisible view 
     } 
     // make position relative to parent... 
     entry.event.offsetLocation(entry.view.getLeft(), entry.view.getTop()); 
     entry.event.setSource(PARENT_DISPATCHER); 
     parent.dispatchTouchEvent(entry.event); 

     if (event.getActionMasked() == MotionEvent.ACTION_UP) { 
      clickListener.onClick(entry.view); 
     } 
    } 
    isDispatching.set(false); 
} 

Helper類

private class MotionEntry { 
    private final View view; 
    private final MotionEvent event; 

    public MotionEntry(View view, MotionEvent event) { 
     this.view = view; 
     this.event = event; 
    } 
}