简介

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. 更换 ECharts 散点图图标(散点图中symbol的使用)

    更换 ECharts 散点图图标 使用symbol关键字

  2. 微信用户授权,获取code

    1.进入微信公众平台 2.进入到   开发->接口授权,点击 网页服务->网页授权->网页授权获取用户基本信息   后面的“修改“. 3.点击网页授权域名的设置 4.设置授权回调域名 ...

  3. Python并发编程-多进程socketserver简易版

    普通版的socketserver #server.py import socket sk = socket.socket() sk.bind(('127.0.0.1',8080))#建立连接 sk.l ...

  4. asp.net core结合Gitlab-CI实现自动化部署

    0.目录 整体架构目录:ASP.NET Core分布式项目实战-目录 一.前言 在之前的文章中写过k8s+Jenkins+GitLab-自动化部署asp.net core项目 的topic,这次讲解一 ...

  5. 非洲top10人口大国2017年的人口、预期寿命、三大主粮进口量、92/08/17年的饥饿指数

  6. javascript入门教程笔记

    BOM BOM 是“ Browser Object Model ”的缩写,简称“ 浏览器对象模型 ”. BOM 定义了 JavaScript 操作浏览器的接口,提供了访问某些功能(如浏览器窗口大小.版 ...

  7. [BZOJ2431][HAOI2009]逆序对数列(DP)

    从小到大加数,根据加入的位置转移,裸的背包DP. #include<cstdio> #include<cstring> #include<algorithm> #d ...

  8. 【lct】bzoj1036 [ZJOI2008]树的统计Count

    题意:给你一棵树,点带权,支持三种操作:单点修改:询问链上和:询问链上max. 这里的Query操作用了与上一题不太一样的做法(上一题用那种做法,因为在边带权的情况下换根太困难啦): 先ChangeR ...

  9. SpringBoot静态资源访问+拦截器+Thymeleaf模板引擎实现简单登陆

    在此记录一下这十几天的学习情况,卡在模板引擎这里已经是四天了. 对Springboot的配置有一个比较深刻的认识,在此和大家分享一下初学者入门Spring Boot的注意事项,如果是初学SpringB ...

  10. Java高级架构师(一)第41节:Nginx的配置优化以及使用建议