你的位置:首页 > 信息动态 > 新闻中心
信息动态
联系我们

Android的Scroller介绍,flutter项目实战

2021/12/27 13:15:02

参数

finished    新的结束值

public final int getCurrX ()

返回当前滚动X方向的偏移

返回值

距离原点X方向的绝对值

public final int getCurrY ()

返回当前滚动Y方向的偏移

返回值

距离原点Y方向的绝对值

public final int getDuration ()

返回滚动事件的持续时间,以毫秒计算。

返回值

滚动持续的毫秒数

public final int getFinalX ()

返回滚动结束位置。仅针对“fling”手势有效

返回值

最终位置X方向距离原点的绝对距离

public final int getFinalY ()

返回滚动结束位置。仅针对“fling”操作有效

返回值

最终位置Y方向距离原点的绝对距离

public final int getStartX ()

返回滚动起始点的X方向的偏移

返回值

起始点在X方向距离原点的绝对距离

public final int getStartY ()

返回滚动起始点的Y方向的偏移

返回值

起始点在Y方向距离原点的绝对距离

public final boolean isFinished ()

返回scroller是否已完成滚动。

返回值

停止滚动返回true,否则返回false

public void setFinalX (int newX)

设置scroller的X方向终止位置

参数

newX    新位置在X方向距离原点的绝对偏移。

参见

extendDuration(int)

setFinalY(int)

public void setFinalY (int newY)

设置scroller的Y方向终止位置

参数

newY    新位置在Y方向距离原点的绝对偏移。

参见

extendDuration(int)

setFinalY(int)

public void startScroll (int startX, int startY, int dx, int dy)

以提供的起始点和将要滑动的距离开始滚动。滚动会使用缺省值250ms作为持续时间。

参数

startX 水平方向滚动的偏移值,以像素为单位。负值表明滚动将向左滚动

startY 垂直方向滚动的偏移值,以像素为单位。负值表明滚动将向上滚动

dx 水平方向滑动的距离,负值会使滚动向左滚动

dy 垂直方向滑动的距离,负值会使滚动向上滚动

public void startScroll (int startX, int startY, int dx, int dy, int duration)

以提供的起始点和将要滑动的距离开始滚动。

参数

startX 水平方向滚动的偏移值,以像素为单位。负值表明滚动将向左滚动

startY 垂直方向滚动的偏移值,以像素为单位。负值表明滚动将向上滚动

dx 水平方向滑动的距离,负值会使滚动向左滚动

dy 垂直方向滑动的距离,负值会使滚动向上滚动

duration    滚动持续时间,以毫秒计。

public int timePassed ()

返回自滚动开始经过的时间

返回值

经过时间以毫秒为单位

五、补充

文章精选

Scroller 粗浅理解

ScrollTextView - scrolling TextView for Android

Android里Scroller类是为了实现View平滑滚动的一个Helper类。通常在自定义的View时使用,在View中定义一个私有成员mScroller = new Scroller(context)。设置mScroller滚动的位置时,并不会导致View的滚动,通常是用mScroller记录/计算View滚动的位置,再重写View的computeScroll(),完成实际的滚动。

相关API介绍如下

Java代码  收藏代码

  1. mScroller.getCurrX() //获取mScroller当前水平滚动的位置

  2. mScroller.getCurrY() //获取mScroller当前竖直滚动的位置

  3. mScroller.getFinalX() //获取mScroller最终停止的水平位置

  4. mScroller.getFinalY() //获取mScroller最终停止的竖直位置

  5. mScroller.setFinalX(int newX) //设置mScroller最终停留的水平位置,没有动画效果,直接跳到目标位置

  6. mScroller.setFinalY(int newY) //设置mScroller最终停留的竖直位置,没有动画效果,直接跳到目标位置

  7. //滚动,startX, startY为开始滚动的位置,dx,dy为滚动的偏移量, duration为完成滚动的时间

  8. mScroller.startScroll(int startX, int startY, int dx, int dy) //使用默认完成时间250ms

  9. mScroller.startScroll(int startX, int startY, int dx, int dy, int duration)

  10. mScroller.computeScrollOffset() //返回值为boolean,true说明滚动尚未完成,false说明滚动已经完成。这是一个很重要的方法,通常放在View.computeScroll()中,用来判断是否滚动是否结束。

举例说明,自定义一个CustomView,使用Scroller实现滚动:

