scroller_scroller滑动 - CSDN
  • 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()这两个方法,如下图所示:

    这两个方法都是用于对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()这两个方法到底有什么区别呢?其实运行一下代码我们就能立刻知道了:

    可以看到,当我们点击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);
        }
    }
    

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

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


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

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

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

            

    展开全文
  • Scroller的用法

    千次阅读 2016-12-18 13:25:52
    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()这两个方法,如下图所示: 


    这两个方法都是用于对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>
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    外层我们使用了一个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);
                }
            });
        }
    }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    没错,代码就是这么简单。当点击了scrollTo按钮时,我们调用了LinearLayout的scrollTo()方法,当点击了scrollBy按钮时,调用了LinearLayout的scrollBy()方法。那有的朋友可能会问了,为什么都是调用的LinearLayout中的scroll方法?这里一定要注意,不管是scrollTo()还是scrollBy()方法,滚动的都是该View内部的内容,而LinearLayout中的内容就是我们的两个Button,如果你直接调用button的scroll方法的话,那结果一定不是你想看到的。 
    另外还有一点需要注意,就是两个scroll方法中传入的参数,第一个参数x表示相对于当前位置横向移动的距离,正值向左移动,负值向右移动,单位是像素。第二个参数y表示相对于当前位置纵向移动的距离,正值向上移动,负值向下移动,单位是像素。 
    那说了这么多,scrollTo()和scrollBy()这两个方法到底有什么区别呢?其实运行一下代码我们就能立刻知道了: 


    可以看到,当我们点击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();
            }
        }
    }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132

    整个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>
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    可以看到,这里我们在ScrollerLayout中放置了三个按钮用来进行测试,其实这里不仅可以放置按钮,放置任何控件都是没问题的。 
    最后MainActivity当中删除掉之前测试的代码:

    public class MainActivity extends AppCompatActivity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
        }
    }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

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


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


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

    展开全文
  • 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

    2019-06-22 11:57:48
     android.widget.Scroller  二、概述  这个类封装了滚动操作。滚动的持续时间可以通过构造函数传递,并且可以指定滚动动作的持续的最长时间。经过这段时间,滚动会自动定位到最终位置,并且通过comp...

     

      一、结构

        public class Scroller extends Object

     

        java.lang.Object

          android.widget.Scroller

     

      二、概述

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

     

      三、构造函数

      public Scroller (Context context)

      使用缺省的持续持续时间和动画插入器创建一个Scroller。(译者注:interpolator这里翻译为动画插入器,见这里。)

     

      public Scroller (Context context, Interpolator interpolator)

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

     

      四、公共方法

      public void abortAnimation ()

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

      参见

            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)

       强制设置为scroll状态

     

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

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

          返回值

                经过时间以毫秒为单位

     

      五、补充

        文章精选

          Scroller 粗浅理解

          ScrollTextView - scrolling TextView for Android

        示例代码

          创建工程MyScroler,或者将下类名“MyScroler”改为自己创建的工程,将下面代码直接覆盖生成的.java文件运行即可:

    package my.Scroller;

    import android.app.Activity;

    import android.os.Bundle;

    import android.view.View;

    import android.view.View.OnClickListener;

    import android.widget.Button;

    import android.widget.LinearLayout;

    import android.widget.Scroller;

     

    public class MyScroler extends Activity {

        /** Called when the activity is first created. */

        LinearLayout lay1,lay2,lay;

         private Scroller mScroller;

         private boolean s1,s2;

        @Override

        public void onCreate(Bundle savedInstanceState) {

            super.onCreate(savedInstanceState);

            mScroller = new Scroller(this);

             lay1 = new LinearLayout(this){

                 @Override 

                 public void computeScroll() { 

                     if (mScroller.computeScrollOffset()) { 

                         scrollTo(mScroller.getCurrX(), 0); 

                         postInvalidate(); 

                     } 

                 } 

             };

             lay2 = new LinearLayout(this){

                 @Override 

                 public void computeScroll() { 

                     if (mScroller.computeScrollOffset()) { 

                        // mScrollX = mScroller.getCurrX(); 

                         scrollTo(mScroller.getCurrX(), 0); 

                         postInvalidate(); 

                     } 

                 } 

             };

          lay1.setBackgroundColor(this.getResources().getColor(android.R.color.darker_gray));

            lay2.setBackgroundColor(this.getResources().getColor(android.R.color.white));

            lay = new LinearLayout(this);

            lay.setOrientation(LinearLayout.VERTICAL);

            LinearLayout.LayoutParams p0 = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.FILL_PARENT,LinearLayout.LayoutParams.FILL_PARENT);     

            this.setContentView(lay, p0);

            

            LinearLayout.LayoutParams p1 = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.FILL_PARENT,LinearLayout.LayoutParams.FILL_PARENT);     

            p1.weight=1;

            lay.addView(lay1,p1);

            LinearLayout.LayoutParams p2 = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.FILL_PARENT,LinearLayout.LayoutParams.FILL_PARENT);     

            p2.weight=1;

            lay.addView(lay2,p2);

            Button tx = new Button(this);

            Button tx2 = new Button(this);

            tx.setText("Button1");  

            tx2.setText("Button2");

            tx.setOnClickListener(new OnClickListener(){

                @Override

                public void onClick(View v) {

                    if(!s1){

                        mScroller.startScroll(0, 0, 5, 10, 10);

                        s1 = true;

                    }else{

                        mScroller.startScroll(0, 0, -50, -10,10);

                        s1 = false;

                    }

                }

                

            });

            tx2.setOnClickListener(new OnClickListener(){

                @Override

                public void onClick(View v) {

                    if(!s2){

                        mScroller.startScroll(0, 0, 5, 20,10);

                        s2=true;

                    }else{

                        mScroller.startScroll(20, 20, -50, -20,10);

                        s2=false;

                    }

                }

            });

            lay1.addView(tx);

            lay2.addView(tx2);

        }

    }

     

     

     

     

     

     

     

     


     

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

    另外我们通常通过它的fling(int startX, int startY, int velocityX,int velocityY, int minX, int maxX, int minY, int maxY)函数来设置一个fling行为(特殊的scroll)模型,即在在nt startX, int startY,这个点起向X和Y方向分别以int velocityX和int velocityY个像素的速度进行滚动。然后我们可以调用computeScrollOffset()计算此时scroll到的位置,并调用getCurrX()和getCurrY()得到到此时在X和Y方向的位置。

    公共构造函数

     

     

    Public Constructors
     

    Scroller(Contextcontext)

    Create a Scroller with the default duration and interpolator.

     

     

     

     

     

     

     

     

     

     

     

    Scroller(Contextcontext, Interpolatorinterpolator)

    Create a Scroller with the specified interpolator.

    interpolator参数只是在computeScrollOffset()函数中用于对我们的流逝的时间(通过timePassed()取得)这值进行重新解析

     

     

     

     

     

     

     

     

     

     

     

    Scroller(Contextcontext, Interpolatorinterpolator, boolean flywheel)

    Create a Scroller with the specified interpolator.

    interpolator只是在computeScrollOffset()函数中用于对我们的流逝的时间(通过timePassed()取得)这值进行重新解析

     

     

     

     

     

     

     

     

     

     

     

     

    公共函数

     

    Public Methods

    void

    abortAnimation()

    Stops the animation.

    停止scroll

     

     

     

     

     

     

     

     

     

     

    boolean

    computeScrollOffset()

    Call this when you want to know the new location.

    计算scroll的情况

     

     

     

     

     

     

     

     

     

     

    void

    extendDuration(int extend)

    Extend the scroll animation.

    增加scroll的时间

     

     

     

     

     

     

     

     

     

     

    void

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

    Start scrolling based on a fling gesture.

    模拟fling形式的scroll行为。

    int startX, int startY代表起点,int velocityX, intvelocityY代表初速度,int minX,int maxX,int minY, int maxY代表终点的范围

     

     

     

     

     

     

     

     

     

     

    final void

    forceFinished(boolean finished)

    Force the finished field to a particular value.

    强制设置为scroll状态

     

     

     

     

     

     

     

     

     

     

    float

    getCurrVelocity()

    Returns the current velocity.

    得到当前速度。该值是X方向和Y方向的合成值

     

     

     

     

     

     

     

     

     

     

    final int

    getCurrX()

    Returns the current X offset in the scroll.

    得到当前的X坐标

     

     

     

     

     

     

     

     

     

     

    final int

    getCurrY()

    Returns the current Y offset in the scroll.

    得到当前的Y坐标

     

     

     

     

     

     

     

     

     

     

    final int

    getDuration()

    Returns how long the scroll event will take, in milliseconds.

    得到设置的scroll行为的总时间值

     

     

     

     

     

     

     

     

     

     

    final int

    getFinalX()

    Returns where the scroll will end.

    得到scroll行为终点的X值

     

     

     

     

     

     

     

     

     

     

    final int

    getFinalY()

    Returns where the scroll will end.

    得到scroll行为终点的Y值

     

     

     

     

     

     

     

     

     

     

    final int

    getStartX()

    Returns the start X offset in the scroll.

    得到scroll行为起点的X值

     

     

     

     

     

     

     

     

     

     

    final int

    getStartY()

    Returns the start Y offset in the scroll.

    得到scroll行为起点的Y值

     

     

     

     

     

     

     

     

     

     

    final boolean

    isFinished()

    Returns whether the scroller has finished scrolling.

    返回scroll行为是否结束

     

     

     

     

     

     

     

     

     

     

    void

    setFinalX(int newX)

    Sets the final position (X) for this scroller.

    设置scroll行为的终点的X值

     

     

     

     

     

     

     

     

     

     

    void

    setFinalY(int newY)

    Sets the final position (Y) for this scroller.

    设置scroll行为的终点的Y值

     

     

     

     

     

     

     

     

     

     

    final void

    setFriction(float friction)

    The amount of friction applied to flings.

     

     

     

     

     

     

     

     

     

     

    void

    startScroll(int startX, int startY, int dx, int dy)

    Start scrolling by providing a starting point and the distance to travel.

    设置一个scrolling行为模型,即在int duration(单位为毫秒)时间的内从int startX, int startY,这个点起向X和Y方向分别滚动int dx和int dy个像素

     

     

     

     

     

     

     

     

     

     

    void

    startScroll(int startX, int startY, int dx, int dy, int duration)

    Start scrolling by providing a starting point and the distance to travel.

    设置一个scrolling行为模型,即在默认时间(250毫秒)内从int startX, int startY,这个点起向X和Y方向分别滚动int dx和int dy个像素

     

     

     

     

     

     

     

     

     

     

    int

    timePassed()

    Returns the time elapsed since the beginning of the scrolling.

    取得从scroll开始到现在已经失去的时间

     

     

     

     

     

     

     

     

     

     

     

    展开全文
  • Android 带你从源码的角度解析Scroller的滚动实现原理

    万次阅读 多人点赞 2014-01-10 14:38:28
    今天给大家讲解的是Scroller类的滚动实现原理,可能很多朋友不太了解该类是用来干嘛的,但是研究Launcher的朋友应该对他很熟悉,Scroller类是滚动的一个封装类,可以实现View的平滑滚动效果,什么是实现View的平滑...
  • scroller>滚动组件,可以容纳子组件进行横向或竖向滚动。如果组件需要进行滚动,可以将<scroller>当作根元素或者父元素使用,否则页面无法滚动(<list>组件除外,<list>默认可以滚动)。 使用...
  • 滑动过渡之Scroller

    2017-12-26 10:34:15
    Scroller:手指滑动中比较重要的一个辅助类,辅助完成一些动画参数的计算等。看到Scroller你可能感到陌生,但我们每个人都用过它,因为viewpage,listvew等控件源码都会用到它。而本文主要实现一个简单的viewpage效果...
  • Scroller 译为“滚动器”,是 ViewGroup 类中原生支持的一个功能。我们经常有这样的体验:打开联系人,手指向上滑动,联系人列表也会跟着一起滑动,但是,当我们松手之后,滑动并不会因此而停止,而是伴随着一段惯性...
  • vue-scroller记录滚动位置 直接修改vue-scroller包里面的东西 vue-scroller.min.js的源代码中查找: 原:this.resizeTimer=setInterval(function(){var e=o(),n=e.width,i=e.height;n===a&&i===s|| (a=n,s...
  • Android开发之Scroller的使用详解

    千次阅读 2015-03-06 18:37:54
    其实关于Scroller这篇文章我早就想写了,起初是因为看Xlistview的源码,接触到了Scroller的使用,之后又在网上读了大量的文章,之后自己写了一个demo,对scroller也算是有了自己的看法。 我学习的道路是曲折的,所以...
  • 白鹭引擎之Scroller容器

    千次阅读 2018-08-26 00:06:09
    最近由我这个Unity3D大师负责公司H5项目,主要...我研究了一下Scroller,首先,如果要想有物体跟着Scroller滚动,那该物体必须放到Scroller的Group中。but,问题来了,Group里的物体是不可以被拖到滚动框...
  • 之前做项目时,要实现Activity从左往右滑出来的效果,那个时候我选择了属性动画,,最近了解了下Scroller,也可以用它实现Activity的滑动,接下来介绍它的用法。 Scroller并不会使View滑动,看看它的源码就会知道...
  • android 使用Scroller实现缓慢移动

    万次阅读 热门讨论 2013-05-31 21:09:08
    在Launcher中的Workspace中实现了左右屏幕切换效果,里面就用到了Scroller记录滑动轨迹,实现一种缓慢地向左或向右移动的效果,这里我对这种效果进行总结: 我们先看一个例子:点击按钮时红经块会从左边缓慢地移向...
  • VUX采坑记录--Scroller

    千次阅读 2018-12-05 10:16:36
    滑动是在移动端使用频率非常告的一个功能,但是vux组件不知什么原因,放弃维护了scroller这个组件。但是经过项目中的使用,发现这个组件虽然不维护了,但是基本能够满足日常的使用。以下就是在使用的过程中所踩的坑...
  • Scroller实现View的平滑滑动

    千次阅读 2019-05-26 12:08:08
    在之前介绍的关于View滑动的内容时,关于使用使用Scroller进行View的平滑滑动没有介绍,今天在这里补充一下。关于为什么使用Scroller滑动,这里就不多介绍,主要是因为使用scrollTo()或者scrollBy()都是瞬间完成,...
  • Egret 滚动控制容器(Scroller)和列表List

    千次阅读 2018-06-26 18:51:14
    屏幕的尺寸总是有限的,当内容已经超出屏幕的范围时,需要特殊的处理方式。EUI 利用滚动条实现此功能。通过拉动滚动条,可以在有限的屏幕中浏览...二、使用滚动控制容器(Scroller) + 列表组合的方式(List) 还对...
  • android Scroller使用小例子

    热门讨论 2020-07-28 23:33:26
    android Scroller使用小例子
  • Scroller实现View的滑动

    2017-11-17 01:24:04
    滑动时,是瞬间完成的,用户体验不佳(在手拖着移动的时候不会看出明显区别,如果有一段是自动完成的,scrollBy会瞬间完成,Scroller就平滑的多了) 这个弹性滑动又是怎么实现的呢? Scroller本身不能实现弹性...
  • 页面列表中需要加入滑动到底部加载新数据的功能,用了vue-scroller中的infinite方法,但还是会有问题,比如有时候上拉加载两次之后就不再加载新数据,目前还没找到解决方案。因为项目中刚好引用了vux,所以就改用vux...
  • 一直以来,Android 开发中绕不过去的话题就是自定义 View,曾几何时,考验一个...本文要讲解的一个技术点,正是广大开发者容易困惑的一个知识点————Scroller。为什么说它是一个容易让人困惑的内容呢?这是因为...
1 2 3 4 5 ... 20
收藏数 15,751
精华内容 6,300
关键字:

scroller