美文网首页
Android事件冲突处理

Android事件冲突处理

作者: GTMYang | 来源:发表于2025-05-27 14:12 被阅读0次

Android 事件冲突处理逻辑手写实现

事件冲突是Android开发中常见的问题,主要发生在父子View或同级View之间对同一事件的竞争处理。下面我将手写实现几种常见的事件冲突处理方案。

一、基础冲突场景

假设有以下两种典型冲突场景:

  1. 横向滑动冲突:外层ViewGroup(如ViewPager)和内层View(如横向ListView)都处理横向滑动
  2. 垂直滑动冲突:ScrollView内部嵌套ListView

二、外部拦截法实现(父容器优先)

public class ParentView extends ViewGroup {
    private int mLastX;
    private int mLastY;
    
    @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
        boolean intercepted = false;
        int x = (int) event.getX();
        int y = (int) event.getY();
        
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                intercepted = false; // 必须返回false,否则子View无法接收事件
                break;
            case MotionEvent.ACTION_MOVE:
                int deltaX = x - mLastX;
                int deltaY = y - mLastY;
                
                // 父容器决定拦截条件:横向滑动距离大于纵向
                if (Math.abs(deltaX) > Math.abs(deltaY)) {
                    intercepted = true; // 拦截横向滑动
                } else {
                    intercepted = false;
                }
                break;
            case MotionEvent.ACTION_UP:
                intercepted = false;
                break;
        }
        
        mLastX = x;
        mLastY = y;
        return intercepted;
    }
}

三、内部拦截法实现(子View优先)

1. 子View实现

public class ChildView extends View {
    private int mLastX;
    private int mLastY;
    
    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        int x = (int) event.getX();
        int y = (int) event.getY();
        
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                getParent().requestDisallowInterceptTouchEvent(true); // 禁止父容器拦截
                break;
            case MotionEvent.ACTION_MOVE:
                int deltaX = x - mLastX;
                int deltaY = y - mLastY;
                
                // 子View决定何时允许父容器拦截
                if (父容器需要处理该事件的条件) {
                    getParent().requestDisallowInterceptTouchEvent(false); // 允许父容器拦截
                }
                break;
            case MotionEvent.ACTION_UP:
                break;
        }
        
        mLastX = x;
        mLastY = y;
        return super.dispatchTouchEvent(event);
    }
}

2. 父容器配合实现

public class ParentView extends ViewGroup {
    @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
        if (event.getAction() == MotionEvent.ACTION_DOWN) {
            return false; // 必须返回false
        } else {
            return true; // 其他情况默认拦截(但会被子View的requestDisallowIntercept影响)
        }
    }
}

四、滑动方向冲突解决方案

1. 水平/垂直滑动冲突判断

public class CustomViewPager extends ViewGroup {
    private float mStartX;
    private float mStartY;
    
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        boolean intercept = false;
        
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                mStartX = ev.getX();
                mStartY = ev.getY();
                intercept = false; // 必须不拦截DOWN事件
                break;
                
            case MotionEvent.ACTION_MOVE:
                float endX = ev.getX();
                float endY = ev.getY();
                float distanceX = Math.abs(endX - mStartX);
                float distanceY = Math.abs(endY - mStartY);
                
                // 横向滑动距离大于纵向,且横向距离超过阈值,则拦截
                if (distanceX > distanceY && distanceX > touchSlop) {
                    intercept = true;
                } else {
                    intercept = false;
                }
                break;
                
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                intercept = false;
                break;
        }
        
        return intercept;
    }
}

2. 嵌套滑动冲突解决方案

public class NestedScrollView extends ViewGroup {
    private float mLastY;
    
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        boolean intercept = false;
        float y = ev.getY();
        
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                mLastY = y;
                intercept = false;
                break;
                
            case MotionEvent.ACTION_MOVE:
                float dy = y - mLastY;
                
                // 如果子View已经滚动到顶部且向下滑动,或者已经滚动到底部且向上滑动
                if ((dy > 0 && isChildScrolledToTop()) || 
                    (dy < 0 && isChildScrolledToBottom())) {
                    intercept = true; // 拦截事件
                }
                break;
        }
        
        mLastY = y;
        return intercept;
    }
    
    private boolean isChildScrolledToTop() {
        // 判断子View是否已经滚动到顶部
        return true; // 简化实现
    }
    
    private boolean isChildScrolledToBottom() {
        // 判断子View是否已经滚动到底部
        return true; // 简化实现
    }
}

五、多点触控冲突处理

public class MultiTouchView extends View {
    private SparseArray<PointF> mActivePointers = new SparseArray<>();
    
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        int pointerIndex = event.getActionIndex();
        int pointerId = event.getPointerId(pointerIndex);
        int maskedAction = event.getActionMasked();
        