Java代码  收藏代码

  1. import android.content.Context;

  2. import android.util.AttributeSet;

  3. import android.util.Log;

  4. import android.view.View;

  5. import android.widget.LinearLayout;

  6. import android.widget.Scroller;

  7. public class CustomView extends LinearLayout {

  8. private static final String TAG = “Scroller”;

  9. private Scroller mScroller;

  10. public CustomView(Context context, AttributeSet attrs) {

  11. super(context, attrs);

  12. mScroller = new Scroller(context);

  13. }

  14. //调用此方法滚动到目标位置

  15. public void smoothScrollTo(int fx, int fy) {

  16. int dx = fx - mScroller.getFinalX();

  17. int dy = fy - mScroller.getFinalY();

  18. smoothScrollBy(dx, dy);

  19. }

  20. //调用此方法设置滚动的相对偏移

  21. public void smoothScrollBy(int dx, int dy) {

  22. //设置mScroller的滚动偏移量

  23. mScroller.startScroll(mScroller.getFinalX(), mScroller.getFinalY(), dx, dy);

  24. invalidate();//这里必须调用invalidate()才能保证computeScroll()会被调用,否则不一定会刷新界面,看不到滚动效果

  25. }

  26. @Override

  27. public void computeScroll() {

  28. //先判断mScroller滚动是否完成

  29. if (mScroller.computeScrollOffset()) {

  30. //这里调用View的scrollTo()完成实际的滚动

  31. scrollTo(mScroller.getCurrX(), mScroller.getCurrY());

  32. //必须调用该方法,否则不一定能看到滚动效果

  33. postInvalidate();

  34. }

  35. super.computeScroll();

  36. }

  37. }

  • Demo的下载地址: http://download.csdn.net/detail/linghu_java/5423269

下面更深一点介绍Scrooler:

友情提示:

在继续往下面读之前,希望您对以下知识点有一定程度掌握,否则,继续看下去对您意义也不大。

1、掌握View(视图)的"视图坐标"以及"布局坐标",以及scrollTo()和scrollBy()方法的作用 ----- 必须理解

如果对这方面知识不太清楚的话,建议先看看我的这篇博客

<Android中滑屏初探 ---- scrollTo 以及 scrollBy方法使用说明>,

不夸张地说,这篇博客理论上来说是我们这篇博文的基础。

2、知道onInterceptTouchEvent()以及onTouchEvent()对触摸事件的分发流程         ---- 不是必须

3、知道怎么绘制自定义ViewGroup即可                                        ---- 不是必须

OK。 继续往下看,请一定有所准备 。大家跟着我一步一步来咯。

知识点一:  关于scrollTo()和scrollBy()以及偏移坐标的设置/取值问题

在前面一篇博文中《Android中滑屏初探 ---- scrollTo 以及 scrollBy方法使用说明》,我们掌握了scrollTo()和

scrollBy()方法的作用,这两个方法的主要作用是将View/ViewGroup移至指定的坐标中,并且将偏移量保存起来。另外:

mScrollX 代表X轴方向的偏移坐标

mScrollY 代表Y轴方向的偏移坐标

关于偏移量的设置我们可以参看下源码:

[java]  view plain copy print ?

  1. package com.qin.customviewgroup;

  2. public class View {

  3. protected int mScrollX;   //该视图内容相当于视图起始坐标的偏移量   , X轴 方向

  4. protected int mScrollY;   //该视图内容相当于视图起始坐标的偏移量   , Y轴方向

  5. //返回值

  6. public final int getScrollX() {

  7. return mScrollX;

  8. }

  9. public final int getScrollY() {

  10. return mScrollY;

  11. }

  12. public void scrollTo(int x, int y) {

  13. //偏移位置发生了改变

  14. if (mScrollX != x || mScrollY != y) {

  15. int oldX = mScrollX;

  16. int oldY = mScrollY;

  17. mScrollX = x;  //赋新值,保存当前便宜量

  18. mScrollY = y;

  19. //回调onScrollChanged方法

  20. onScrollChanged(mScrollX, mScrollY, oldX, oldY);

  21. if (!awakenScrollBars()) {

  22. invalidate();  //一般都引起重绘

  23. }

  24. }

  25. }

  26. // 看出原因了吧 。。 mScrollX 与 mScrollY 代表我们当前偏移的位置 , 在当前位置继续偏移(x ,y)个单位

  27. public void scrollBy(int x, int y) {

  28. scrollTo(mScrollX + x, mScrollY + y);

  29. }

  30. //…

  31. }

于是,在任何时刻我们都可以获取该View/ViewGroup的偏移位置了,即调用getScrollX()方法和getScrollY()方法

