【问题标题】:Intercept click/touch event without overriding ViewGroup or View methods拦截点击/触摸事件而不覆盖 ViewGroup 或 View 方法
【发布时间】:2017-09-05 14:02:24
【问题描述】:

有什么方法可以在不扩展View 或包装一些ViewGroup(可以拦截子事件)的情况下拦截/装饰视图的触摸事件?

假设我有处理项目点击事件的ExpandableListView。如果我在适配器返回的膨胀项视图上设置适配器OnClickListenerOnTouchListenerExpandableListView 不会执行相应的操作(组扩展),因为事件已被项的侦听器消耗。

我不想使用ExpandableListView#setOnItemClickListener 的原因是,我想在不使用ExpandableListView 依赖的情况下装饰适配器中的点击事件。

【问题讨论】:

    标签: java android android-event


    【解决方案1】:

    我找到了解决此问题的有效解决方案。

    解决方案:在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;
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2014-01-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-01-30
      • 2015-03-28
      相关资源
      最近更新 更多