自定义View系列(二)

一、

自定义View实战之

自定义取消预加载机制的ViewPager

ViewPager默认具有预加载机制,那么如何取消这个预加载机制呢?

其实很简单,只需要修改ViewPager的源码,将其中的常量

DEFAULT_OFFSCREEN_PAGES

的值改成0就行了,这个值默认为1。

注:viewPager有一个

setOffscreenPageLimit

方法,有人会想我们这样做:

viewPager.setOffscreenPageLimit(0);

不就行了吗?答案是不行,博主实测发现这种设置无效。为什么无效呢?原因很简单,大家看一下源码就明白了。

    public void setOffscreenPageLimit(int limit) {
        if (limit < DEFAULT_OFFSCREEN_PAGES) {
            Log.w(TAG, "Requested offscreen page limit " + limit + " too small; defaulting to " +
                    DEFAULT_OFFSCREEN_PAGES);
            limit = DEFAULT_OFFSCREEN_PAGES;
        }
        if (limit != mOffscreenPageLimit) {
            mOffscreenPageLimit = limit;
            populate();
        }
    }

二、

自定义View实战之

自定义可以控制是否可以左右滑动的ViewPager

实现如下:

修改ViewPager的源码。

第一步:

声明一个全局变量并初始化

private boolean noScroll = false;

第二步:

set方法

public void setNoScroll(boolean noScroll) {
        this.noScroll = noScroll;
    }

第三步:

修改onInterceptTouchEvent方法

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        if (noScroll) {
            return false;
        } else {
            /*
             * This method JUST determines whether we want to intercept the motion.
             * If we return true, onMotionEvent will be called and we do the actual
             * scrolling there.
             */

            final int action = ev.getAction() & MotionEventCompat.ACTION_MASK;

            // Always take care of the touch gesture being complete.
            if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
                // Release the drag.
                if (DEBUG) Log.v(TAG, "Intercept done!");
                mIsBeingDragged = false;
                mIsUnableToDrag = false;
                mActivePointerId = INVALID_POINTER;
                return false;
            }

            // Nothing more to do here if we have decided whether or not we
            // are dragging.
            if (action != MotionEvent.ACTION_DOWN) {
                if (mIsBeingDragged) {
                    if (DEBUG) Log.v(TAG, "Intercept returning true!");
                    return true;
                }
                if (mIsUnableToDrag) {
                    if (DEBUG) Log.v(TAG, "Intercept returning false!");
                    return false;
                }
            }

            switch (action) {
                case MotionEvent.ACTION_MOVE: {
                    /*
                     * mIsBeingDragged == false, otherwise the shortcut would have caught it. Check
                     * whether the user has moved far enough from his original down touch.
                     */

                    /*
                     * Locally do absolute value. mLastMotionY is set to the y value
                     * of the down event.
                     */
                    final int activePointerId = mActivePointerId;
                    if (activePointerId == INVALID_POINTER) {
                        // If we don't have a valid id, the touch down wasn't on content.
                        break;
                    }

                    final int pointerIndex = MotionEventCompat.findPointerIndex(ev, activePointerId);
                    final float x = MotionEventCompat.getX(ev, pointerIndex);
                    final float dx = x - mLastMotionX;
                    final float xDiff = Math.abs(dx);
                    final float y = MotionEventCompat.getY(ev, pointerIndex);
                    final float yDiff = Math.abs(y - mLastMotionY);
                    final int scrollX = getScrollX();
                    final boolean atEdge = (dx > 0 && scrollX == 0) || (dx < 0 && mAdapter != null &&
                            scrollX >= (mAdapter.getCount() - 1) * getWidth() - 1);
                    if (DEBUG) Log.v(TAG, "Moved x to " + x + "," + y + " diff=" + xDiff + "," + yDiff);

                    if (canScroll(this, false, (int) dx, (int) x, (int) y)) {
                        // Nested view has scrollable area under this point. Let it be handled there.
                        mInitialMotionX = mLastMotionX = x;
                        mLastMotionY = y;
                        return false;
                    }
                    if (xDiff > mTouchSlop && xDiff > yDiff) {
                        if (DEBUG) Log.v(TAG, "Starting drag!");
                        mIsBeingDragged = true;
                        setScrollState(SCROLL_STATE_DRAGGING);
                        mLastMotionX = x;
                        setScrollingCacheEnabled(true);
                    } else {
                        if (yDiff > mTouchSlop) {
                            // The finger has moved enough in the vertical
                            // direction to be counted as a drag...  abort
                            // any attempt to drag horizontally, to work correctly
                            // with children that have scrolling containers.
                            if (DEBUG) Log.v(TAG, "Starting unable to drag!");
                            mIsUnableToDrag = true;
                        }
                    }
                    break;
                }

                case MotionEvent.ACTION_DOWN: {
                    /*
                     * Remember location of down touch.
                     * ACTION_DOWN always refers to pointer index 0.
                     */
                    mLastMotionX = mInitialMotionX = ev.getX();
                    mLastMotionY = ev.getY();
                    mActivePointerId = MotionEventCompat.getPointerId(ev, 0);

                    if (mScrollState == SCROLL_STATE_SETTLING) {
                        // Let the user 'catch' the pager as it animates.
                        mIsBeingDragged = true;
                        mIsUnableToDrag = false;
                        setScrollState(SCROLL_STATE_DRAGGING);
                    } else {
                        completeScroll();
                        mIsBeingDragged = false;
                        mIsUnableToDrag = false;
                    }

                    if (DEBUG) Log.v(TAG, "Down at " + mLastMotionX + "," + mLastMotionY
                            + " mIsBeingDragged=" + mIsBeingDragged
                            + "mIsUnableToDrag=" + mIsUnableToDrag);
                    break;
                }

                case MotionEventCompat.ACTION_POINTER_UP:
                    onSecondaryPointerUp(ev);
                    break;
            }

            /*
             * The only time we want to intercept motion events is if we are in the
             * drag mode.
             */
            return mIsBeingDragged;
        }
    }

