在[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. CGFW时装发布及活动整体一览表

    CGFW时装发布及活动整体一览表 CGFW时装发布及活动整体一览表

  2. #include <boost/scoped_ptr.hpp>

    多个元素使用#include <boost/scoped_array.hpp> 单个元素使用#include <boost/scoped_ptr.hpp> 作用域指针 它独占一 ...

  3. OC基础12:数字、字符串和集合1

    "OC基础"这个分类的文章是我在自学Stephen G.Kochan的<Objective-C程序设计第6版>过程中的笔记. 1.有时要将一些数字数据类型的值当做对象来 ...

  4. HttpContext详解【转】

    HttpContext 类:封装有关个别 HTTP 请求的所有 HTTP 特定的信息. 在处理请求执行链的各个阶段中,会有一个对象在各个对象之间进行传递,也即会保存请求的上下文信息,这个对象就是Htt ...

  5. 对拍BAT

    :loop makedata.exe K.exe Kture.exe fc a.out b.out if %errorlevel%==0 goto loop pause

  6. 使用FileSystemWatcher捕获系统文件状态

    源代码: using System; using System.Collections.Generic; using System.Linq; using System.Text; using Sys ...

  7. tab.js分享及浏览器兼容性问题汇总

    在 样式布局分享-基于frozen.js的移动OA 文章中,用了到第三方组件 tab.js(带菜单的横屏滑动插件),其兼容性很差,进行优化后,已兼容全平台(且支持IE6+). tab.js GitHu ...

  8. 详解Linq to SQL

    第一部分,什么是Linq to sql Linq to sql(或者叫DLINQ)是LINQ(.NET语言集成查询)的一部分,全称基于关系数据的 .NET 语言集成查询,用于以对象形式管理关系数据,并 ...

  9. Mac下Mysql启动异常["ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/tmp/mysql.sock' (2)"]

    在mac下使用brew安装mysql,之前没有使用过,今天启动的时候发现启动不了 huijundeMacBook-Pro:bin huijunzhang$ mysql ERROR (HY000): C ...

  10. Android中view的事件

    view:top.left.right.bottom,相对于parent的位置参数,获取通过get*()来获取.width=right-left.height=bottom-top.x=left+tr ...