在[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. 函数模板的载体-HPP

    在C++中,我们通常将声明放在.h头文件中,将具体的实现代码放在.cpp文件中.但是函数模板通常不这么做,函数模板是将其声明和实现都放在.hpp文件中.hpp是Header Plus Plus的缩写, ...

  2. Winform 窗体设计器 无法识别重复成员变量声明的问题

    打开窗体设计视图出现如下错误: 查看后台代码: ColumnHeader colHead; colHead = new ColumnHeader(); colHead.Text = "Ch& ...

  3. 【POJ 3009 Curling2.0 迷宫寻径 DFS】

    http://poj.org/problem?id=3009 模拟冰壶的移动,给出到达终点的最少投掷次数(不可达时为-1). 具体移动规则如下: 每次选四个方向之一,沿此方向一直前进,直到撞到bloc ...

  4. iOS10隐私设置及相应问题

    iOS10新添加了10个隐私设置: NSBluetoothPeripheralUsageDescription="App需要您的同意,才能访问蓝牙"; NSCalendarsUsa ...

  5. lua的学习

    lua是什么 Lua是一中嵌入式的脚本语言. Lua语言可以独立进行编程,但这不是其主要的使用方式.Lua虽然有动态.灵活的语法提供强大的功能,但并不像 Java.Python 等一样有一个完善的库( ...

  6. oracle子查询

    子查询:在一个查询的内部包含另外一个查询. 普通子查询 -- 查询出比7654工资还高的所有雇员的信息 select * from emp e where e.sal > (select sal ...

  7. ORACLE AWR概述及生成AWR报告

    1.Overview of the Automatic Workload Repository The Automatic Workload Repository (AWR) collects, pr ...

  8. 【最大点独立集】【poj1419】【Graph Coloring】

    题意: 最多能选取多少点,没有边相连. 解法: 取反图,求最大团 代码: #include<cstdio> #include<cstring> #include<iost ...

  9. javascript设计模式——Singleton

    单例模式指的是只能被实例化一次. 推荐阅读: http://blog.mgechev.com/2014/04/16/singleton-in-javascript/ 比较通用的一种Singleton模 ...

  10. UVA1600 Patrol Robot

    题意: 求机器人走最短路线,而且可以穿越障碍.N代表有N行,M代表最多能一次跨过多少个障碍. 分析: bfs()搜索,把访问状态数组改成了3维的,加了个维是当前能跨过的障碍数. 代码: #includ ...