1 前言

Scroller是一个专门用于处理滚动效果的工具类,一般在我们自定义View时,要实现滚动或者滑动效果,除了使用动画之外,我们往往会使用Scroller来帮我们完成。在android中如ViewPager、ListView等,内部也是使用了Scroller来实现的。

2 View的Scroll

在介绍Scroller之前,我们知道,在View中自带scroll属性,那就是View中有两个方法,可以完成view的滚动。他们是:

void    scrollBy(int x, int y)//滚动x,yMove the scrolled position of your view. void    scrollTo(int x, int y) //滚动到x ySet the scrolled position of your view. 

需要说明的的两点
1 根据android的坐标体系,当x为负时,向右滚动,为正时向左滚动,y为负向下滚动,y为正,向上滚动
2 以上两个滚动只是滚动的View的内容,并没有滚动View本身,需要滚动view本身时,可以考虑布局属性LayoutParams及属性动画。
因此,针对这一点:我们队某个View做scroll往往并无太大的意义,但是对于ViewGroup来说,其内容往往就是某个子View,对ViewGroup做scroll就有意义!
3 以上滚动是瞬间完成,并没有过渡过程。

另外,以下两个API也是需要注意的,分别返回滚动时的x,y

final int getScrollX() //返回滚动时的left位置 xReturn the scrolled left position of this view. final int getScrollY()  //返回滚动时的top 位置 yReturn the scrolled top position of this view. 

下面来看一个View使用scroll的例子

<?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);            }        });    }}

效果如下:

3 Scroller API

一般来说,我们使用Scroller主要涉及到以下几个api

final int   getCurrX()  //获取当前的x偏移Returns the current X offset in the scroll.final int   getCurrY()  //获取当前的y偏移Returns the current Y offset in the scroll.final int   getDuration()   //获取持续时间Returns how long the scroll event will take, in milliseconds. //开始滚动void    startScroll(int startX, int startY, int dx, int dy, int duration)Start scrolling by providing a starting point, the distance to travel, and the duration of the scroll.//开始滚动void    startScroll(int startX, int startY, int dx, int dy)Start scrolling by providing a starting point and the distance to travel. //判断滚动是否完成,true表示没有完成boolean     computeScrollOffset()Call this when you want to know the new location. 

4 Scroller使用

Scroller的基本用法分为以下几个步骤
1 创建Scroller的实例
2 调用Scroller#startScroll()方法来初始化滚动数据并调用invalidate()刷新界面
3 重写View#computeScroll()方法,并在其内部完成平滑滚动的逻辑,在滚动时,会不断的调用这个方法。由于默认的View的computeScroll()方法为空,我们需要自己实现,不过一般代码也固定

例子如下:
我们自定义一个ViewGroup,仿照ViewPager的类似效果,水平布局

/** * Created by qiyei2015 on 17/6/19. */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();        }    }}

注意:在重写computeScroll()时,当中的代码往往是固定的,就是判断滚动有没有完成,没有完成就调用View的scrollTo()滚动到指定位置,然后invalidate()刷新界面。

<?xml version="1.0" encoding="utf-8"?><com.example.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.scrollertest.ScrollerLayout>

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

效果如下:

更多相关文章

  1. Android(安卓)UI框架 Android(安卓)UI控件类简介 android5大布局
  2. Android(安卓)利用addView 动态给Activity添加View组件
  3. Android之自制的分页表格控件
  4. Android(安卓)Studio的TextView实现滚动显示文字
  5. Android控件之利用selector自定义的带文字的图片按钮
  6. android 自定义控件学习之三 控件布局常用知识总结
  7. Android之打造ListView的万能适配器
  8. Android中UI设计的一些技巧!!!
  9. 将服务器端字符读取至android的文本控件,换行符变成了黑方块的原

随机推荐

  1. 《Android(安卓)智能穿戴设备开发-从入门
  2. android WebView总结
  3. 安卓开发中Spinner的基本用法(总结)
  4. 【Android进阶】android:configChanges属
  5. Android中的日历读写操作!!!
  6. Android安全机制详解
  7. Android(安卓)SDK目录结构介绍
  8. Android占领2010的六大理由
  9. zz浅谈android的selector,背景选择器
  10. android 核心组件( 2 )