2017-09-05 3 views
0

を拡張したり、子イベントを傍受することができるViewGroupをラップすることなく、ビューのタッチイベントをインターセプト/デコレートする方法はありますか?ViewGroupまたはViewメソッドをオーバーライドしないでクリック/タッチイベントを傍受する

アイテムのクリックイベントを処理するExpandableListViewがあるとします。アダプタによって返された膨らまされたアイテムビューのOnClickListenerまたはOnTouchListenerを設定した場合、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); 
} 

ヘルパークラス

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

    public MotionEntry(View view, MotionEvent event) { 
     this.view = view; 
     this.event = event; 
    } 
} 
関連する問題