简介

android.widget.Scroller是用于模拟scrolling行为,它是scrolling行为的一个帮助类。我们通常通过它的 startScroll 函数来设置一个 scrolling 行为模型,即在 duration 毫秒时间内从 int startX, int startY 这个点起向X和Y方向分别滚动 int dx 和 int dy 个像素;或者通过它的 fling 函数来设置一个 fling 行为模型(特殊的scroll),即在 int startX, int startY 这个点起向X和Y方向分别以 int velocityX 和  int velocityY 个像素的速度进行滚动。然后我们可以调用 computeScrollOffset 方法计算此时scroll到的位置,并调用 getCurrX 和 getCurrY 得到到此时在X和Y方向的位置。


构造函数

  • Scroller(Context context):Create a Scroller with the default duration and interpolator.
  • Scroller(Context context, Interpolator interpolator):interpolator参数只是在computeScrollOffset()中用于对我们的流逝的时间(通过timePassed()取得)这值进行重新解析。If the interpolator is null, the default (viscous) interpolator will be used. "Flywheel" behavior will be in effect for apps targeting Honeycomb or newer.
  • Scroller(Context context, Interpolator interpolator, boolean flywheel):Specify指定 whether or not to support progressive "flywheel" behavior in flinging.

公共函数

  • void abortAnimation()  停止scroll。Stops the animation.
  • boolean computeScrollOffset():计算scroll的情况,判断滚动操作是否已经完成了。Call this when you want to know the new location. If it returns true,the animation is not yet finished.
  • void extendDuration(int extend):增加scroll的时间。Extend the scroll animation. This allows a running animation to scroll further and longer, when used with setFinalX(int) orsetFinalY(int).
  • void fling(int startX, int startY, int velocityX, int velocityY, int minX, int maxX, int minY, int maxY):模拟fling形式的scroll行为。int startX, int startY 代表起点,int velocityX, int velocityY代表初速度,int minX, int maxX, int minY, int maxY代表终点的范围
  • final void forceFinished(boolean finished):强制设置为scroll状态。Force the finished field to a particular特定的 value.
  • float getCurrVelocity():得到当前速度。该值是X方向和Y方向的合成值。Returns the current velocity. Return the original velocity less the deceleration减速. Result may be negative负值.
  • final int getCurrX():得到当前的X坐标。Returns the current X offset in the scroll.
  • final int getDuration():得到设置的scroll行为的总时间值。Returns how long the scroll event will take, in milliseconds.
  • final int getFinalX():得到scroll行为终点的X值。Returns where the scroll will end.
  • void setFinalX(int newX):设置scroll行为的终点的X值。Sets the final position (X) for this scroller.
  • final int getStartX():得到scroll行为起点的X值。Returns the start X offset in the scroll.
  • final boolean isFinished():返回scroll行为是否结束。Returns whether the scroller has finished scrolling.True if the scroller has finished scrolling, false otherwise.
  • boolean isScrollingInDirection(float xvel, float yvel):这是一个public类型的hide方法
  • final void setFriction(float friction):设置摩擦力。The amount of friction applied to flings. The default value is ViewConfiguration的getScrollFriction.
  • void startScroll(int startX, int startY, int dx, int dy):设置一个scrolling行为模型,即在默认时间(250毫秒)内从int startX, int startY 这个点起向X和Y方向分别滚动 int dx 和 int dy 个像素。Start scrolling by providing a starting point and the distance距离 to travel.
  • void startScroll(int startX, int startY, int dx, int dy, int duration):指定时间
  • int timePassed():从scroll开始到现在已经过去的时间。其值为 AnimationUtils.currentAnimationTimeMillis() - mStartTime。Returns the time elapsed消逝、过去 since the beginning of the scrolling.

View与Scroller的关系

Scroller是一个封装位置和速度等信息的变量,startScroll()函数只是对它的一些成员变量做一些设置,这个设置的唯一效果就是导致mScroller.computeScrollOffset()返回true,而并不会导致View的滚动。

这里大家可能就会有一个疑问,既然startScroll()只是虚晃一枪,那scroll的动态效果到底是谁触发的呢?

其实是invalidate方法,或导致invalidate方法被调用的其他操作。
当View重绘时(即因故调用了其invalidate方法),若此时mScroller.startScroll方法被调用了,那么mScroller将会存在一些有效的设置。然后当整个View系统自上而下重新绘制过程中调用到drawChild时,也会使得View的computeScroll函数被触发,如果我们在computeScroll方法中对View进行了移动(调用其自身的scrollTo、scrollBy方法),将会导致系统不断的重绘,直到startScroll的mScroller.computeScrollOffset()返回false才停下来。

所以,我们可以在父容器重画自己孩子过程中,在调用孩子的computScroll方法时,在这个方法里去获取事先设置好的成员变量mScroller中的位置、时间、加速度等信息,用这些参数来做我们想做的事情。

