【问题标题】:Disable swipe on some fragments in ViewPager在 ViewPager 中禁用某些片段的滑动
【发布时间】:2016-08-01 19:42:11
【问题描述】:

我有一个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);
    }
}

有些片段可以滑动,但有些则不能。寻呼机适配器知道每个片段的滑动行为。这种行为可以在ViewPager.OnPageChangeListener中改变:

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

问题
有时,当我快速滑动片段并同时单击其他片段的选项卡时,viewpager 会抛出IllegalArgumentException

致命例外:
主 java.lang.IllegalArgumentException:pointerIndex 超出范围
在 android.view.MotionEvent.nativeGetAxisValue(Native Method)
在 android.view.MotionEvent.getX(MotionEvent.java:1979)
在 android.support.v4.view.MotionEventCompatEclair.getX(MotionEventCompatEclair.java:32)
在 android.support.v4.view.MotionEventCompat$EclairMotionEventVersionImpl.getX(MotionEventCompat.java:110)
在 android.support.v4.view.MotionEventCompat.getX(MotionEventCompat.java:462)
在 android.support.v4.view.ViewPager.onTouchEvent(ViewPager.java:2080)
在 com.test.debugpager.ConfigurablePager.onTouchEvent(ConfigurablePager.java:39)
在 android.view.View.dispatchTouchEvent(View.java:7384)
在 android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2203)
在 android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1938)
在 android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2231)
在 android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1952)
在 android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2209)

这是因为ViewPager 保存最后一个pointerId 并获得不一致的状态(一些触摸事件被onInterceptTouchEvent 丢弃)例如ACTION_MOVE 最后一次触摸事件中的 mActivePointerId 不正确(参见 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);
            }
        }
    }

【问题讨论】:

    标签: java android android-viewpager swipe touch-event


    【解决方案1】:

    已解决

    我在ViewGroup 中仔细阅读了android guide 关于手势识别的内容,并分析了ViewPager onTouchEvent 来源。在这里我认识到ViewPager 只为ACTION_MOVE 事件滑动,所以我们不应该只为这个动作调用触摸回调,我们应该在调用基类onTouchEvent 之前遵循基本ViewGrouponInterceptTouchEvent 结果。

    根据这条规则,我更改了我的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);
        }
    }
    

    【讨论】:

    • @toddsalpen 它应该可以工作。当您说某些事情不起作用时,您必须提供一些该解决方案不适合您的案例和环境。没有这个评论是没有用的。
    猜你喜欢
    • 1970-01-01
    • 2017-06-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-12-23
    • 1970-01-01
    相关资源
    最近更新 更多