在[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. 15 Tricks to Appear Smart in Emails(转)

    英文原文:15 Tricks to Appear Smart in Emails 如果你不关心在邮件中表现聪明,就不要看本文了. 好吧,我们是孤独的. 在公司环境里,与电子通讯的肥沃土壤相比,没有什么 ...

  2. python-django如何在sae中使用自带ImageField和FileField -django-上善若水小站

    python-django如何在sae中使用自带ImageField和FileField -django-上善若水小站 python-django如何在sae中使用自带ImageField和FileF ...

  3. hdu 5607 graph (矩阵乘法快速幂)

    考虑一个经典的问题: 询问从某个点出发,走 k 步到达其它各点的方案数? 这个问题可以转化为矩阵相乘,所以矩阵快速幂即可解决. 本题思路: 矩阵经典问题:求从i点走k步后到达j点的方案数(mod p) ...

  4. centos6.2+nginx-1.2.3+php-5.3.17安装脚本

    #!/bin/bash # # vm test install script # # create by xk # # data 2013-04-25 # # SOFTPATH=/home/tools ...

  5. 依赖注入及AOP简述(十)——Web开发中常用Scope简介 .

    1.2.    Web开发中常用Scope简介 这里主要介绍基于Servlet的Web开发中常用的Scope. l        第一个比较常用的就是Application级Scope,通常我们会将一 ...

  6. VS2012 黑色护眼主题

    在黑色主题基础上,更改了字体 Ms Comic Sans 字号也增大了 附件中有两个 一个是原版主题下载自https://studiostyl.es/ 第二个是如下改完后的主题 vssettings. ...

  7. JS高级——闭包

    <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <m ...

  8. hdu2368Alfredo's Pizza Restaurant

    Problem Description Traditionally after the Local Contest, judges and contestants go to their favour ...

  9. UVa 1583 - Digit Generator

    A+A的每一位的数字的和=B 问你每一个B对应 的最小的A 是多少 不然输出0: #include <cstdio> #include <iostream> #include ...

  10. HDU 1232(畅通工程)题解

    以防万一,题目原文和链接均附在文末.那么先是题目分析: [一句话题意] 给定一具有N个节点的图和其边集,求其集合数量. [题目分析] 并查集经典题...其实就是创建好并查集就行了.. [算法流程] 于 ...