我们再看下View中computeScroll方法的默认实现:
/**
* Called by a parent to request that a child update its values for mScrollX and mScrollY if necessary.
* This will typically通常 be done if the child is animating a scroll在滚动 using a Scroller object.
*/
public void computeScroll() {
}
可以看到,View的computeScroll是一个空函数,很明显我们需要去实现它,至于做什么,就由我们自己来决定了。

示例代码

/**
 * 自定义模仿ViewPage,只实现其最基本的滑动平滑切换item的功能
 * @author 白乾涛
 */
public class ScrollerLayout extends ViewGroup {
    private Scroller mScroller = new Scroller(getContext(), new AccelerateInterpolator());//可以指定加速器
    private int mTouchSlop = 20;//判定为拖动的最小移动像素数
    private float mXDown;//手指按下时的屏幕坐标
    private float mLastX;//【上次】触发ACTION_MOVE事件时的屏幕坐标
    private int leftBorder;//界面可滚动的左边界
    private int rightBorder;//界面可滚动的右边界
    public ScrollerLayout(Context context) {
        this(context, null);
    }
    public ScrollerLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int childCount = getChildCount();
        for (int i = 0; i < childCount; i++) {
            measureChild(getChildAt(i), widthMeasureSpec, heightMeasureSpec);// 为ScrollerLayout中的每一个子控件测量大小
        }
    }
    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        if (changed) {
            int childCount = getChildCount();
            for (int i = 0; i < childCount; i++) {
                View childView = getChildAt(i);
                int left = i * childView.getMeasuredWidth();
                int right = (i + 1) * childView.getMeasuredWidth();
                childView.layout(left, 0, right, childView.getMeasuredHeight());// 为ScrollerLayout中的每一个子控件在水平方向上进行布局
            }
            // 初始化左右边界值
            leftBorder = getChildAt(0).getLeft();
            rightBorder = getChildAt(getChildCount() - 1).getRight();
        }
    }
    @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
        Log.i("bqt", "onInterceptTouchEvent" + "--" + mLastX);
        switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            mXDown = event.getRawX();
            break;
        case MotionEvent.ACTION_MOVE:
            // 当手指拖动值大于TouchSlop时,认为应该进行滚动,拦截子控件的事件,之后就会将事件交给自己的onTouchEvent()方法来处理
            if (Math.abs(event.getRawX() - mXDown) > mTouchSlop) {
                mLastX = event.getRawX();//记录拦截事件之前、即调用自己的onTouchEvent()方法之前的触摸点的位置
                return true;
            }
            break;
        }
        return super.onInterceptTouchEvent(event);
    }
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.i("bqt", "onTouchEvent" + "--" + mLastX);
        switch (event.getAction()) {
        //1、在拖动过程中,使用View自身的scrollTo和scrollBy方法滚动自身的【内容】
        //使用scrollTo和scrollBy滑动的过程本身并非是平滑的,但是由于ACTION_MOVE事件比较密集,所以看起来像是平滑的一样
        case MotionEvent.ACTION_MOVE:
            float scrolledX = mLastX - event.getRawX();//上一次回调与此次回调期间,用户拖动了多少距离
            if (getScrollX() + scrolledX < leftBorder) scrollTo(leftBorder, 0);//scrollTo()是让View相对于初始的位置滚动某段距离,【左上】滚动为正
            else if (getScrollX() + getWidth() + scrolledX > rightBorder) scrollTo(rightBorder - getWidth(), 0);
            else scrollBy((int) scrolledX, 0);//scrollBy()是让View相对于当前的位置滚动某段距离。用户拖动了多少这里就scrollBy多少
            mLastX = event.getRawX();
            break;
        //2、当手指抬起时,使用Scroller的startScroll方法【初始化滚动数据】。注意调用Scroller的startScroll方法本身并不会导致View的滑动!
        case MotionEvent.ACTION_UP:
            int targetIndex = (getScrollX() + getWidth() / 2) / getWidth();
            int dx = targetIndex * getWidth() - getScrollX();
            mScroller.startScroll(getScrollX(), 0, dx, 0, 100);//参数:滚动开始时X/Y坐标,横/纵向滚动的距离,滚动时间,【左上】滚动为正
            invalidate();//一定要在startScroll后刷新才可以
            break;
        }
        return super.onTouchEvent(event);
    }
    @Override
    //3、重写View的computeScroll()方法,并在其内部完成平滑滚动的逻辑。在整个后续的平滑滚动过程中,此方法是会一直被调用的
    public void computeScroll() {
        if (mScroller.computeScrollOffset()) {//判断滚动操作是否已经完成了
            scrollTo(mScroller.getCurrX(), mScroller.getCurrY());//把Scroller的currX和currY坐标传入。由此可以看出,Scroller只是提供数据用的
            invalidate();
        }
    }

}


