简介

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. Webpack, VSCode 和 Babel 组件模块导入别名

    很多时候我们使用别人的库,都是通过 npm install,再简单的引入,就可以使用了.     1 2 import React from 'react' import { connect } fr ...

  2. Es6懒加载

    const Login = resolve => require(['@/components/Login'], resolve) 注(当路由被访问的时候才加载这个组件)

  3. Django+Nginx+uwsgi搭建自己的博客(七)

    上一篇博客中介绍了Blogs App的部分后端功能的实现,在这篇博客中,将继续为大家介绍Blogs App中前端功能的实现. 首先来看发布博客功能的前端页面.在blogs/templates/blog ...

  4. CTF西湖论剑

    一,西湖论剑 itoa()函数有3个参数:第一个参数是要转换的数字,第二个参数是要写入转换结果的目标字符串,第三个参数是转移数字时所用 的基数.在上例中,转换基数为10.10:十进制:2:二进制... ...

  5. 洛谷——P1821 [USACO07FEB]银牛派对Silver Cow Party

    P1821 [USACO07FEB]银牛派对Silver Cow Party 题目描述 One cow from each of N farms (1 ≤ N ≤ 1000) conveniently ...

  6. xss可用事件

    onabort onafterprint onbeforeprint onbeforeunload onblur oncanplay oncanplaythrough onchange onclick ...

  7. MPI - 缓冲区和非阻塞通信

    转载自: Introduction to MPI - Part II (Youtube) Buffering  Suppose we have ) MPI_Send(sendbuf,...,,...) ...

  8. String 字符串补0

    method1: 前提是你的长度已经确定!比如规定现实10位! - 优点: 不需要都是数字类型    String str_m =  "123X";  String str =&q ...

  9. SDOI 2017 Round1 解题报告

    Day 1 T1 数字表格 题目大意 · 求\(\prod\limits_{i=1}^n\prod\limits_{j=1}^mFibonacci(\gcd(i,j))\),\(T\leq1000\) ...

  10. 入侵91网直到拿下服务器#并泄露150w+用户信息

    在补天看到一厂商 首先挖到一处注入 http://www.91taoke.com/index.php?m=Dayi&a=answer&aid=26313 此处注入是dba权限 打算使用 ...