知识点二: Scroller类的介绍


在初次看Launcher滑屏的时候,我就对Scroller类的学习感到非常蛋疼,完全失去了继续研究的欲望。如今,没得办法,

得重新看Launcher模块,基本上将Launcher大部分类以及功能给掌握了。当然,也花了一天时间来学习Launcher里的滑屏实现

,基本上业是拨开云雾见真知了。

我们知道想把一个View偏移至指定坐标(x,y)处,利用scrollTo()方法直接调用就OK了,但我们不能忽视的是,该方法本身

来的的副作用:非常迅速的将View/ViewGroup偏移至目标点,而没有对这个偏移过程有任何控制,对用户而言可能是不太

友好的。于是,基于这种偏移控制,Scroller类被设计出来了,该类的主要作用是为偏移过程制定一定的控制流程(后面我们会

知道的更多),从而使偏移更流畅,更完美。

可能上面说的比较悬乎,道理也没有讲透。下面我就根据特定情景帮助大家分析下:

情景: 从上海如何到武汉?

普通的人可能会想,so easy : 飞机、轮船、11路公交车…

文艺的人可能会想,  小 case : 时空忍术(火影的招数)、翻个筋斗(孙大圣的招数)…

不管怎么样,我们想出来的套路可能有两种:

1、有个时间控制过程才能抵达(缓慢的前进)                              -----     对应于Scroller的作用

假设做火车,这个过程可能包括: 火车速率,花费周期等;

2、瞬间抵达(超神太快了,都眩晕了,用户体验不太好)                     ------   对应于scrollTo()的作用

模拟Scroller类的实现功能:

假设从上海做动车到武汉需要10个小时,行进距离为1000km ,火车速率200/h 。采用第一种时间控制方法到达武汉的

整个配合过程可能如下:

我们每隔一段时间(例如1小时),计算火车应该行进的距离,然后调用scrollTo()方法,行进至该处。10小时过完后,

我们也就达到了目的地了。

相信大家心里应该有个感觉了。我们就分析下源码里去看看Scroller类的相关方法.

其源代码(部分)如下: 路径位于 \frameworks\base\core\java\android\widget\Scroller.java

