[UI]抽屉菜单DrawerLayout分析(三)

在[UI]抽屉菜单DrawerLayout分析(一)和[UI]抽屉菜单DrawerLayout分析(二)中分别介绍了DrawerLayout得基本框架结构和ViewDragerHelper的作用以及手势分发,本文一起来分析其中的Scroller的使用情况。

  在ViewDragerHelper中可以发现private ScrollerCompat mScroller;说明抽屉菜单的具体滑动也是依赖于Scroller的使用,检索一下mScroller的引用,定位到forceSettleCapturedViewAt,这个方法回调用Scroller的startScroll来计算位移,它本身适用于计算和保存位移在特定时间的变化情况,最终的在绘制view时我可以获取其保存的x,y坐标值。
/**
 * Settle the captured view at the given (left, top) position.
 *
 * @param finalLeft Target left position for the captured view
 * @param finalTop Target top position for the captured view
 * @param xvel Horizontal velocity
 * @param yvel Vertical velocity
 * @return true if animation should continue through {@link #continueSettling(boolean)} calls
 */
private boolean forceSettleCapturedViewAt(int finalLeft, int finalTop, int xvel, int yvel) {
    final int startLeft = mCapturedView.getLeft();
    final int startTop = mCapturedView.getTop();
    final int dx = finalLeft - startLeft;
    final int dy = finalTop - startTop;

    if (dx == 0 && dy == 0) {
        // Nothing to do. Send callbacks, be done.
        mScroller.abortAnimation();
        setDragState(STATE_IDLE);
        returnfalse;
    }

    final int duration = computeSettleDuration(mCapturedView, dx, dy, xvel, yvel);
    mScroller.startScroll(startLeft, startTop, dx, dy, duration);

    setDragState(STATE_SETTLING);
    returntrue;
}
  这里用的是v4扩展包里的ScrollerCompat用于低版本兼容,它继承自ScrollerCompatImpl,可以看到里面主要的方法声明:
interface ScrollerCompatImpl{
    Object createScroller(Context context, Interpolator interpolator);
    boolean isFinished(Object scroller);
    int getCurrX(Object scroller);
    int getCurrY(Object scroller);
    float getCurrVelocity(Object scroller);
    boolean computeScrollOffset(Object scroller);
    void startScroll(Object scroller, int startX, int startY, int dx, int dy);
    void startScroll(Object scroller, int startX, int startY, int dx, int dy, int duration);
    void fling(Object scroller, int startX, int startY, int velX, int velY,
            int minX, int maxX, int minY, int maxY);
    void fling(Object scroller, int startX, int startY, int velX, int velY,
            int minX, int maxX, int minY, int maxY, int overX, int overY);
    void abortAnimation(Object scroller);
    void notifyHorizontalEdgeReached(Object scroller, int startX, int finalX, int overX);
    void notifyVerticalEdgeReached(Object scroller, int startY, int finalY, int overY);
    boolean isOverScrolled(Object scroller);
    int getFinalX(Object scroller);
    int getFinalY(Object scroller);
}

这里写图片描述
DragActionMethodFlow
从Scroller一直往上追溯,可以得到如图的调用流程。

当滑动屏幕时,DrawerLayout中的手势分发被触发,先执行onInterceptTouchEvent根据返回结果确定是否执行onTouchEvent,之后就是一些和ViewDragHelper之间的回调接口处理。

接下来追踪一下什么时候从Scroller中取出x,y来使用:
这里写图片描述
Scroller

在View里面有一个实现为空的computeScroll,DrawerLayout对它进行重写,这个方法应该是在view自动重绘是会被调用,回到continueSettling:

/**

 * Move the captured settling view by the appropriate amount for the current time.
 * If <code>continueSettling</code> returns true, the caller should call it again
 * on the next frame to continue.
 *
 * @param deferCallbacks true if state callbacks should be deferred via posted message.
 *                       Set this to true if you are calling this method from
 *                       {@link android.view.View#computeScroll()} or similar methods
 *                       invoked as part of layout or drawing.
 * @return true if settle is still in progress
 */
public boolean continueSettling(boolean deferCallbacks) {
    if (mDragState == STATE_SETTLING) {
        boolean keepGoing = mScroller.computeScrollOffset();
        final int x = mScroller.getCurrX();
        final int y = mScroller.getCurrY();
        final int dx = x - mCapturedView.getLeft();
        final int dy = y - mCapturedView.getTop();

        if (dx != 0) {
            mCapturedView.offsetLeftAndRight(dx);
        }
        if (dy != 0) {
            mCapturedView.offsetTopAndBottom(dy);
        }

        if (dx != 0 || dy != 0) {
            mCallback.onViewPositionChanged(mCapturedView, x, y, dx, dy);
        }

        if (keepGoing && x == mScroller.getFinalX() && y == mScroller.getFinalY()) {
            // Close enough. The interpolator/scroller might think we're still moving
            // but the user sure doesn't.
            mScroller.abortAnimation();
            keepGoing = mScroller.isFinished();
        }

        if (!keepGoing) {
            if (deferCallbacks) {
                mParentView.post(mSetIdleRunnable);
            } else {
                setDragState(STATE_IDLE);
            }
        }
    }

    return mDragState == STATE_SETTLING;
}

当状态处于STATE_SETTLING时开始获取Scroller中的x,y值,结合当前运动view的left,top位置,计算出偏移量,通过offsetLeftAndRight设置,里面是一些具体的位置改变,挺复杂的。

/**
 * Offset this view's horizontal location by the specified amount of pixels.
 *
 * @param offset the number of pixels to offset the view by
 */
public void offsetLeftAndRight(int offset) {
    if (offset != 0) {
        updateMatrix();
        final boolean matrixIsIdentity = mTransformationInfo == null
                || mTransformationInfo.mMatrixIsIdentity;
        if (matrixIsIdentity) {
            if (mDisplayList != null) {
                invalidateViewProperty(false, false);
            } else {
                final ViewParent p = mParent;
                if (p != null && mAttachInfo != null) {
                    final Rect r = mAttachInfo.mTmpInvalRect;
                    int minLeft;
                    int maxRight;
                    if (offset < 0) {
                        minLeft = mLeft + offset;
                        maxRight = mRight;
                    } else {
                        minLeft = mLeft;
                        maxRight = mRight + offset;
                    }
                    r.set(0, 0, maxRight - minLeft, mBottom - mTop);
                    p.invalidateChild(this, r);
                }
            }
        } else {
            invalidateViewProperty(false, false);
        }

        mLeft += offset;
        mRight += offset;
        if (mDisplayList != null) {
            mDisplayList.offsetLeftAndRight(offset);
            invalidateViewProperty(false, false);
        } else {
            if (!matrixIsIdentity) {
                invalidateViewProperty(false, true);
            }
            invalidateParentIfNeeded();
        }
    }
}

小结

至此DrawerLayout的基本工作流程分析完毕,简单做一个总结,v4包提供了ViewDragHelper类,里面封装了对scroller合view的位移操作,和Callback接口,通过DrawerLayout内的onInterceptTouchEvent和onTouchEvent的重载,触发ViewDragHelper内的相关方法,同时在DrawerLayout内实现ViewDragHelp.Callback.

作者:小文字
出处:http://www.cnblogs.com/avenwu/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值