在ViewPager中禁用某些片段的滑动

4

我有一个ViewPager,可以禁用或启用滑动触摸:

public class ConfigurablePager extends ViewPager {

    private final AtomicBoolean touchesAllowed = new AtomicBoolean();

    ...

    private boolean touchesAllowed() {
        return touchesAllowed.get();
    }

    public void enableTouches() {
        touchesAllowed.set(true);
    }

    public void disableTouches() {
        touchesAllowed.set(false);
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        return touchesAllowed() && super.onTouchEvent(ev);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        return touchesAllowed() && super.onInterceptTouchEvent(ev);
    }
}

一些片段可以滑动,而其他片段则不行。Pager适配器知道每个片段的滑动行为。这种行为可以在ViewPager.OnPageChangeListener中更改:

@Override
public void onPageSelected(int position) {
    if (adapter.isTouchesAllowed(position)) {
        views.pager.enableTouches();
    } else {
        views.pager.disableTouches();
    }
}

问题
有时,当我快速滑动片段并同时点击其他片段的选项卡时,视图页面可能会抛出IllegalArgumentException:

FATAL EXCEPTION:
main java.lang.IllegalArgumentException: pointerIndex out of range
at android.view.MotionEvent.nativeGetAxisValue(Native Method)
at android.view.MotionEvent.getX(MotionEvent.java:1979)
at android.support.v4.view.MotionEventCompatEclair.getX(MotionEventCompatEclair.java:32)
at android.support.v4.view.MotionEventCompat$EclairMotionEventVersionImpl.getX(MotionEventCompat.java:110)
at android.support.v4.view.MotionEventCompat.getX(MotionEventCompat.java:462)
at android.support.v4.view.ViewPager.onTouchEvent(ViewPager.java:2080)
at com.test.debugpager.ConfigurablePager.onTouchEvent(ConfigurablePager.java:39)
at android.view.View.dispatchTouchEvent(View.java:7384)
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2203)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1938)
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2231)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1952)
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2209)

这是因为ViewPager保存了最后一个指针ID,并获取不一致状态(一些触摸事件由onInterceptTouchEvent丢弃),例如使用来自上一个触摸事件的不正确的mActivePointerIdACTION_MOVE(请参见ViewPager.java源代码)。

问题
是否有其他方法禁用某些片段上的滑动,而无需覆盖onInterceptTouchEvent

ViewPager源代码(onTouchEvent):

case MotionEvent.ACTION_MOVE:
    if (!mIsBeingDragged) {
        final int pointerIndex = MotionEventCompat.findPointerIndex(ev, mActivePointerId);
        final float x = MotionEventCompat.getX(ev, pointerIndex);
        final float xDiff = Math.abs(x - mLastMotionX);
        final float y = MotionEventCompat.getY(ev, pointerIndex);
        final float yDiff = Math.abs(y - mLastMotionY);
        if (DEBUG) Log.v(TAG, "Moved x to " + x + "," + y + " diff=" + xDiff + "," + yDiff);
        if (xDiff > mTouchSlop && xDiff > yDiff) {
            if (DEBUG) Log.v(TAG, "Starting drag!");
            mIsBeingDragged = true;
            requestParentDisallowInterceptTouchEvent(true);
            mLastMotionX = x - mInitialMotionX > 0 ? mInitialMotionX + mTouchSlop :
            mInitialMotionX - mTouchSlop;
            mLastMotionY = y;
            setScrollState(SCROLL_STATE_DRAGGING);
            setScrollingCacheEnabled(true);

            // Disallow Parent Intercept, just in case
            ViewParent parent = getParent();
            if (parent != null) {
                parent.requestDisallowInterceptTouchEvent(true);
            }
        }
    }
1个回答

1

已解决

我仔细阅读了有关在 ViewGroup 中识别手势的 安卓指南 并分析了 ViewPageronTouchEvent 源代码。在这里,我认识到 ViewPager 仅适用于 ACTION_MOVE 事件,因此我们不应该仅为此操作调用触摸回调,并且在调用基类 onTouchEvent 之前应遵守基本的 ViewGroup onInterceptTouchEvent 结果。

根据这些规则,我修改了我的 ViewPager 代码:

@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
    if (touchesAllowed()) {
        return super.onInterceptTouchEvent(ev);
    } else {
        if (MotionEventCompat.getActionMasked(ev) == MotionEvent.ACTION_MOVE) {
            // ignore move action
        } else {
            if (super.onInterceptTouchEvent(ev)) {
                super.onTouchEvent(ev);
            }
        }
        return false;
    }
}

@Override
public boolean onTouchEvent(MotionEvent ev) {
    if (touchesAllowed()) {
        return super.onTouchEvent(ev);
    } else {
        return MotionEventCompat.getActionMasked(ev) != MotionEvent.ACTION_MOVE && super.onTouchEvent(ev);
    }
}

1
@toddsalpen 它应该可以工作。当你说某个解决方案无法工作时,你必须提供一些情况和环境,说明这个解决方案对你不起作用。如果没有这样的信息,这个评论就毫无意义。 - Kirill

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接