在[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);

}

从Scroller一直往上追溯,可以得到如图的调用流程。

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

接下来追踪一下什么时候从Scroller中取出x,y来使用:

在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.

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

[UI]抽屉菜单DrawerLayout分析(三)的更多相关文章

  1. [UI]抽屉菜单DrawerLayout分析(一)

    本文转载于:http://www.cnblogs.com/avenwu/archive/2014/04/16/3669367.html 侧拉菜单作为常见的导航交互控件,最开始在没有没有android官 ...

  2. [UI]抽屉菜单DrawerLayout分析(二)

    继续分析DrawerLayout的手势分发部分 谈到手势分发,这本身就是个好话题,DrawerLayout作为继承自ViewGroup得布局他可以拦截手势也可以分发给子view,也就是在 onInte ...

  3. Android抽屉菜单DrawerLayout的实现案例

    (1)项目布局文件 activity_main.xml <android.support.v4.widget.DrawerLayout xmlns:android="http://sc ...

  4. android 5.X Toolbar+DrawerLayout实现抽屉菜单

    前言  android5.X新增的一个控件Toolbar,这个控件比ActionBar更加自由,可控,因为曾经的ActionBar的灵活性比較差,所以google逐渐使用Toolbar替代Action ...

  5. 使用AsyncTask异步更新UI界面及原理分析

    概述: AsyncTask是在Android SDK 1.5之后推出的一个方便编写后台线程与UI线程交互的辅助类.AsyncTask的内部实现是一个线程池,所有提交的异步任务都会在这个线程池中的工作线 ...

  6. 可折叠的ToolBar+抽屉菜单NavigationView+浮动按钮FloatButton

    使用Material Design风格的ToolBar和抽屉导航 先看个简单的运行效果 主要记录下布局的写法 1 用到的Google Design依赖和V7包依赖 compile 'com.andro ...

  7. AndroidUI 侧滑菜单 DrawerLayout的使用

    直接上代码: activity_main.xml: <android.support.v4.widget.DrawerLayout xmlns:android="http://sche ...

  8. wemall doraemon中Android app商城系统解决左侧抽屉菜单和viewpager不能兼容问题

    完美解决左侧抽屉菜单和viewpager不能兼容左右滑动的问题,可进行参考. WeMall-Client/res/layout/wemall_main_ui.xml </RadioGroup&g ...

  9. Android 高仿QQ5.2双向側滑菜单DrawerLayout实现源代码

    Android 高仿QQ5.2双向側滑菜单DrawerLayout实现源代码 左右側滑效果图 watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a ...

随机推荐

  1. bzoj2719[Violet 4]银河之星

    Description Input Output 一道坑爹的搜索……题意是可以往任意方向移动3格,或者如果旁边有格子的时候可以越过它移动,然后把它吃掉.要求吃到最后一个的位置在x0,y0 注意到可以越 ...

  2. Linux 程序启停脚本

    start.sh #!/bin/sh java -jar ./program.jar & echo $! > /var/run/program.pid stop.sh #!/bin/sh ...

  3. as3 页游中,新手指导中,屏蔽所有交互对象,但除了指定交互对象可用的方法【转http://blog.csdn.net/linjf520/article/details/9450945】

    package { import flash.display.InteractiveObject; import flash.display.Stage; import flash.events.Mo ...

  4. 从linux telnet到exchange邮件server来測试发送邮件

    我们在Linux下,能够通过telnet邮件server的25port(一般smtp邮件server都是这个),来測试是否能发送邮件. 前提是先得把DNS配好,或者/etc/hosts文件制定好邮件s ...

  5. uva 10161 Ant on a Chessboard 蛇形矩阵 简单数学题

    题目给出如下表的一个矩阵: (红字表示行数或列数) 25 24 23 22 21 5 10 11 12 13 20 9 8 7 14 19 3 2 3 6 15 18 2 1 4 5 16 17 1 ...

  6. 让rdlc报表在ReportViewer中水平居中的方法

    正常情况下,rdlc报表在Reportviewer中是居左显示的,如图: 在Reporviewer的属性中,我没有找到能让rdlc的居中显示的方法.网上其他人用的方法都试了,没能实现,只能自己找方法解 ...

  7. JavaScript 【非IE DOM2级XML】

    DOM2中的XML IE可以实现了对XML字符串或XML文件的读取,其他浏览器也各自实现了对XML处理功能.DOM2级在document.implementaion中引入了createDocument ...

  8. IE6下a标签上发送ajax请求总是error

    IE6下真是处处是坑啊!!!走过了一个又一个坑,记录一下吧. 之前不知道a标签上注册click事件之后,发送ajax请求总是error.后来经过几番网上搜索,终于找到高人遇到此坑的解决办法.原来是a标 ...

  9. Genymotion error:The virtual device got no IP address

    控制面板,网络和intent,网络和共享中心,更改适配器设置,看下你的VirtualBox Host-Only Ethernet Adapter这个显卡 启动了没有, 没有就启动它!!!

  10. 几个Python oj的网站

    http://www.rqnoj.cn/ http://www.pythontip.com/