        switch (maskedAction) {
            case MotionEvent.ACTION_DOWN:
            case MotionEvent.ACTION_POINTER_DOWN: {
                PointF f = new PointF();
                f.x = event.getX(pointerIndex);
                f.y = event.getY(pointerIndex);
                mActivePointers.put(pointerId, f);
                break;
            }
            case MotionEvent.ACTION_MOVE: {
                for (int i = 0; i < event.getPointerCount(); i++) {
                    PointF point = mActivePointers.get(event.getPointerId(i));
                    if (point != null) {
                        point.x = event.getX(i);
                        point.y = event.getY(i);
                    }
                }
                break;
            }
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_POINTER_UP:
            case MotionEvent.ACTION_CANCEL: {
                mActivePointers.remove(pointerId);
                break;
            }
        }
        
        // 处理多点触控逻辑
        if (mActivePointers.size() == 2) {
            // 双指操作处理
            handleTwoFingers();
        }
        
        return true;
    }
    
    private void handleTwoFingers() {
        // 双指缩放或旋转逻辑
    }
}

六、复杂冲突处理策略

1. 优先级队列策略

public class PriorityTouchHandler {
    private List<View> mTouchTargets = new ArrayList<>();
    
    public void addTarget(View view, int priority) {
        mTouchTargets.add(view);
        // 按优先级排序
        Collections.sort(mTouchTargets, (v1, v2) -> {
            // 比较优先级逻辑
            return 0;
        });
    }
    
    public boolean dispatchTouchEvent(MotionEvent event) {
        for (View view : mTouchTargets) {
            if (view.dispatchTouchEvent(event)) {
                return true; // 高优先级View消费了事件
            }
        }
        return false;
    }
}

2. 事件代理策略

public class TouchDelegateGroup extends View {
    private List<View> mDelegates = new ArrayList<>();
    
    public void addDelegate(View delegate) {
        mDelegates.add(delegate);
    }
    
    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        // 将事件转换坐标后分发给各个代理View
        for (View delegate : mDelegates) {
            MotionEvent transformedEvent = transformEvent(event, delegate);
            if (delegate.dispatchTouchEvent(transformedEvent)) {
                return true;
            }
        }
        return super.dispatchTouchEvent(event);
    }
    
    private MotionEvent transformEvent(MotionEvent event, View target) {
        // 坐标转换逻辑
        return event;
    }
}

关键点总结

  1. 拦截时机

    • DOWN事件必须不拦截,否则后续事件无法传递到子View
    • MOVE事件根据业务逻辑决定是否拦截
  2. 滑动冲突判断

    • 比较横向和纵向滑动距离
    • 考虑touchSlop阈值(系统认为的最小滑动距离)
  3. 多点触控处理

    • 使用PointerId跟踪不同手指
    • 注意ACTION_POINTER_DOWN/UP事件
  4. 性能优化

    • 避免在事件处理方法中创建对象
    • 减少不必要的计算

以上实现可以根据具体业务需求进行调整和组合,实际开发中可能需要结合多种策略来解决复杂的事件冲突问题。

相关文章

  • Android 事件冲突处理

    概述 本文主要分享Android常见的事件冲突处理,处理方式有两种: 外部拦截:父容器处理冲突 内部拦截:子控件处...

  • android事件分发机制

    android中的事件处理,以及解决滑动冲突问题都离不开事件分发机制,android中的事件流,即MotionEv...

  • Android事件分发|事件冲突处理

    android的事件分发在面试时算是高频问题,工作中也能用到,这里将事件分发、事件冲突,和NestedScroll...

  • Android触摸事件机制

    在Android开发中经常会遇到事件冲突(如滑动事件),要处理这类问题需要了解事件传递机制,一次完整的事件主要包括...

  • android 事件分发以及滑动冲突

    android 事件分发以及滑动冲突 Android Activity 页面布局如下: view 的事件分发机制 ...

  • android 事件分发原理

    事件分发在 android 中实在是太重要了,滑动冲突的前置基础知识,滑动冲突不会处理,那么大部分页面效果是写不出...

  • android点击事件与触摸冲突

    Android ontouch 拖动与点击事件冲突解决方式

  • Android-解决事件冲突和处理滑动事件

    1.解决事件冲突的主要思路 1.down事件首先会传递到onInterceptTouchEvent()方法 2.如...

  • 二十二、事件处理

    Android提供了强大的事件处理机制,包含两套事件处理机制:基于监听的事件处理:为Android界面组件绑定特定...

  • Android 事件冲突

    单点的Down 事件 dispatchTouchEvent(MotionEvent ev) 来处理事件分发(Vie...

网友评论

      本文标题:Android事件冲突处理

      本文链接:https://www.haomeiwen.com/subject/mixkwjtx.html