修改onTouchEvent方法

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        if (noScroll) {
            return false;
        } else {
            if (mFakeDragging) {
                // A fake drag is in progress already, ignore this real one
                // but still eat the touch events.
                // (It is likely that the user is multi-touching the screen.)
                return true;
            }

            if (ev.getAction() == MotionEvent.ACTION_DOWN && ev.getEdgeFlags() != 0) {
                // Don't handle edge touches immediately -- they may actually belong to one of our
                // descendants.
                return false;
            }

            if (mAdapter == null || mAdapter.getCount() == 0) {
                // Nothing to present or scroll; nothing to touch.
                return false;
            }

            if (mVelocityTracker == null) {
                mVelocityTracker = VelocityTracker.obtain();
            }
            mVelocityTracker.addMovement(ev);

            final int action = ev.getAction();
            boolean needsInvalidate = false;

            switch (action & MotionEventCompat.ACTION_MASK) {
                case MotionEvent.ACTION_DOWN: {
                    /*
                     * If being flinged and user touches, stop the fling. isFinished
                     * will be false if being flinged.
                     */
                    completeScroll();

                    // Remember where the motion event started
                    mLastMotionX = mInitialMotionX = ev.getX();
                    mActivePointerId = MotionEventCompat.getPointerId(ev, 0);
                    break;
                }
                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;
                            mLastMotionX = x;
                            setScrollState(SCROLL_STATE_DRAGGING);
                            setScrollingCacheEnabled(true);
                        }
                    }
                    if (mIsBeingDragged) {
                        // Scroll to follow the motion event
                        final int activePointerIndex = MotionEventCompat.findPointerIndex(
                                ev, mActivePointerId);
                        final float x = MotionEventCompat.getX(ev, activePointerIndex);
                        final float deltaX = mLastMotionX - x;
                        mLastMotionX = x;
                        float oldScrollX = getScrollX();
                        float scrollX = oldScrollX + deltaX;
                        final int width = getWidth();
                        final int widthWithMargin = width + mPageMargin;

                        final int lastItemIndex = mAdapter.getCount() - 1;
                        final float leftBound = Math.max(0, (mCurItem - 1) * widthWithMargin);
                        final float rightBound =
                                Math.min(mCurItem + 1, lastItemIndex) * widthWithMargin;
                        if (scrollX < leftBound) {
                            if (leftBound == 0) {
                                float over = -scrollX;
                                needsInvalidate = mLeftEdge.onPull(over / width);
                            }
                            scrollX = leftBound;
                        } else if (scrollX > rightBound) {
                            if (rightBound == lastItemIndex * widthWithMargin) {
                                float over = scrollX - rightBound;
                                needsInvalidate = mRightEdge.onPull(over / width);
                            }
                            scrollX = rightBound;
                        }
                        // Don't lose the rounded component
                        mLastMotionX += scrollX - (int) scrollX;
                        scrollTo((int) scrollX, getScrollY());
                        if (mOnPageChangeListener != null) {
                            final int position = (int) scrollX / widthWithMargin;
                            final int positionOffsetPixels = (int) scrollX % widthWithMargin;
                            final float positionOffset = (float) positionOffsetPixels / widthWithMargin;
                            mOnPageChangeListener.onPageScrolled(position, positionOffset,
                                    positionOffsetPixels);
                        }
                    }
                    break;
                case MotionEvent.ACTION_UP:
                    if (mIsBeingDragged) {
                        final VelocityTracker velocityTracker = mVelocityTracker;
                        velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
                        int initialVelocity = (int) VelocityTrackerCompat.getXVelocity(
                                velocityTracker, mActivePointerId);
                        mPopulatePending = true;
                        final int widthWithMargin = getWidth() + mPageMargin;
                        final int scrollX = getScrollX();
                        final int currentPage = scrollX / widthWithMargin;
                        int nextPage = initialVelocity > 0 ? currentPage : currentPage + 1;
                        setCurrentItemInternal(nextPage, true, true, initialVelocity);

                        mActivePointerId = INVALID_POINTER;
                        endDrag();
                        needsInvalidate = mLeftEdge.onRelease() | mRightEdge.onRelease();
                    }
                    break;
                case MotionEvent.ACTION_CANCEL:
                    if (mIsBeingDragged) {
                        setCurrentItemInternal(mCurItem, true, true);
                        mActivePointerId = INVALID_POINTER;
                        endDrag();
                        needsInvalidate = mLeftEdge.onRelease() | mRightEdge.onRelease();
                    }
                    break;
                case MotionEventCompat.ACTION_POINTER_DOWN: {
                    final int index = MotionEventCompat.getActionIndex(ev);
                    final float x = MotionEventCompat.getX(ev, index);
                    mLastMotionX = x;
                    mActivePointerId = MotionEventCompat.getPointerId(ev, index);
                    break;
                }
                case MotionEventCompat.ACTION_POINTER_UP:
                    onSecondaryPointerUp(ev);
                    mLastMotionX = MotionEventCompat.getX(ev,
                            MotionEventCompat.findPointerIndex(ev, mActivePointerId));
                    break;
            }
            if (needsInvalidate) {
                invalidate();
            }
            return true;
        }
    }

第四步:在业务代码中通过调用

setNoScroll

方法,我们就可以自由控制ViewPager是否可以左右滑动了,默认是可以左右滑动的。

猜你喜欢

转载自blog.csdn.net/zdj_Develop/article/details/81356983