Android View体系(四)从源码解析Scroller
在Android View体系(二)实现View滑动的六种方法这篇文章中我们讲到了用Scroller来实现View的滑动,所以这篇文章我们就不介绍Scroller是如何使用的了,本篇就从源码来分析下Scroller为何能够实现View的滑动。
1.Scroller的构造函数
要想使用Scroller,必须先调用new Scroller(),我们先来看看Scroller的构造函数:
/**
* Create a Scroller with the default duration and interpolator.
*/
public Scroller(Context context) {
this(context, null);
} /**
* Create a Scroller with the specified interpolator. If the interpolator is
* null, the default (viscous) interpolator will be used. "Flywheel" behavior will
* be in effect for apps targeting Honeycomb or newer.
*/
public Scroller(Context context, Interpolator interpolator) {
this(context, interpolator,
context.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.HONEYCOMB);
} /**
* Create a Scroller with the specified interpolator. If the interpolator is
* null, the default (viscous) interpolator will be used. Specify whether or
* not to support progressive "flywheel" behavior in flinging.
*/
public Scroller(Context context, Interpolator interpolator, boolean flywheel) {
mFinished = true;
if (interpolator == null) {
mInterpolator = new ViscousFluidInterpolator();
} else {
mInterpolator = interpolator;
}
mPpi = context.getResources().getDisplayMetrics().density * 160.0f;
mDeceleration = computeDeceleration(ViewConfiguration.getScrollFriction());
mFlywheel = flywheel; mPhysicalCoeff = computeDeceleration(0.84f); // look and feel tuning
}
Scroller有三个构造函数,通常情况我们都用第一种,第二种需要传进去一个差值器Interpolator ,如果不传则采用默认的差值器(viscous)。
2.Scroller的startScroll方法
public void startScroll(int startX, int startY, int dx, int dy, int duration) {
mMode = SCROLL_MODE;
mFinished = false;
mDuration = duration;
mStartTime = AnimationUtils.currentAnimationTimeMillis();
mStartX = startX;
mStartY = startY;
mFinalX = startX + dx;
mFinalY = startY + dy;
mDeltaX = dx;
mDeltaY = dy;
mDurationReciprocal = 1.0f / (float) mDuration;
}
在startScroll()方法中并没有调用类似开启滑动的方法,而是保存了传进来的各种参数:startX和startY表示滑动开始的起点,dx和dy表示滑动的距离,duration则表示滑动持续的时间。所以startScroll()方法只是用来做前期准备的并不能使View进行滑动。关键是我们在startScroll()方法后调用了 invalidate()方法,这个方法会导致View的重绘,而View的重绘会调用View的draw()方法,draw()方法又会调用View的computeScroll()方法,我们重写computeScroll()方法:
@Override
public void computeScroll() {
super.computeScroll();
if(mScroller.computeScrollOffset()){
((View) getParent()).scrollTo(mScroller.getCurrX(),mScroller.getCurrY());
//通过不断的重绘不断的调用computeScroll方法
invalidate();
}
}
我们在computeScroll()方法中通过Scroller来获取当前的ScrollX和ScrollY然后调用scrollTo()方法来进行View的滑动,接着调用invalidate方法来让View进行重绘,重绘就会调用computeScroll()方法来实现View的滑动。这样我们就通过不断的移动一个小的距离并连贯起来就实现了平滑移动的效果。但是在Scroller中我们如何能获取当前的位置的ScrollX和ScrollY呢?我们忘了一点就是在调用scrollTo()方法前会调用Scroller的computeScrollOffset()方法,接下来我们就来看看computeScrollOffset()方法。
3.Scroller的computeScrollOffset方法
public boolean computeScrollOffset() {
if (mFinished) {
return false;
}
//动画持续的时间
int timePassed = (int)(AnimationUtils.currentAnimationTimeMillis() - mStartTime);
if (timePassed < mDuration) {
switch (mMode) {
case SCROLL_MODE:
final float x = mInterpolator.getInterpolation(timePassed * mDurationReciprocal);
//根据插值器来算出timePassed这段时间移动的距离
mCurrX = mStartX + Math.round(x * mDeltaX);
mCurrY = mStartY + Math.round(x * mDeltaY);
break;
case FLING_MODE:
final float t = (float) timePassed / mDuration;
final int index = (int) (NB_SAMPLES * t);
float distanceCoef = .f;
float velocityCoef = .f;
if (index < NB_SAMPLES) {
final float t_inf = (float) index / NB_SAMPLES;
final float t_sup = (float) (index + ) / NB_SAMPLES;
final float d_inf = SPLINE_POSITION[index];
final float d_sup = SPLINE_POSITION[index + ];
velocityCoef = (d_sup - d_inf) / (t_sup - t_inf);
distanceCoef = d_inf + (t - t_inf) * velocityCoef;
}
mCurrVelocity = velocityCoef * mDistance / mDuration * 1000.0f;
mCurrX = mStartX + Math.round(distanceCoef * (mFinalX - mStartX));
// Pin to mMinX <= mCurrX <= mMaxX
mCurrX = Math.min(mCurrX, mMaxX);
mCurrX = Math.max(mCurrX, mMinX);
mCurrY = mStartY + Math.round(distanceCoef * (mFinalY - mStartY));
// Pin to mMinY <= mCurrY <= mMaxY
mCurrY = Math.min(mCurrY, mMaxY);
mCurrY = Math.max(mCurrY, mMinY);
if (mCurrX == mFinalX && mCurrY == mFinalY) {
mFinished = true;
}
break;
}
}
else {
mCurrX = mFinalX;
mCurrY = mFinalY;
mFinished = true;
}
return true;
}
首先会计算动画持续的时间timePassed,如果动画持续时间小于我们设置的滑动持续时间mDuration,则执行Swich语句,因为在startScroll()方法中mMode为SCROLL_MODE所以执行分支语句SCROLL_MODE,然后根据插值器Interpolator来计算出在该时间段里面移动的距离,赋值给mCurrX和mCurrY,这样我们就能通过Scroller来获取当前的ScrollX和ScrollY了。另外,computeScrollOffset()的返回值如果为true则表示滑动未结束,false则表示滑动结束,所以如果滑动未结束我们就得持续的调用scrollTo()方法和invalidate()方法来进行View的滑动。
Android View体系(四)从源码解析Scroller的更多相关文章
- Android Handler机制(四)---Handler源码解析
Handler的主要用途有两个:(1).在将来的某个时刻执行消息或一个runnable,(2)把消息发送到消息队列. 主要依靠post(Runnable).postAtTime(Runnable, l ...
- Android View 事件分发机制 源码解析 (上)
一直想写事件分发机制的文章,不管咋样,也得自己研究下事件分发的源码,写出心得~ 首先我们先写个简单的例子来测试View的事件转发的流程~ 1.案例 为了更好的研究View的事件转发,我们自定以一个My ...
- Android IntentService使用介绍以及源码解析
版权声明:本文出自汪磊的博客,转载请务必注明出处. 一.IntentService概述及使用举例 IntentService内部实现机制用到了HandlerThread,如果对HandlerThrea ...
- 【Android应用开发】EasyDialog 源码解析
示例源码下载 : http://download.csdn.net/detail/han1202012/9115227 EasyDialog 简介 : -- 作用 : 用于在界面进行一些介绍, 说明; ...
- Android View事件分发-从源码分析
View事件分发-从源码分析 学习自 <Android开发艺术探索> https://blog.csdn.net/qian520ao/article/details/78555397?lo ...
- Android Handler机制(三)----Looper源码解析
一.Looper Looper对象,顾名思义,直译过来就是循环的意思,从MessageQueue中不断取出message. Class used to run a message loop for a ...
- Android Handler机制(二)---MessageQueue源码解析
MessageQueue 1.变量 private final boolean mQuitAllowed;//表示MessageQueue是否允许退出 @SuppressWarnings(" ...
- Android事件总线(四)源码解析otto
前言 上一篇文章中讲到了otto的用法,这一篇我们来讲一下otto的源码.可能有人觉得otto过时了,但是通过源码我们学习的是高手设计otto时的设计理念,这种设计理念是不过时的. otto各个类的作 ...
- Android FM 模块学习之四 源码解析(1)
Normal 0 7.8 磅 0 2 false false false EN-US ZH-CN X-NONE MicrosoftInternetExplorer4 前一章我们了解了FM手动调频,接下 ...
随机推荐
- Swift5 语言指南(一) 关于Swift
Swift是编写软件的绝佳方式,无论是用于手机,台式机,服务器还是其他任何运行代码的软件.它是一种安全,快速,交互式的编程语言,它将现代语言思维的最佳结合与来自更广泛的Apple工程文化的智慧和来自开 ...
- log4j 详细使用教程【转载】
日志是应用软件中不可缺少的部分,Apache的开源项目log4j是一个功能强大的日志组件,提供方便的日志记录.在apache网站:jakarta.apache.org/log4j 可以免费下载到Log ...
- jmeter获取cookies信息(配置)
jmeter发送请求后,响应信息里获取不到cookies(实际上会返回一个cookies),解决方法: 在jmeter.properties里找到CookieManager.save.cookies, ...
- Linux - 快速进入目录的方法
cd命令技巧 直接进入用户的home目录: cd ~ 进入上一个目录: cd - 进入当前目录的上一层目录: cd .. 进入当前目录的上两层目录: cd ../.. 其他常用方法 利用tab键,自动 ...
- shell脚本中一些特殊变量
在shell脚本中,一些常见的特殊变量表示方式还是需要知道的 如下就是一些经常用到的特殊变量表示方法: $0 当前脚本名$1 $2... 传入脚本or函数的参数(大于10需大括号括起来)$ ...
- an error occurred attempting install_Github_for_windows_无法安装的解决方法_
都在这了,作者原创.我就截图好了.
- SpringBoot集成Redis
1.引入 spring-boot-starter-redis <dependency> <groupId>redis.clients</groupId> <a ...
- Spring Boot初识(3)- Spring Boot整合Swagger
一.本文介绍 如果Web项目是完全前后端分离的话(我认为现在完全前后端分离已经是趋势了)一般前端和后端交互都是通过接口的,对接口入参和出参描述的文档就是Mock文档.随着接口数量的增多和参数的个数增加 ...
- 【P2P浏览器】跟大家讨论一个想法
内容分享者通过这个浏览器来创建自己待分享的内容 任谁看了这些内容,就会在本地保存一份,成为一个节点只要任一个节点在线,别人就能看这些内容在线的节点越多,获取这些内容的速度越快 任何使用这个浏览器的人, ...
- 设计模式教程(Design Patterns Tutorial)笔记之三 行为型模式(Behavioral Patterns)
目录 · Strategy · When to use the Strategy Design Pattern? · Sample Code · Observer · When to use the ...