[java]  view plain copy print ?

  1. public class Scroller  {

  2. private int mStartX;    //起始坐标点 ,  X轴方向

  3. private int mStartY;    //起始坐标点 ,  Y轴方向

  4. private int mCurrX;     //当前坐标点  X轴, 即调用startScroll函数后,经过一定时间所达到的值

  5. private int mCurrY;     //当前坐标点  Y轴, 即调用startScroll函数后,经过一定时间所达到的值

  6. private float mDeltaX;  //应该继续滑动的距离, X轴方向

  7. private float mDeltaY;  //应该继续滑动的距离, Y轴方向

  8. private boolean mFinished;  //是否已经完成本次滑动操作, 如果完成则为 true

  9. //构造函数

  10. public Scroller(Context context) {

  11. this(context, null);

  12. }

  13. public final boolean isFinished() {

  14. return mFinished;

  15. }

  16. //强制结束本次滑屏操作

  17. public final void forceFinished(boolean finished) {

  18. mFinished = finished;

  19. }

  20. public final int getCurrX() {

  21. return mCurrX;

  22. }

  23. /* Call this when you want to know the new location.  If it returns true,

  24. * the animation is not yet finished.  loc will be altered to provide the

  25. * new location. */

  26. //根据当前已经消逝的时间计算当前的坐标点,保存在mCurrX和mCurrY值中

  27. public boolean computeScrollOffset() {

  28. if (mFinished) {  //已经完成了本次动画控制,直接返回为fal

《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》

【docs.qq.com/doc/DSkNLaERkbnFoS0ZF】 完整内容开源分享

se

  1. return false;

  2. }

  3. int timePassed = (int)(AnimationUtils.currentAnimationTimeMillis() - mStartTime);

  4. if (timePassed < mDuration) {

  5. switch (mMode) {

  6. case SCROLL_MODE:

  7. float x = (float)timePassed * mDurationReciprocal;

  8. mCurrX = mStartX + Math.round(x * mDeltaX);

  9. mCurrY = mStartY + Math.round(x * mDeltaY);

  10. break;

  11. }

  12. else {

  13. mCurrX = mFinalX;

  14. mCurrY = mFinalY;

  15. mFinished = true;

  16. }

  17. return true;

  18. }

  19. //开始一个动画控制,由(startX , startY)在duration时间内前进(dx,dy)个单位,即到达坐标为(startX+dx , startY+dy)出

  20. public void startScroll(int startX, int startY, int dx, int dy, int duration) {

  21. mFinished = false;

  22. mDuration = duration;

  23. mStartTime = AnimationUtils.currentAnimationTimeMillis();

  24. mStartX = startX;       mStartY = startY;

  25. mFinalX = startX + dx;  mFinalY = startY + dy;

  26. mDeltaX = dx;            mDeltaY = dy;

  27. }

  28. }

其中比较重要的两个方法为:

public void startScroll(int startX, int startY, int dx, int dy, int duration)

函数功能说明:根据当前已经消逝的时间计算当前的坐标点,保存在mCurrX和mCurrY值中

public void startScroll(int startX, int startY, int dx, int dy, int duration)

函数功能说明:开始一个动画控制,由(startX , startY)在duration时间内前进(dx,dy)个单位,到达坐标为

(startX+dx , startY+dy)处。

PS : 强烈建议大家看看该类的源码,便于后续理解。

知识点二: computeScroll()方法介绍

为了易于控制滑屏控制,Android框架提供了 computeScroll()方法去控制这个流程。在绘制View时,会在draw()过程调用该

方法。因此, 再配合使用Scroller实例,我们就可以获得当前应该的偏移坐标,手动使View/ViewGroup偏移至该处。

computeScroll()方法原型如下,该方法位于ViewGroup.java类中

[java]  view plain copy print ?

  1. /**

  2. * Called by a parent to request that a child update its values for mScrollX

  3. * and mScrollY if necessary. This will typically be done if the child is

  4. * animating a scroll using a {@link android.widget.Scroller Scroller}

  5. * object.

  6. */由父视图调用用来请求子视图根据偏移值 mScrollX,mScrollY重新绘制

  7. public void computeScroll() { //空方法 ,自定义ViewGroup必须实现方法体

  8. }

为了实现偏移控制,一般自定义View/ViewGroup都需要重载该方法 。

其调用过程位于View绘制流程draw()过程中,如下:

[java]  view plain copy print ?

  1. @Override

  2. protected void dispatchDraw(Canvas canvas){

  3. for (int i = 0; i < count; i++) {

  4. final View child = children[getChildDrawingOrder(count, i)];

  5. if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {

  6. more |= drawChild(canvas, child, drawingTime);

  7. }

  8. }

  9. }

  10. protected boolean drawChild(Canvas canvas, View child, long drawingTime) {

  11. child.computeScroll();

  12. }

Demo说明:

我们简单的复用了之前写的一个自定义ViewGroup,与以前一次有区别的是,我们没有调用scrollTo()方法去进行瞬间

偏移。 本次做法如下:

第一、调用Scroller实例去产生一个偏移控制(对应于startScroll()方法)

第二、手动调用invalid()方法去重新绘制,剩下的就是在 computeScroll()里根据当前已经逝去的时间,获取当前

应该偏移的坐标(由Scroller实例对应的computeScrollOffset()计算而得),

第三、当前应该偏移的坐标,调用scrollBy()方法去缓慢移动至该坐标处。

截图如下:

                         

原始界面                                     点击按钮或者触摸屏之后的显示界面

附:由于滑动截屏很难,只是简单的截取了两个个静态图片,触摸的话可以实现左右滑动切屏了。

更多知识点,请看代码注释。。

[java]  view plain copy print ?

  1. //自定义ViewGroup , 包含了三个LinearLayout控件,存放在不同的布局位置,通过scrollBy或者scrollTo方法切换

  2. public class MultiViewGroup extends ViewGroup {

  3. //startScroll开始移至下一屏

  4. public void startMove(){

  5. curScreen ++ ;

  6. Log.i(TAG, "----startMove---- curScreen " + curScreen);

  7. //使用动画控制偏移过程 , 3s内到位

  8. mScroller.startScroll((curScreen-1) * getWidth(), 0, getWidth(), 0,3000);

  9. //其实点击按钮的时候,系统会自动重新绘制View,我们还是手动加上吧。

  10. invalidate();

  11. //使用scrollTo一步到位

  12. //scrollTo(curScreen * MultiScreenActivity.screenWidth, 0);

  13. }

  14. // 由父视图调用用来请求子视图根据偏移值 mScrollX,mScrollY重新绘制

  15. @Override

  16. public void computeScroll() {

  17. // TODO Auto-generated method stub

  18. Log.e(TAG, “computeScroll”);

  19. // 如果返回true,表示动画还没有结束

  20. // 因为前面startScroll,所以只有在startScroll完成时 才会为false

  21. if (mScroller.computeScrollOffset()) {

  22. Log.e(TAG, mScroller.getCurrX() + “======” + mScroller.getCurrY());

  23. // 产生了动画效果,根据当前值 每次滚动一点

  24. scrollTo(mScroller.getCurrX(), mScroller.getCurrY());

  25. Log.e(TAG, "### getleft is " + getLeft() + " ### getRight is " + getRight());

  26. //此时同样也需要刷新View ,否则效果可能有误差

  27. postInvalidate();

  28. }

  29. else

  30. Log.i(TAG, “have done the scoller -----”);

  31. }

  32. //马上停止移动,如果已经超过了下一屏的一半,我们强制滑到下一个屏幕

  33. public void stopMove(){

  34. Log.v(TAG, “----stopMove ----”);

  35. if(mScroller != null){

  36. //如果动画还没结束,我们就按下了结束的按钮,那我们就结束该动画,即马上滑动指定位置

  37. if(!mScroller.isFinished()){

  38. int scrollCurX= mScroller.getCurrX() ;

  39. //判断是否超过下一屏的中间位置,如果达到就抵达下一屏,否则保持在原屏幕

  40. // 这样的一个简单公式意思是:假设当前滑屏偏移值即 scrollCurX 加上每个屏幕一半的宽度,除以每个屏幕的宽度就是

  41. //  我们目标屏所在位置了。 假如每个屏幕宽度为320dip, 我们滑到了500dip处,很显然我们应该到达第二屏

  42. //即(500 + 320/2)/320 = 2

  43. int descScreen = ( scrollCurX + getWidth() / 2) / getWidth() ;

  44. Log.i(TAG, “-mScroller.is not finished scrollCurX +” + scrollCurX);

  45. Log.i(TAG, “-mScroller.is not finished descScreen +” + descScreen);

  46. mScroller.abortAnimation();

  47. //停止了动画,我们马上滑倒目标位置

  48. scrollTo(descScreen *getWidth() , 0);

  49. curScreen = descScreen ; //纠正目标屏位置

  50. }

  51. else

  52. Log.i(TAG, "----OK mScroller.is  finished ---- ");

  53. }

  54. }

  55. }

如何实现触摸滑屏?

===========

其实网上有很多关于Launcher实现滑屏的博文,基本上也把道理阐释的比较明白了 。我这儿也是基于自己的理解,将一些

重要方面的知识点给补充下,希望能帮助大家理解。

想要实现滑屏操作,值得考虑的事情包括如下几个方面:

其中:onInterceptTouchEvent()主要功能是控制触摸事件的分发,例如是子视图的点击事件还是滑动事件。

其他所有处理过程均在onTouchEvent()方法里实现了。

1、屏幕的滑动要根据手指的移动而移动  ---- 主要实现在onTouchEvent()方法中

宽度,除以每个屏幕的宽度就是

  1. //  我们目标屏所在位置了。 假如每个屏幕宽度为320dip, 我们滑到了500dip处,很显然我们应该到达第二屏

  2. //即(500 + 320/2)/320 = 2

  3. int descScreen = ( scrollCurX + getWidth() / 2) / getWidth() ;

  4. Log.i(TAG, “-mScroller.is not finished scrollCurX +” + scrollCurX);

  5. Log.i(TAG, “-mScroller.is not finished descScreen +” + descScreen);

  6. mScroller.abortAnimation();

  7. //停止了动画,我们马上滑倒目标位置

  8. scrollTo(descScreen *getWidth() , 0);

  9. curScreen = descScreen ; //纠正目标屏位置

  10. }

  11. else

  12. Log.i(TAG, "----OK mScroller.is  finished ---- ");

  13. }

  14. }

  15. }

如何实现触摸滑屏?

===========

其实网上有很多关于Launcher实现滑屏的博文,基本上也把道理阐释的比较明白了 。我这儿也是基于自己的理解,将一些

重要方面的知识点给补充下,希望能帮助大家理解。

想要实现滑屏操作,值得考虑的事情包括如下几个方面:

其中:onInterceptTouchEvent()主要功能是控制触摸事件的分发,例如是子视图的点击事件还是滑动事件。

其他所有处理过程均在onTouchEvent()方法里实现了。

1、屏幕的滑动要根据手指的移动而移动  ---- 主要实现在onTouchEvent()方法中