扩大viewpager响应区域(viewpager嵌套时)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/duoluo9/article/details/79960271

1.需求

         viewpager中嵌套了scollview实现fragment的纵向滑动,scollview嵌套了viewpager实现轮播形式。已有代码使用viewpager的 android:clipChildren="false"实现一页显示3个view的效果,但是视觉效果是3个,而实际viewpager大小并没有改变,先需要按触摸到视觉效果的左右2个view时响应子viewpager的滑动事件,而不是父viewpager的滑动事件。

2.实现

            自定义2个viewpager,父viewpager处理当有子viewpager时不拦截touchevent,子viewpager实现响应区域扩大逻辑(TouchDelegate)。

3.代码

            父viewpager:

            

public class ShrinkTouchViewPager extends ViewPager {

    private final int TOUCH_SHRINK = 0;
    private int mTouchShrinkBottom = 0;
    private int mTouchShrinkLeft = 0;
    private int mTouchShrinkRight = 0;
    private int mTouchShrinkTop = 0;

    public ShrinkTouchViewPager(Context context) {
        super(context);
        init(context, null);
    }

    public ShrinkTouchViewPager(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context, attrs);
    }

    private void init(Context context, AttributeSet attrs) {
        TypedArray a = context.obtainStyledAttributes(attrs,
                R.styleable.LargeTouchableAreaView);
        int shrink = (int) a.getDimension(
                R.styleable.ShrinkTouchViewPager_shrink, TOUCH_SHRINK);
        mTouchShrinkBottom = shrink;
        mTouchShrinkLeft = shrink;
        mTouchShrinkRight = shrink;
        mTouchShrinkTop = shrink;
        mTouchShrinkBottom = (int) a.getDimension(
                R.styleable.ShrinkTouchViewPager_shrinkBottom,
                mTouchShrinkBottom);
        mTouchShrinkLeft = (int) a.getDimension(
                R.styleable.ShrinkTouchViewPager_shrinkLeft,
                mTouchShrinkLeft);
        mTouchShrinkRight = (int) a.getDimension(
                R.styleable.ShrinkTouchViewPager_shrinkRight,
                mTouchShrinkRight);
        mTouchShrinkTop = (int) a.getDimension(
                R.styleable.ShrinkTouchViewPager_shrinkTop,
                mTouchShrinkTop);
        a.recycle();
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {

        if (ev.getY() > mTouchShrinkTop && ev.getY() < mTouchShrinkBottom && getCurrentItem() == 0) {
            return false;
        }
        return super.onInterceptTouchEvent(ev);
    }
}

            attrs:

 <declare-styleable name="LargeTouchableAreaView">
        <attr name="addition" format="dimension" />
        <attr name="additionBottom" format="dimension" />
        <attr name="additionLeft" format="dimension" />
        <attr name="additionRight" format="dimension" />
        <attr name="additionTop" format="dimension" />
    </declare-styleable>

            子viewpager:

public class EnlargeTouchViewPager extends ViewPager {

    private final int TOUCH_ADDITION = 0;
    private int mTouchAdditionBottom = 0;
    private int mTouchAdditionLeft = 0;
    private int mTouchAdditionRight = 0;
    private int mTouchAdditionTop = 0;
    private int mPreviousLeft = -1;
    private int mPreviousRight = -1;
    private int mPreviousBottom = -1;
    private int mPreviousTop = -1;

    public EnlargeTouchViewPager(Context context) {
        super(context);
        init(context, null);
    }