【Scroller】scrollTo scrollBy startScroll computeScroll 自定义ViewPage 简介 示例的更多相关文章

  1. Android scrollTo() scrollBy() Scroller解说及应用

    版本号:1.0  日期:2014.6.17  2014.6.18 版权:© 2014 kince 转载注明出处   scrollTo() .scrollBy()及 Scroller在视图滑动中常常使用 ...

  2. 图解Android View的scrollTo(),scrollBy(),getScrollX(), getScrollY()

    https://blog.csdn.net/bigconvience/article/details/26697645 Android系统手机屏幕的左上角为坐标系,同时y轴方向与笛卡尔坐标系的y轴方向 ...

  3. View:Android View的scrollTo(),scrollBy(),getScrollX(), getScrollY()的理解

    Android系统手机屏幕的左上角为坐标系,同时y轴方向与笛卡尔坐标系的y轴方向想反.提供了 getLeft(), getTop(), getBottom(), getRight() 这些API来获取 ...

  4. MVC中自定义ViewPage和WebViewPage

    ViewPage和WebViewPage的作用就是将Controller中数据返回给页面,一个是针对aspx一个相对cshtml的.代码如下: public abstract class WebVie ...

  5. ivew数控件Tree自定义节点内容示例分析

    ivew数控件Tree自定义节点内容示例分析 demo地址:https://run.iviewui.com/plcWlM4H <template> <Tree :data=" ...

  6. scrollTo , scrollBy区别

    View视图中scrollTo 与scrollBy这两个函数的区别 . 首先 ,我们必须明白在Android View视图是没有边界的,Canvas是没有边界的,只不过我们通过绘制特定的View时对 ...

  7. getX,getY,getScrollX,getScrollY,ScrollTo(),ScrollBy()辨析

    前言:前两天看了自定义控件,其中有一些东西我觉得有必要深入理解一下 以下图为例: getX(),getY()返回的是触摸点A相对于view的位置 getRaw(),getRawY()返回的是触摸点B相 ...

  8. Salesforce自定义权限简介

    自定义权限(Custom Permission) Salesforce默认提供了多种方式设定用户的权限,比如简档.权限集等.在这些设定中,已经包括了系统中的对象.应用.字段.页面布局等组件,管理员或开 ...

  9. [转][Java]自定义标签简介

    作用:自定义标签主要用于移除 jsp 页面中的 java 代码. 实现:需要完成以下两个步骤: 编写一个实现 Tag 接口的 Java 类,把页面 java 代码移到这个 java 类中.(标签处理类 ...

随机推荐

  1. Linux中find用法

    Linux中find用法 linux常用命令 find -name april* 在当前目录下查找以april开始的文件 find -name april* fprint file 在当前目录下查找以 ...

  2. 微软企业库5.0 学习之路——扩展学习篇、库中的依赖关系注入(重构 Microsoft Enterprise Library)[转]

    这篇文章是我在patterns & practices看到的一篇有关EntLib5.0的文章,主要介绍了EntLib5.0的这次的架构变化由来,觉得很不错,大家可以看一下! 在过去几年中,依赖 ...

  3. poj1562 Oil Deposits(DFS)

    题目链接 http://poj.org/problem?id=1562 题意 输入一个m行n列的棋盘,棋盘上每个位置为'*'或者'@',求'@'的连通块有几个(连通为8连通,即上下左右,两条对角线). ...

  4. POJ3255 Roadblocks [Dijkstra,次短路]

    题目传送门 Roadblocks Description Bessie has moved to a small farm and sometimes enjoys returning to visi ...

  5. 关于字体剥离和精简工具 FontSubsetGUI 和 FontPruner 的比较。

    在 Unity 中制作游戏时对动态字体的剥离和精简是现在常用的手段,现在有两篇博客是大家阅读和参照较多的,分别是 如何精简Unity中使用的字体文件 和 FontPruner 字体精简工具.他们各自提 ...

  6. TCP/IP——链路层简记

    在TCP/IP协议族中链路层的主要目的有三个: 1,为IP模块发送和接受IP数据报. 2,为ARP模块发送ARP请求和接受ARP应答. 3,为RARP模块发送RARP请求和接受RARP应答. 链路层包 ...

  7. django配置Ueditor

    1.安装DjangoUeditor pip install DjangoUeditor 2.在Django中安装DjangoUedito app,在INSTALL_APPS里面增加DjangoUedi ...

  8. BZOJ 1030 [JSOI2007]文本生成器(AC自动机)

    [题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=1030 [题目大意] 求出包含任意一个给定串的串数量 [题解] 我们求出不包含任意一个给 ...

  9. 快速排序-C

    #include <stdio.h> #include <stdlib.h> #define N 6 int partition(int arr[], int low, int ...

  10. 给WebAPI的REST接口服务添加测试页面(一)

    当使用WebAPI提供REST服务的时候,一个经常进行的操作是对接口进行测试.Asp.net WebAPI框架本身并没有提供这一接口,不过由于提供的是标准的REST服务,是可以非常方便的使用一些第三方 ...