【Scroller】scrollTo scrollBy startScroll computeScroll 自定义ViewPage 简介 示例

时间:2021-10-15 20:05:42

简介

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&lpar;&rpar; scrollBy&lpar;&rpar; Scroller解说及应用

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

  2. 图解Android View的scrollTo&lpar;&rpar;&comma;scrollBy&lpar;&rpar;&comma;getScrollX&lpar;&rpar;&comma; getScrollY&lpar;&rpar;

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

  3. View:Android View的scrollTo&lpar;&rpar;&comma;scrollBy&lpar;&rpar;&comma;getScrollX&lpar;&rpar;&comma; getScrollY&lpar;&rpar;的理解

    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=&quot ...

  6. scrollTo &comma; scrollBy区别

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

  7. getX,getY&comma;getScrollX&comma;getScrollY&comma;ScrollTo&lpar;&rpar;&comma;ScrollBy&lpar;&rpar;辨析

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

  8. Salesforce自定义权限简介

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

  9. &lbrack;转&rsqb;&lbrack;Java&rsqb;自定义标签简介

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

随机推荐

  1. Odd Even Linked List

    Given a singly linked list, group all odd nodes together followed by the even nodes. Please note her ...

  2. poj1543-Perfect Cubes&lpar;暴力&rpar;

    水题:求n^3 =  a^3 + b^3 + c^3 ;暴力即可 #include<iostream> using namespace std; int main(){ int n ; c ...

  3. java并发:线程同步机制之计数器&amp&semi;Exechanger

    第一节 CountDownLatch (1)初识CountDownLatch (2)详述CountDownLatch CountDownLatch是通过一个计数器来实现的,计数器的初始值为线程的数量. ...

  4. HTTP请求头参数

      Accept-Language: zh-cn,zh;q=0.5 意思:浏览器支持的语言分别是中文和简体中文,优先支持简体中文. 详解: Accept-Language表示浏览器所支持的语言类型: ...

  5. POJ 2155 Matrix &lpar;二维线段树入门,成段更新,单点查询 &sol; 二维树状数组,区间更新,单点查询&rpar;

    题意: 有一个n*n的矩阵,初始化全部为0.有2中操作: 1.给一个子矩阵,将这个子矩阵里面所有的0变成1,1变成0:2.询问某点的值 方法一:二维线段树 参考链接: http://blog.csdn ...

  6. delete语句与reference约束 FK&lowbar;subplan&lowbar;job&lowbar;id冲突问题,导致job无法删除解决办法

    在SQL Server 2008上删除已运行维护计划后,维护计划job没有自动删除掉,手工再删除维护计划job,提示删除失败. 错误现象:delete  语句与 reference 约束"F ...

  7. easyui 菜单树搜索

    //树形菜单搜索方法    function searchTree(treeObj,parentNode,searchCon){        var children;        for(var ...

  8. &lbrack;Swift&rsqb;LeetCode327&period; 区间和的个数 &vert; Count of Range Sum

    Given an integer array nums, return the number of range sums that lie in [lower, upper] inclusive.Ra ...

  9. org&period;apache&period;xerces&period;dom&period;ElementNSImpl&period;setUserData&lpar;Ljava&sol;lang&sol;String&semi;Ljava&sol;lang

    HTTP Status 500 - Handler processing failed; nested exception is java.lang.AbstractMethodError: org. ...

  10. optimizer&lowbar;switch引起的诡异问题

    参数描述 MySQL中不同的版本优化器会有很多新特性,比如MRR.BKA等,optimizer_switch这个参数就是控制查询优化器怎样使用这些特性.很多情况下我们会根据自身的需求去设置optimi ...