06_View滑动冲突处理

134 阅读3分钟

View滑动冲突处理指南

在开发中,经常会遇到滑动冲突的问题。滑动冲突主要发生在多层滑动控件嵌套的情况下。本文将介绍常见的滑动冲突场景及其处理方法。

1. 常见的滑动冲突场景

场景1:外部滑动方向和内部滑动方向不一致

例如,使用ViewPager与Fragment结合实现页面滑动效果。ViewPager可以左右滑动切换Fragment,而Fragment内部可能包含一个List(如RecyclerView)。这种情况下,本应存在滑动冲突,但ViewPager内部已处理好滑动冲突,因此无需担心。如果使用ScrollView等其他控件,则需要手动处理滑动冲突,否则只能有一层能够滑动。其他类似情况还包括外部上下滑动,内部左右滑动等。

场景2:外部滑动方向和内部滑动方向一致

当内外层都在同一方向上滑动时,会出现滑动冲突。系统无法判断用户是希望滑动外层还是内层,导致滑动操作卡顿或只有一层能滑动。

场景3:上述两种情况的嵌套

存在复杂的嵌套结构时,需要同时处理方向一致和不一致的滑动冲突。

2. 滑动冲突处理规则

对于场景1

处理规则是根据滑动方向决定事件拦截。具体来说,可以根据滑动路径的角度或水平和竖直方向的距离差判断滑动方向。例如,通过判断竖直方向滑动距离大于水平距离来确定竖直滑动,否则为水平滑动。

对于场景2

无法单纯依靠滑动角度或距离差来判断。通常可以根据业务逻辑,例如某种状态下需要外部View响应滑动,而在另一状态下内部View响应滑动,从而确定处理规则。

对于场景3

需要综合处理场景1和场景2的滑动冲突。

3. 滑动冲突的解决方式

3.1 外部拦截法

外部拦截法指所有事件首先由父容器处理,父容器根据需要决定是否拦截事件。此方法符合事件分发机制。需重写父容器的onInterceptTouchEvent方法。示例代码如下:

@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;
            break;
        case MotionEvent.ACTION_MOVE:
            if (父容器需要当前点击事件) {
                intercepted = true;
            } else {
                intercepted = false;
            }
            break;
        case MotionEvent.ACTION_UP:
            intercepted = false;
            break;
        default:
            break;
    }
    mLastInterceptX = x;
    mLastInterceptY = y;
    return intercepted;
}

3.2 内部拦截法

内部拦截法指父容器不拦截任何事件,所有事件传递给子元素处理。如果子元素不需要事件,则由父容器处理。这种方法需通过requestDisallowInterceptTouchEvent方法配置,使用上比外部拦截法复杂。需重写子元素的dispatchTouchEvent方法。示例代码如下:

@Override
public boolean dispatchTouchEvent(MotionEvent event) {
    int x = (int) event.getX();
    int y = (int) event.getY();
    switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            parent.requestDisallowInterceptTouchEvent(true);
            break;
        case MotionEvent.ACTION_MOVE:
            int deltaX = x - mLastX;
            int deltaY = y - mLastY;
            if (父容器需要此类点击事件) {
                parent.requestDisallowInterceptTouchEvent(false);
            }
            break;
        case MotionEvent.ACTION_UP:
            break;
        default:
            break;
    }
    mLastX = x;
    mLastY = y;
    return super.dispatchTouchEvent(event);
}

通过以上方法,可以有效解决常见的滑动冲突问题,提升用户体验。

View系列文章

01_View基础知识

02_View的滑动

03_View的事件分发机制

04_View的工作流程

05_自定义View

05_自定义ViewGroup

06_View滑动冲突处理