    public EnlargeTouchViewPager(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context, attrs);
    }

    private void init(Context context, AttributeSet attrs) {
        TypedArray a = context.obtainStyledAttributes(attrs,
                R.styleable.LargeTouchableAreaView);
        int addition = (int) a.getDimension(
                R.styleable.LargeTouchableAreaView_addition, TOUCH_ADDITION);
        mTouchAdditionBottom = addition;
        mTouchAdditionLeft = addition;
        mTouchAdditionRight = addition;
        mTouchAdditionTop = addition;
        mTouchAdditionBottom = (int) a.getDimension(
                R.styleable.LargeTouchableAreaView_additionBottom,
                mTouchAdditionBottom);
        mTouchAdditionLeft = (int) a.getDimension(
                R.styleable.LargeTouchableAreaView_additionLeft,
                mTouchAdditionLeft);
        mTouchAdditionRight = (int) a.getDimension(
                R.styleable.LargeTouchableAreaView_additionRight,
                mTouchAdditionRight);
        mTouchAdditionTop = (int) a.getDimension(
                R.styleable.LargeTouchableAreaView_additionTop,
                mTouchAdditionTop);
        a.recycle();
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right,
                            int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        if (left != mPreviousLeft || top != mPreviousTop
                || right != mPreviousRight || bottom != mPreviousBottom) {
            mPreviousLeft = left;
            mPreviousTop = top;
            mPreviousRight = right;
            mPreviousBottom = bottom;
            final View parent = (View) this.getParent();
            parent.setTouchDelegate(new TouchDelegate(new Rect(left
                    - mTouchAdditionLeft, top - mTouchAdditionTop, right
                    + mTouchAdditionRight, bottom + mTouchAdditionBottom), this));
        }
    }

    class TouchDelegate extends android.view.TouchDelegate {

        /**
         * View that should receive forwarded touch events
         */
        private View mDelegateView;

        /**
         * Bounds in local coordinates of the containing view that should be mapped to the delegate
         * view. This rect is used for initial hit testing.
         */
        private Rect mBounds;

        /**
         * mBounds inflated to include some slop. This rect is to track whether the motion events
         * should be considered to be be within the delegate view.
         */
        private Rect mSlopBounds;

        /**
         * True if the delegate had been targeted on a down event (intersected mBounds).
         */
        private boolean mDelegateTargeted;

        /**
         * The touchable region of the View extends above its actual extent.
         */
        public static final int ABOVE = 1;

        /**
         * The touchable region of the View extends below its actual extent.
         */
        public static final int BELOW = 2;

        /**
         * The touchable region of the View extends to the left of its
         * actual extent.
         */
        public static final int TO_LEFT = 4;

        /**
         * The touchable region of the View extends to the right of its
         * actual extent.
         */
        public static final int TO_RIGHT = 8;

        private int mSlop;

        private boolean isStartLeft = false;

        /**
         * Constructor
         *
         * @param bounds       Bounds in local coordinates of the containing view that should be mapped to
         *                     the delegate view
         * @param delegateView The view that should receive motion events
         */
        public TouchDelegate(Rect bounds, View delegateView) {
            super(bounds, delegateView);
            mBounds = bounds;

            mSlop = ViewConfiguration.get(delegateView.getContext()).getScaledTouchSlop();
            mSlopBounds = new Rect(bounds);
            mSlopBounds.inset(-mSlop, -mSlop);
            mDelegateView = delegateView;
        }

        /**
         * Will forward touch events to the delegate view if the event is within the bounds
         * specified in the constructor.
         *
         * @param event The touch event to forward
         * @return True if the event was forwarded to the delegate, false otherwise.
         */
        public boolean onTouchEvent(MotionEvent event) {
            int x = (int) event.getX();
            int y = (int) event.getY();
            boolean sendToDelegate = false;
            boolean hit = true;
            boolean handled = false;

            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    Rect bounds = mBounds;

                    if (bounds.contains(x, y)) {
                        mDelegateTargeted = true;
                        sendToDelegate = true;
                        if (event.getX() < mDelegateView.getX()) {
                            isStartLeft = true;
                        } else {
                            isStartLeft = false;
                        }
                    }
                    break;
                case MotionEvent.ACTION_UP:
                case MotionEvent.ACTION_MOVE:
                    sendToDelegate = mDelegateTargeted;
                    if (sendToDelegate) {
                        Rect slopBounds = mSlopBounds;
                        if (!slopBounds.contains(x, y)) {
                            hit = false;
                        }
                    }
                    break;
                case MotionEvent.ACTION_CANCEL:
                    sendToDelegate = mDelegateTargeted;
                    mDelegateTargeted = false;
                    break;
            }
            if (sendToDelegate) {
                final View delegateView = mDelegateView;

                if (hit) {
                    // Offset event coordinates to be inside the target view
                    if (isStartLeft) {
                        event.setLocation(event.getX() + mTouchAdditionLeft, delegateView.getY() + delegateView.getHeight() / 2);
                    } else {
                        event.setLocation(event.getX() - mTouchAdditionRight, delegateView.getY() + delegateView.getHeight() / 2);
                    }
                } else {
                    // Offset event coordinates to be outside the target view (in case it does
                    // something like tracking pressed state)
                    int slop = mSlop;
                    event.setLocation(-(slop * 2), -(slop * 2));
                }
                handled = delegateView.dispatchTouchEvent(event);
            }
            return handled;
        }
    }
}

            attrs:

 <declare-styleable name="ShrinkTouchViewPager">
        <attr name="shrink" format="dimension" />
        <attr name="shrinkBottom" format="dimension" />
        <attr name="shrinkLeft" format="dimension" />
        <attr name="shrinkRight" format="dimension" />
        <attr name="shrinkTop" format="dimension" />
    </declare-styleable>

阅读更多

没有更多推荐了,返回首页