精华内容
参与话题
问答
  • 2016大家新年好!这是今年的第一篇文章,那么应CSDN工作人员的建议,为了能给大家带来更好的阅读体验,我也是将博客换成了宽屏版。另外,作为一个对新鲜事物从来后知后觉的人,我终于也在新的一年里改用MarkDown...

    转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/48719871
    2016大家新年好!这是今年的第一篇文章,那么应CSDN工作人员的建议,为了能给大家带来更好的阅读体验,我也是将博客换成了宽屏版。另外,作为一个对新鲜事物从来后知后觉的人,我终于也在新的一年里改用MarkDown编辑器来写博客了,希望大家在我的博客里也能体验到新年新的气象。

    我写博客的题材很多时候取决于平时大家问的问题,最近一段时间有不少朋友都问到ViewPager是怎么实现的。那ViewPager相信每个人都再熟悉不过了,因此它实在是太常用了,我们可以借助ViewPager来轻松完成页面之间的滑动切换效果,但是如果问到它是如何实现的话,我感觉大部分人还是比较陌生的, 为此我也是做了一番功课。其实说到ViewPager最基本的实现原理主要就是两部分内容,一个是事件分发,一个是Scroller,那么对于事件分发,其实我在很早之前就已经写过了相关的内容,感兴趣的朋友可以去阅读 Android事件分发机制完全解析,带你从源码的角度彻底理解,但是对于Scroller我还从来没有讲过,因此本篇文章我们就先来学习一下Scroller的用法,并结合事件分发和Scroller来实现一个简易版的ViewPager。


    Scroller是一个专门用于处理滚动效果的工具类,可能在大多数情况下,我们直接使用Scroller的场景并不多,但是很多大家所熟知的控件在内部都是使用Scroller来实现的,如ViewPager、ListView等。而如果能够把Scroller的用法熟练掌握的话,我们自己也可以轻松实现出类似于ViewPager这样的功能。那么首先新建一个ScrollerTest项目,今天就让我们通过例子来学习一下吧。
    先撇开Scroller类不谈,其实任何一个控件都是可以滚动的,因为在View类当中有scrollTo()和scrollBy()这两个方法,如下图所示:

    20160107203341461

    这两个方法都是用于对View进行滚动的,那么它们之间有什么区别呢?简单点讲,scrollBy()方法是让View相对于当前的位置滚动某段距离,而scrollTo()方法则是让View相对于初始的位置滚动某段距离。这样讲大家理解起来可能有点费劲,我们来通过例子实验一下就知道了。

    修改activity_main.xml中的布局文件,代码如下所示:

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:id="@+id/layout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        tools:context="com.example.guolin.scrollertest.MainActivity">
    
        <Button
            android:id="@+id/scroll_to_btn"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="scrollTo"/>
    
        <Button
            android:id="@+id/scroll_by_btn"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="scrollBy"/>
    
    </LinearLayout>
    

    外层我们使用了一个LinearLayout,然后在里面包含了两个按钮,一个用于触发scrollTo逻辑,一个用于触发scrollBy逻辑。

    接着修改MainActivity中的代码,如下所示:

    public class MainActivity extends AppCompatActivity {
    
        private LinearLayout layout;
    
        private Button scrollToBtn;
    
        private Button scrollByBtn;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            layout = (LinearLayout) findViewById(R.id.layout);
            scrollToBtn = (Button) findViewById(R.id.scroll_to_btn);
            scrollByBtn = (Button) findViewById(R.id.scroll_by_btn);
            scrollToBtn.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    layout.scrollTo(-60, -100);
                }
            });
            scrollByBtn.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    layout.scrollBy(-60, -100);
                }
            });
        }
    }
    

    没错,代码就是这么简单。当点击了scrollTo按钮时,我们调用了LinearLayout的scrollTo()方法,当点击了scrollBy按钮时,调用了LinearLayout的scrollBy()方法。那有的朋友可能会问了,为什么都是调用的LinearLayout中的scroll方法?这里一定要注意,不管是scrollTo()还是scrollBy()方法,滚动的都是该View内部的内容,而LinearLayout中的内容就是我们的两个Button,如果你直接调用button的scroll方法的话,那结果一定不是你想看到的。

    另外还有一点需要注意,就是两个scroll方法中传入的参数,第一个参数x表示相对于当前位置横向移动的距离,正值向左移动,负值向右移动,单位是像素。第二个参数y表示相对于当前位置纵向移动的距离,正值向上移动,负值向下移动,单位是像素。

    那说了这么多,scrollTo()和scrollBy()这两个方法到底有什么区别呢?其实运行一下代码我们就能立刻知道了:

    20160110164232041

    可以看到,当我们点击scrollTo按钮时,两个按钮会一起向右下方滚动,因为我们传入的参数是-60和-100,因此向右下方移动是正确的。但是你会发现,之后再点击scrollTo按钮就没有任何作用了,界面不会再继续滚动,只有点击scrollBy按钮界面才会继续滚动,并且不停点击scrollBy按钮界面会一起滚动下去。

    现在我们再来回头看一下这两个方法的区别,scrollTo()方法是让View相对于初始的位置滚动某段距离,由于View的初始位置是不变的,因此不管我们点击多少次scrollTo按钮滚动到的都将是同一个位置。而scrollBy()方法则是让View相对于当前的位置滚动某段距离,那每当我们点击一次scrollBy按钮,View的当前位置都进行了变动,因此不停点击会一直向右下方移动。

    通过这个例子来理解,相信大家已经把scrollTo()和scrollBy()这两个方法的区别搞清楚了,但是现在还有一个问题,从上图中大家也能看得出来,目前使用这两个方法完成的滚动效果是跳跃式的,没有任何平滑滚动的效果。没错,只靠scrollTo()和scrollBy()这两个方法是很难完成ViewPager这样的效果的,因此我们还需要借助另外一个关键性的工具,也就我们今天的主角Scroller。

    Scroller的基本用法其实还是比较简单的,主要可以分为以下几个步骤:

    1. 创建Scroller的实例
    2. 调用startScroll()方法来初始化滚动数据并刷新界面
    3. 重写computeScroll()方法,并在其内部完成平滑滚动的逻辑

    那么下面我们就按照上述的步骤,通过一个模仿ViewPager的简易例子来学习和理解一下Scroller的用法。
    新建一个ScrollerLayout并让它继承自ViewGroup来作为我们的简易ViewPager布局,代码如下所示:

    /**
     * Created by guolin on 16/1/12.
     */
    public class ScrollerLayout extends ViewGroup {
    
        /**
         * 用于完成滚动操作的实例
         */
        private Scroller mScroller;
    
        /**
         * 判定为拖动的最小移动像素数
         */
        private int mTouchSlop;
    
        /**
         * 手机按下时的屏幕坐标
         */
        private float mXDown;
    
        /**
         * 手机当时所处的屏幕坐标
         */
        private float mXMove;
    
        /**
         * 上次触发ACTION_MOVE事件时的屏幕坐标
         */
        private float mXLastMove;
    
        /**
         * 界面可滚动的左边界
         */
        private int leftBorder;
    
        /**
         * 界面可滚动的右边界
         */
        private int rightBorder;
    
        public ScrollerLayout(Context context, AttributeSet attrs) {
            super(context, attrs);
            // 第一步,创建Scroller的实例
            mScroller = new Scroller(context);
            ViewConfiguration configuration = ViewConfiguration.get(context);
            // 获取TouchSlop值
            mTouchSlop = ViewConfigurationCompat.getScaledPagingTouchSlop(configuration);
        }
    
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
            int childCount = getChildCount();
            for (int i = 0; i < childCount; i++) {
                View childView = getChildAt(i);
                // 为ScrollerLayout中的每一个子控件测量大小
                measureChild(childView, widthMeasureSpec, heightMeasureSpec);
            }
        }
    
        @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);
                    // 为ScrollerLayout中的每一个子控件在水平方向上进行布局
                    childView.layout(i * childView.getMeasuredWidth(), 0, (i + 1) * childView.getMeasuredWidth(), childView.getMeasuredHeight());
                }
                // 初始化左右边界值
                leftBorder = getChildAt(0).getLeft();
                rightBorder = getChildAt(getChildCount() - 1).getRight();
            }
        }
    
        @Override
        public boolean onInterceptTouchEvent(MotionEvent ev) {
            switch (ev.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    mXDown = ev.getRawX();
                    mXLastMove = mXDown;
                    break;
                case MotionEvent.ACTION_MOVE:
                    mXMove = ev.getRawX();
                    float diff = Math.abs(mXMove - mXDown);
                    mXLastMove = mXMove;
                    // 当手指拖动值大于TouchSlop值时,认为应该进行滚动,拦截子控件的事件
                    if (diff > mTouchSlop) {
                        return true;
                    }
                    break;
            }
            return super.onInterceptTouchEvent(ev);
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            switch (event.getAction()) {
                case MotionEvent.ACTION_MOVE:
                    mXMove = event.getRawX();
                    int scrolledX = (int) (mXLastMove - mXMove);
                    if (getScrollX() + scrolledX < leftBorder) {
                        scrollTo(leftBorder, 0);
                        return true;
                    } else if (getScrollX() + getWidth() + scrolledX > rightBorder) {
                        scrollTo(rightBorder - getWidth(), 0);
                        return true;
                    }
                    scrollBy(scrolledX, 0);
                    mXLastMove = mXMove;
                    break;
                case MotionEvent.ACTION_UP:
                    // 当手指抬起时,根据当前的滚动值来判定应该滚动到哪个子控件的界面
                    int targetIndex = (getScrollX() + getWidth() / 2) / getWidth();
                    int dx = targetIndex * getWidth() - getScrollX();
                    // 第二步,调用startScroll()方法来初始化滚动数据并刷新界面
                    mScroller.startScroll(getScrollX(), 0, dx, 0);
                    invalidate();
                    break;
            }
            return super.onTouchEvent(event);
        }
    
        @Override
        public void computeScroll() {
            // 第三步,重写computeScroll()方法,并在其内部完成平滑滚动的逻辑
            if (mScroller.computeScrollOffset()) {
                scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
                invalidate();
            }
        }
    }
    

    整个Scroller用法的代码都在这里了,代码并不长,一共才100多行,我们一点点来看。
    首先在ScrollerLayout的构造函数里面我们进行了上述步骤中的第一步操作,即创建Scroller的实例,由于Scroller的实例只需创建一次,因此我们把它放到构造函数里面执行。另外在构建函数中我们还初始化的TouchSlop的值,这个值在后面将用于判断当前用户的操作是否是拖动。

    接着重写onMeasure()方法和onLayout()方法,在onMeasure()方法中测量ScrollerLayout里的每一个子控件的大小,在onLayout()方法中为ScrollerLayout里的每一个子控件在水平方向上进行布局。如果有朋友对这两个方法的作用还不理解,可以参照我之前写的一篇文章 Android视图绘制流程完全解析,带你一步步深入了解View(二)

    接着重写onInterceptTouchEvent()方法, 在这个方法中我们记录了用户手指按下时的X坐标位置,以及用户手指在屏幕上拖动时的X坐标位置,当两者之间的距离大于TouchSlop值时,就认为用户正在拖动布局,然后我们就将事件在这里拦截掉,阻止事件传递到子控件当中。

    那么当我们把事件拦截掉之后,就会将事件交给ScrollerLayout的onTouchEvent()方法来处理。如果当前事件是ACTION_MOVE,说明用户正在拖动布局,那么我们就应该对布局内容进行滚动从而影响拖动事件,实现的方式就是使用我们刚刚所学的scrollBy()方法,用户拖动了多少这里就scrollBy多少。另外为了防止用户拖出边界这里还专门做了边界保护,当拖出边界时就调用scrollTo()方法来回到边界位置。

    如果当前事件是ACTION_UP时,说明用户手指抬起来了,但是目前很有可能用户只是将布局拖动到了中间,我们不可能让布局就这么停留在中间的位置,因此接下来就需要借助Scroller来完成后续的滚动操作。首先这里我们先根据当前的滚动位置来计算布局应该继续滚动到哪一个子控件的页面,然后计算出距离该页面还需滚动多少距离。接下来我们就该进行上述步骤中的第二步操作,调用startScroll()方法来初始化滚动数据并刷新界面。startScroll()方法接收四个参数,第一个参数是滚动开始时X的坐标,第二个参数是滚动开始时Y的坐标,第三个参数是横向滚动的距离,正值表示向左滚动,第四个参数是纵向滚动的距离,正值表示向上滚动。紧接着调用invalidate()方法来刷新界面。

    现在前两步都已经完成了,最后我们还需要进行第三步操作,即重写computeScroll()方法,并在其内部完成平滑滚动的逻辑 。在整个后续的平滑滚动过程中,computeScroll()方法是会一直被调用的,因此我们需要不断调用Scroller的computeScrollOffset()方法来进行判断滚动操作是否已经完成了,如果还没完成的话,那就继续调用scrollTo()方法,并把Scroller的curX和curY坐标传入,然后刷新界面从而完成平滑滚动的操作。

    现在ScrollerLayout已经准备好了,接下来我们修改activity_main.xml布局中的内容,如下所示:

    <?xml version="1.0" encoding="utf-8"?>
    <com.example.guolin.scrollertest.ScrollerLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        >
    
        <Button
            android:layout_width="match_parent"
            android:layout_height="100dp"
            android:text="This is first child view"/>
    
        <Button
            android:layout_width="match_parent"
            android:layout_height="100dp"
            android:text="This is second child view"/>
    
        <Button
            android:layout_width="match_parent"
            android:layout_height="100dp"
            android:text="This is third child view"/>
    
    </com.example.guolin.scrollertest.ScrollerLayout>
    

    可以看到,这里我们在ScrollerLayout中放置了三个按钮用来进行测试,其实这里不仅可以放置按钮,放置任何控件都是没问题的。

    最后MainActivity当中删除掉之前测试的代码:

    public class MainActivity extends AppCompatActivity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
        }
    }
    

    好的,所有代码都在这里了,现在我们可以运行一下程序来看一看效果了,如下图所示:

    20160114230048304

    怎么样,是不是感觉有点像一个简易的ViewPager了?其实借助Scroller,很多漂亮的滚动效果都可以轻松完成,比如实现图片轮播之类的特效。当然就目前这一个例子来讲,我们只是借助它来学习了一下Scroller的基本用法,例子本身有很多的功能点都没有去实现,比如说ViewPager会根据用户手指滑动速度的快慢来决定是否要翻页,这个功能在我们的例子中并没有体现出来,不过大家也可以当成自我训练来尝试实现一下。


    好的,那么本篇文章就到这里,相信通过这篇文章的学习,大家已经能够熟练掌握Scroller的使用方法了,当然ViewPager的内部实现要比这复杂得多,如果有朋友对ViewPager的源码感兴趣也可以尝试去读一下,不过一定需要非常扎实的基本功才行。

    关注我的技术公众号,每天都有优质技术文章推送。关注我的娱乐公众号,工作、学习累了的时候放松一下自己。

    微信扫一扫下方二维码即可关注:

    20160507110203928         20161011100137978

    展开全文
  • Scroller使用详解

    2018-06-08 18:59:26
    一、概述 这个类封装了滚动操作。滚动的持续时间可以通过构造函数传递,并且可以指定滚动动作的持续的最长时间。经过这段时间,滚动会自动定位到最终位置,...使用缺省的持续持续时间和动画插入器创建一个Scroller...

    一、概述

    这个类封装了滚动操作。滚动的持续时间可以通过构造函数传递,并且可以指定滚动动作的持续的最长时间。经过这段时间,滚动会自动定位到最终位置,并且通过computeScrollOffset()会得到的返回值为false,表明滚动动作已经结束。

    二、构造函函数

    public Scroller (Context context)

    使用缺省的持续持续时间和动画插入器创建一个Scroller。


    public Scroller (Context context, Interpolator interpolator)

    根据指定的动画插入器创建一个Scroller,如果指定的动画插入器为空,则会使用缺省的动画插入器(粘滞viscous)创建。

    三、公共方法

    public void abortAnimation ()

      停止动画。与forceFinished(boolean)相反,Scroller滚动到最终xy位置时中止动画。

      参见

            forceFinished(boolean)


      public boolean computeScrollOffset ()

      当想要知道新的位置时,调用此函数。如果返回true,表示动画还没有结束。位置改变以提供一个新的位置。

     

      public void extendDuration (int extend)

      延长滚动动画时间。此函数允许当使用setFinalX(int) or setFinalY(int) 时,卷动动作持续更长时间并且卷动更长距离。

              参数

                  extend 卷动事件延长的时间,以毫秒为单位

              参见

                  setFinalX(int)

                  setFinalY(int)

     

      public void fling (int startX, int startY, int velocityX, int velocityY, int minX, int maxX, int minY, int maxY)

      在fling(译者注:快滑,用户按下触摸屏、快速移动后松开)手势基础上开始滚动。滚动的距离取决于fling的初速度。

          参数

                startX 滚动起始点X坐标

    startY 滚动起始点Y坐标

    velocityX   当滑动屏幕时X方向初速度,以每秒像素数计算

    velocityY   当滑动屏幕时Y方向初速度,以每秒像素数计算

    minX    X方向的最小值,scroller不会滚过此点。

    maxX    X方向的最大值,scroller不会滚过此点。

    minY    Y方向的最小值,scroller不会滚过此点。

    maxY    Y方向的最大值,scroller不会滚过此点。


      public final void forceFinished (boolean finished)

      强制终止的字段到特定值。(译者注:立即停止滚动?)

          参数

              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 ()

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

          返回值

                经过时间以毫秒为单位


                   getScrollX()的返回值为:当前View显示部分的左边到第一个View的左边的距离。

    举个例子,一个横向的ViewGroup,如果每个View的宽度为300,那么当前显示第一个View的时候getScrollX()返回值为0,当你向左移动第一个View,移动距离为30,那么此时getScrollX()的返回值为30,如果当前显示第二个View,那么getScrollX()的返回值为300,第二个View再向左移动一点,假如距离为20,那么此时getScrollX()的返回值为320,以此类推。



    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.   
    9.     private static final String TAG = “Scroller”;  
    10.   
    11.     private Scroller mScroller;  
    12.   
    13.     public CustomView(Context context, AttributeSet attrs) {  
    14.   
    15.     super(context, attrs);  
    16.     mScroller = new Scroller(context);  
    17.     }  
    18.   
    19.     //调用此方法滚动到目标位置  
    20.     public void smoothScrollTo(int fx, int fy) {  
    21.   
    22.     int dx = fx - mScroller.getFinalX();  
    23.     int dy = fy - mScroller.getFinalY();  
    24.     smoothScrollBy(dx, dy);  
    25.     }  
    26.   
    27.     //调用此方法设置滚动的相对偏移  
    28.     public void smoothScrollBy(int dx, int dy) {  
    29.   
    30.     //设置mScroller的滚动偏移量  
    31.     mScroller.startScroll(mScroller.getFinalX(), mScroller.getFinalY(), dx, dy);  
    32.     invalidate();//这里必须调用invalidate()才能保证computeScroll()会被调用,否则不一定会刷新界面,看不到滚动效果  
    33.     }  
    34.   
    35.     @Override  
    36.     public void computeScroll() {  
    37.   
    38.     //先判断mScroller滚动是否完成  
    39.     if (mScroller.computeScrollOffset()) {  
    40.   
    41.     //这里调用View的scrollTo()完成实际的滚动  
    42.     scrollTo(mScroller.getCurrX(), mScroller.getCurrY());  
    43.   
    44.     //必须调用该方法,否则不一定能看到滚动效果  
    45.     postInvalidate();  
    46.     }  
    47.     super.computeScroll();  
    48.   }  
    49. }  

    我们先来看一下,Scroller,这个对象里有startScroll方法
    void android.widget.Scroller.startScroll(int startX, int startY, int dx, int dy, int duration)
    第一个参数是起始移动的x坐标值,第二个是起始移动的y坐标值,第三个第四个参数都是移到某点的坐标值,而duration 当然就是执行移动的时间。这个有什么用呢。要知道有什么用还得再看一个方法
    boolean android.widget.Scroller.computeScrollOffset()
    当startScroll执行过程中即在duration时间内,computeScrollOffset  方法会一直返回false,但当动画执行完成后会返回返加true.
    有了这两个方法还不够,我们还需要再重写viewGroup的一个方法
    computeScroll 这个方法什么时候会被调用呢


    官网上这样说的
    public void computeScroll ()
    Since: API Level 1
    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.


    当我们执行ontouch或invalidate()或postInvalidate()都会导致这个方法的执行
    所以我们像下面这样调用,postInvalidate执行后,会去调computeScroll 方法,而这个方法里再去调postInvalidate,这样就可以不断地去调用scrollTo方法了,直到mScroller动画结束,当然第一次时,我们需要手动去调用一次postInvalidate才会去调用.




    展开全文
  • Scroller的用法

    千次阅读 2018-04-08 15:04:45
    Scroller的基本用法   Scroller用于View的滑动,其基本原理还是ScrollTo/ScrollBy。Scroller在其基础上把滑动的位移切分成无数细小的单元,并在一个时间段内对其进行位移,使View的滑动看起来具有平滑的效果。 ...

    Scroller的基本用法

      Scroller用于View的滑动,其基本原理还是ScrollTo/ScrollBy。Scroller在其基础上把滑动的位移切分成无数细小的单元,并在一个时间段内对其进行位移,使View的滑动看起来具有平滑的效果。
      Scroller的一般使用要结合View的ComputeScroll方法。这个方法默认会在View的draw方法(我们一般重写的是onDraw方法,不是draw方法)中调用。draw方法会在我们调用invalidate方法后对View进行重新绘制。利用这个特性可以完成多段位移。

    public class ScrollButton extends android.support.v7.widget.AppCompatButton {
        Scroller scroller;
        int direction = -1;
    
        public ScrollButton(Context context) {
    
            this(context,null);
        }
    
        public ScrollButton(Context context, AttributeSet attrs) {
            this(context, attrs,0);
        }
    
        public ScrollButton(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            scroller = new Scroller(context);
        }
    
        @Override
        public void computeScroll() {
            if(scroller!=null){
                if(scroller.computeScrollOffset()){//判断scroll是否完成
                    ((View) getParent()).scrollTo(
                            scroller.getCurrX(),scroller.getCurrY()
                    );//执行本段位移
    
                    invalidate();//进行下段位移
                }
            }
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    scroller.startScroll(((int) getX()), ((int) getY()), ((int) getX())*direction,
                            ((int) getY())*direction);//开始位移,真正开始是在下面的invalidate
                    direction*=-1;//改变方向
                    invalidate();//开始执行位移
                    break;
            }
            return super.onTouchEvent(event);
        }
    }

      上面自定义了一个按钮,并且按下按钮会让按钮进行一段平滑的位移。要使用这个按钮,只需要在xml文件中声明即可,无需其他步骤。
      下面来看一下不借助View的ComputeScroll来使用Scroller。当然这里只是为了加深理解,大多数Scroller还是要结合View的ComputeScroll方法的。

            final Scroller scroller = new Scroller(this);
            final Handler handler = new Handler(){
                @Override
                public void handleMessage(Message msg) {
                    if(scroller.computeScrollOffset()){
                        ((View) tv1.getParent()).scrollTo(
                                scroller.getCurrX(),scroller.getCurrY()
                                );
                        sendEmptyMessage(0);
                    }
                }
            };
            tv1.setOnClickListener(new View.OnClickListener() {//tv1是一个TextView
                @Override
                public void onClick(View v) {
                    scroller.startScroll(0, 0, 100,
                           100);
                    handler.sendEmptyMessage(0);
                }
            });

      可以看到,也是利用了handler的循环发消息的特点来完成scroll。那么在这里可以总结一下,Scroller其实只提供了一个拆分位移的功能,不过还是结合View的ComputeScroll来使用最好,可以完成合理的控件封装。

    展开全文
  • VueScroller 学习

    千次阅读 2018-10-08 15:13:20
    学习了... 文档:https://www.npmjs.com/package/vue-scroller 自己记录一下VueScroller的学习 安装并引入 npm install vue-scroller -D //网上看是这个参数 main.js: ...

    学习了https://blog.csdn.net/youyudexiaowangzi/article/details/81144497 这篇文章后

    文档:https://www.npmjs.com/package/vue-scroller

    自己记录一下VueScroller的学习

    安装并引入

    npm install vue-scroller -D //网上看是这个参数

    main.js

    import VueScroller  from 'vue-scroller'

    Vue.use(VueScroller)

    代码如下

     <!--on-refresh:下拉刷新-->
     <!--on-infinite:上拉加载-->    
    <scroller class="scroller" ref="my_scroller" :on-infinite="infinite" :on-refresh ="refresh" :noDataText="noDataText" ></scroller>
    data(){
            return{
                p:0,
                list:[],
                loading:true,
                noDataText:"没有更多了"
            }
        },
    methods: {
            getList(done){
                let url = "http://www.baidu.com";
                let p = this.p;
                let that = this;
    
                if(this.loading == true){
                    this.loading = false;
    
                    //你的ajax
                    this.$ajax({url: url, params: {p: p}
                        }).then(response =>{
                            let list = response.data;
    
                            if (list.length) {
                                if(that.p == 0) that.list = [];     //下拉刷新时重置数组
    
                                list.forEach((v, k) => {
                                    that.list.push(v);
                                })
    
                                that.p++;
    
                                that.loading = true;
    
        //                        that.$refs.my_scroller.finishInfinite(true);   //有数据停止无限加载,可用done代替
                                done(true);
                            }else{
        //                        that.$refs.my_scroller.finishInfinite(2);     //无数据停止无限加载,可用done代替
                                done(2);
                            }
                        });
                }else{
                    done(2);
                }
            },
            //上拉加载
            infinite: function (done) {
                console.log('infinite')
                this.getList(done);
            },
            //下拉刷新
            refresh: function (done) {
                console.log('refresh');
                this.p = 0;
                this.loading = true;
                this.getList(done);
            },
        }

    上拉加载后需要手动调用来 finishInfinite 停止重复刷新,用done 的话上拉和下拉就可以少做一个判断

    遇到的问题还有header 头被它遮挡了,加一个跟你header 头高度的padding 就行

    .scroller{ padding-top:50px;}

     

    展开全文
  • vue-scroller用法

    万次阅读 热门讨论 2018-07-21 14:55:31
    今天要用vue实现一个下拉刷新,上拉加载更多的功能,百度发现各种scroll,iscroll、better-scroscroller、vue-infinite-scroll等,最终选择了vue-scroller,没别的原因,就是看这个名字顺眼。 先说简单用法 安装:...
  • 之前做项目时,要实现Activity从左往右滑出来的效果,那个时候我选择了属性动画,,最近了解了下Scroller,也可以用它实现Activity的滑动,接下来介绍它的用法。 Scroller并不会使View滑动,看看它的源码就会知道...
  • Android开发之Scroller的使用详解

    千次阅读 2015-03-02 22:05:42
    其实关于Scroller这篇文章我早就想写了,起初是因为看Xlistview的源码,接触到了Scroller的使用,之后又在网上读了大量的文章,之后自己写了一个demo,对scroller也算是有了自己的看法。 我学习的道路是曲折的,所以...
  • 一直以来,Android 开发中绕不过去的话题就是自定义 View,曾几何时,考验一个...本文要讲解的一个技术点,正是广大开发者容易困惑的一个知识点————Scroller。为什么说它是一个容易让人困惑的内容呢?这是因为...
  • Android_Scroller滑动动画

    千次阅读 2016-07-07 08:44:23
    当我们在使用view的scrollTo()或scrollBy()时,会发现这个滑动很生硬,没有动画效果,一下就过去了,就像...如果能平滑的滑动回去的话,最好不过了,刚好安卓提供一个Scroller类,专门来处理view在scrollTo()或scrollBy
  • Scroller滑动剖析

    2018-09-04 13:54:52
    Scroller 官方介绍:Scroller里面封装了滚动操作,并通过收集数据产生滚动动画 1.Scroller的使用 mScroller = new Scroller(context); public void beginScroll(){ mScroller.startScroll(0,0,-200,0,...
  • 安卓view滑动Scroller实现弹性滑动

    千次阅读 2018-06-12 18:42:09
    背景这几天开始学习安卓开发里面的view滑动部分,首先学习的是利用Scroller实现弹性滑动首先,view滑动不是真正意义上的滑动,只是通过改变画布的xy坐标,来不断绘制view的不同部分,看起来像滑动一样我实现的弹性...
  • 学习本来就是从困惑中摸索问题答案的过程,能够描述出来问题就已经成功了一半。只要发现了困扰你的东西是什么,那么你就离解答出来不远了。————肯尼斯 R. 莱伯德 一直以来,Android 开发中绕不过去的话题就是...
  • 滑动过渡之Scroller

    2017-09-04 14:59:11
    Scroller:手指滑动中比较重要的一个辅助类,辅助完成一些动画参数的计算等。看到Scroller你可能感到陌生,但我们每个人都用过它,因为viewpage,listvew等控件源码都会用到它。而本文主要实现一个简单的viewpage效果...
  • scroller 滑动

    2012-08-14 16:06:00
    http://blog.csdn.net/qinjuning/article/details/7419207 转载于:https://www.cnblogs.com/olvo/archive/2012/08/14/2638195.html
  • 使用Scroller类实现滑动效果的原因: 使用 scrollTo 和 scrollBy 方法滑动View过于突兀,是瞬间完成的,使用Scroller类有一定的动画效果 使用Scroller类的三个步骤: 初始化Scroller Scroller mScroller; ...
  • 手势滑动之玩转onTouchEvent()与Scroller

    万次阅读 热门讨论 2016-11-05 16:18:06
    10月份工作太忙只写了一篇博客,这个月...昨天和我一个超级要好的朋友聊起自定义view和手势滑动,正好群里好多小伙伴总是问关于onTouchEvent()与Scroller的处理,所以就正好写一篇这样的博客,希望可以帮到需要的朋友。
  • 在页面中经常会用到滚动,下拉刷新,下拉加载等功能,vux的scroller可以使用,但是它不再维护,而且要配合load-more组件一起使用。所以一般在项目中我都是用vue-scroller. vue-scroller文档 1.在项目中安装: npm...
  • Android 带你从源码的角度解析Scroller的滚动实现原理

    万次阅读 多人点赞 2013-12-26 22:14:25
    一个View从在我们指定的时间内从一个位置滚动到另外一个位置,我们利用Scroller类可以实现匀速滚动,可以先加速后减速,可以先减速后加速等等效果,而不是瞬间的移动的效果,所以Scroller可以帮我们实现很多滑动的...
  • 在android学习中,动作交互是软件中重要的一部分,其中的Scroller就是提供了拖动效果的类,在网上,比如说一些Launcher实现滑屏都可以通过这个类去实现。。 例子相关博文:Android 仿 窗帘效果 和 登录界面...
  • vux scroller在iOS上滑动列表,滑完就会跳到顶部,然后去查了下vux的文档,没有解决办法,百度了一大堆,也没有相关问题,只能看下源码到底是哪除了问题。 经过一层层的查找,终于发现是ios13上面获取transform的...

空空如也

1 2 3 4 5 ... 20
收藏数 17,101
精华内容 6,840
关键字:

scroller