正文
一、结构
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)
强制终止的字段到特定值。(译者注:立即停止滚动?)
参数
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 ()
返回自滚动开始经过的时间
返回值
经过时间以毫秒为单位
五、补充
文章精选
Scroller 粗浅理解
ScrollTextView - scrolling TextView for Android
Android里Scroller类是为了实现View平滑滚动的一个Helper类。通常在自定义的View时使用,在View中定义一个私有成员mScroller = new Scroller(context)。设置mScroller滚动的位置时,并不会导致View的滚动,通常是用mScroller记录/计算View滚动的位置,再重写View的computeScroll(),完成实际的滚动。
相关API介绍如下
- mScroller.getCurrX()
- mScroller.getCurrY()
- mScroller.getFinalX()
- mScroller.getFinalY()
- mScroller.setFinalX(int newX)
- mScroller.setFinalY(int newY)
-
-
- mScroller.startScroll(int startX, int startY, int dx, int dy)
- mScroller.startScroll(int startX, int startY, int dx, int dy, int duration)
-
- mScroller.computeScrollOffset()
举例说明,自定义一个CustomView,使用Scroller实现滚动:
- import android.content.Context;
- import android.util.AttributeSet;
- import android.util.Log;
- import android.view.View;
- import android.widget.LinearLayout;
- import android.widget.Scroller;
-
- public class CustomView extends LinearLayout {
-
- private static final String TAG = "Scroller";
-
- private Scroller mScroller;
-
- public CustomView(Context context, AttributeSet attrs) {
- super(context, attrs);
- mScroller = new Scroller(context);
- }
-
-
- public void smoothScrollTo(int fx, int fy) {
- int dx = fx - mScroller.getFinalX();
- int dy = fy - mScroller.getFinalY();
- smoothScrollBy(dx, dy);
- }
-
-
- public void smoothScrollBy(int dx, int dy) {
-
-
- mScroller.startScroll(mScroller.getFinalX(), mScroller.getFinalY(), dx, dy);
- invalidate();
- }
-
- @Override
- public void computeScroll() {
-
-
- if (mScroller.computeScrollOffset()) {
-
-
- scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
-
-
- postInvalidate();
- }
- super.computeScroll();
- }
- }
Demo的下载地址: http://download.csdn.net/detail/linghu_java/5423269
下面更深一点介绍Scrooler:
友情提示:
在继续往下面读之前,希望您对以下知识点有一定程度掌握,否则,继续看下去对您意义也不大。
1、掌握View(视图)的"视图坐标"以及"布局坐标",以及scrollTo()和scrollBy()方法的作用 ----- 必须理解
如果对这方面知识不太清楚的话,建议先看看我的这篇博客
<Android中滑屏初探 ---- scrollTo 以及 scrollBy方法使用说明>,
不夸张地说,这篇博客理论上来说是我们这篇博文的基础。
2、知道onInterceptTouchEvent()以及onTouchEvent()对触摸事件的分发流程 ---- 不是必须
3、知道怎么绘制自定义ViewGroup即可 ---- 不是必须
OK。 继续往下看,请一定有所准备 。大家跟着我一步一步来咯。
知识点一: 关于scrollTo()和scrollBy()以及偏移坐标的设置/取值问题
在前面一篇博文中《Android中滑屏初探 ---- scrollTo 以及 scrollBy方法使用说明》,我们掌握了scrollTo()和
scrollBy()方法的作用,这两个方法的主要作用是将View/ViewGroup移至指定的坐标中,并且将偏移量保存起来。另外:
mScrollX 代表X轴方向的偏移坐标
mScrollY 代表Y轴方向的偏移坐标
关于偏移量的设置我们可以参看下源码:
- package com.qin.customviewgroup;
-
- public class View {
- ....
- protected int mScrollX;
- protected int mScrollY;
-
- public final int getScrollX() {
- return mScrollX;
- }
- public final int getScrollY() {
- return mScrollY;
- }
- public void scrollTo(int x, int y) {
-
- if (mScrollX != x || mScrollY != y) {
- int oldX = mScrollX;
- int oldY = mScrollY;
- mScrollX = x;
- mScrollY = y;
-
- onScrollChanged(mScrollX, mScrollY, oldX, oldY);
- if (!awakenScrollBars()) {
- invalidate();
- }
- }
- }
-
- public void scrollBy(int x, int y) {
- scrollTo(mScrollX + x, mScrollY + y);
- }
-
- }
于是,在任何时刻我们都可以获取该View/ViewGroup的偏移位置了,即调用getScrollX()方法和getScrollY()方法
知识点二: Scroller类的介绍
在初次看Launcher滑屏的时候,我就对Scroller类的学习感到非常蛋疼,完全失去了继续研究的欲望。如今,没得办法,
得重新看Launcher模块,基本上将Launcher大部分类以及功能给掌握了。当然,也花了一天时间来学习Launcher里的滑屏实现
,基本上业是拨开云雾见真知了。
我们知道想把一个View偏移至指定坐标(x,y)处,利用scrollTo()方法直接调用就OK了,但我们不能忽视的是,该方法本身
来的的副作用:非常迅速的将View/ViewGroup偏移至目标点,而没有对这个偏移过程有任何控制,对用户而言可能是不太
友好的。于是,基于这种偏移控制,Scroller类被设计出来了,该类的主要作用是为偏移过程制定一定的控制流程(后面我们会
知道的更多),从而使偏移更流畅,更完美。
可能上面说的比较悬乎,道理也没有讲透。下面我就根据特定情景帮助大家分析下:
情景: 从上海如何到武汉?
普通的人可能会想,so easy : 飞机、轮船、11路公交车...
文艺的人可能会想, 小 case : 时空忍术(火影的招数)、翻个筋斗(孙大圣的招数)...
不管怎么样,我们想出来的套路可能有两种:
1、有个时间控制过程才能抵达(缓慢的前进) ----- 对应于Scroller的作用
假设做火车,这个过程可能包括: 火车速率,花费周期等;
2、瞬间抵达(超神太快了,都眩晕了,用户体验不太好) ------ 对应于scrollTo()的作用
模拟Scroller类的实现功能:
假设从上海做动车到武汉需要10个小时,行进距离为1000km ,火车速率200/h 。采用第一种时间控制方法到达武汉的
整个配合过程可能如下:
我们每隔一段时间(例如1小时),计算火车应该行进的距离,然后调用scrollTo()方法,行进至该处。10小时过完后,
我们也就达到了目的地了。
相信大家心里应该有个感觉了。我们就分析下源码里去看看Scroller类的相关方法.
其源代码(部分)如下: 路径位于 \frameworks\base\core\java\android\widget\Scroller.java
- public class Scroller {
-
- private int mStartX;
- private int mStartY;
- private int mCurrX;
- private int mCurrY;
-
- private float mDeltaX;
- private float mDeltaY;
- private boolean mFinished;
-
-
- public Scroller(Context context) {
- this(context, null);
- }
- public final boolean isFinished() {
- return mFinished;
- }
-
- public final void forceFinished(boolean finished) {
- mFinished = finished;
- }
- public final int getCurrX() {
- return mCurrX;
- }
-
-
-
-
- public boolean computeScrollOffset() {
- if (mFinished) {
- return false;
- }
- int timePassed = (int)(AnimationUtils.currentAnimationTimeMillis() - mStartTime);
- if (timePassed < mDuration) {
- switch (mMode) {
- case SCROLL_MODE:
- float x = (float)timePassed * mDurationReciprocal;
- ...
- mCurrX = mStartX + Math.round(x * mDeltaX);
- mCurrY = mStartY + Math.round(x * mDeltaY);
- break;
- ...
- }
- else {
- mCurrX = mFinalX;
- mCurrY = mFinalY;
- mFinished = true;
- }
- return true;
- }
-
- public void startScroll(int startX, int startY, int dx, int dy, int duration) {
- mFinished = false;
- mDuration = duration;
- mStartTime = AnimationUtils.currentAnimationTimeMillis();
- mStartX = startX; mStartY = startY;
- mFinalX = startX + dx; mFinalY = startY + dy;
- mDeltaX = dx; mDeltaY = dy;
- ...
- }
- }
其中比较重要的两个方法为:
public void startScroll(int startX, int startY, int dx, int dy, int duration)
函数功能说明:根据当前已经消逝的时间计算当前的坐标点,保存在mCurrX和mCurrY值中
public void startScroll(int startX, int startY, int dx, int dy, int duration)
函数功能说明:开始一个动画控制,由(startX , startY)在duration时间内前进(dx,dy)个单位,到达坐标为
(startX+dx , startY+dy)处。
PS : 强烈建议大家看看该类的源码,便于后续理解。
知识点二: computeScroll()方法介绍
为了易于控制滑屏控制,Android框架提供了 computeScroll()方法去控制这个流程。在绘制View时,会在draw()过程调用该
方法。因此, 再配合使用Scroller实例,我们就可以获得当前应该的偏移坐标,手动使View/ViewGroup偏移至该处。
computeScroll()方法原型如下,该方法位于ViewGroup.java类中
-
-
-
-
-
- 由父视图调用用来请求子视图根据偏移值 mScrollX,mScrollY重新绘制
- public void computeScroll() {
-
- }
为了实现偏移控制,一般自定义View/ViewGroup都需要重载该方法 。
其调用过程位于View绘制流程draw()过程中,如下:
- @Override
- protected void dispatchDraw(Canvas canvas){
- ...
-
- for (int i = 0; i < count; i++) {
- final View child = children[getChildDrawingOrder(count, i)];
- if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
- more |= drawChild(canvas, child, drawingTime);
- }
- }
- }
- protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
- ...
- child.computeScroll();
- ...
- }
Demo说明:
我们简单的复用了之前写的一个自定义ViewGroup,与以前一次有区别的是,我们没有调用scrollTo()方法去进行瞬间
偏移。 本次做法如下:
第一、调用Scroller实例去产生一个偏移控制(对应于startScroll()方法)
第二、手动调用invalid()方法去重新绘制,剩下的就是在 computeScroll()里根据当前已经逝去的时间,获取当前
应该偏移的坐标(由Scroller实例对应的computeScrollOffset()计算而得),
第三、当前应该偏移的坐标,调用scrollBy()方法去缓慢移动至该坐标处。
截图如下:
![[置顶] Android的Scroller介绍_第2张图片](http://img.e-com-net.com/image/info5/e7e62553522342ea901e1d4728fed8d2.png)
原始界面 点击按钮或者触摸屏之后的显示界面
附:由于滑动截屏很难,只是简单的截取了两个个静态图片,触摸的话可以实现左右滑动切屏了。
更多知识点,请看代码注释。。
-
- public class MultiViewGroup extends ViewGroup {
- ...
-
- public void startMove(){
- curScreen ++ ;
- Log.i(TAG, "----startMove---- curScreen " + curScreen);
-
-
- mScroller.startScroll((curScreen-1) * getWidth(), 0, getWidth(), 0,3000);
-
- invalidate();
-
-
- }
-
- @Override
- public void computeScroll() {
-
- Log.e(TAG, "computeScroll");
-
-
- if (mScroller.computeScrollOffset()) {
- Log.e(TAG, mScroller.getCurrX() + "======" + mScroller.getCurrY());
-
- scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
-
- Log.e(TAG, "### getleft is " + getLeft() + " ### getRight is " + getRight());
-
- postInvalidate();
- }
- else
- Log.i(TAG, "have done the scoller -----");
- }
-
- public void stopMove(){
-
- Log.v(TAG, "----stopMove ----");
-
- if(mScroller != null){
-
- if(!mScroller.isFinished()){
-
- int scrollCurX= mScroller.getCurrX() ;
-
-
-
-
- int descScreen = ( scrollCurX + getWidth() / 2) / getWidth() ;
-
- Log.i(TAG, "-mScroller.is not finished scrollCurX +" + scrollCurX);
- Log.i(TAG, "-mScroller.is not finished descScreen +" + descScreen);
- mScroller.abortAnimation();
-
-
- scrollTo(descScreen *getWidth() , 0);
- curScreen = descScreen ;
- }
- else
- Log.i(TAG, "----OK mScroller.is finished ---- ");
- }
- }
- ...
- }
如何实现触摸滑屏?
其实网上有很多关于Launcher实现滑屏的博文,基本上也把道理阐释的比较明白了 。我这儿也是基于自己的理解,将一些
重要方面的知识点给补充下,希望能帮助大家理解。
想要实现滑屏操作,值得考虑的事情包括如下几个方面:
其中:onInterceptTouchEvent()主要功能是控制触摸事件的分发,例如是子视图的点击事件还是滑动事件。
其他所有处理过程均在onTouchEvent()方法里实现了。
1、屏幕的滑动要根据手指的移动而移动 ---- 主要实现在onTouchEvent()方法中
2、当手指松开时,可能我们并没有完全滑动至某个屏幕上,这是我们需要手动判断当前偏移至去计算目标屏(当前屏或者
前后屏),并且优雅的偏移到目标屏(当然是用Scroller实例咯)。
3、调用computeScroll ()去实现缓慢移动过程。
知识点介绍:
VelocityTracker类
功能: 根据触摸位置计算每像素的移动速率。
常用方法有:
public void addMovement (MotionEvent ev)
功能:添加触摸对象MotionEvent , 用于计算触摸速率。
public void computeCurrentVelocity (int units)
功能:以每像素units单位考核移动速率。额,其实我也不太懂,赋予值1000即可。
参照源码 该units的意思如下:
参数 units : The units you would like the velocity in. A value of 1
provides pixels per millisecond, 1000 provides pixels per second, etc.
public float getXVelocity ()
功能:获得X轴方向的移动速率。
ViewConfiguration类
功能: 获得一些关于timeouts(时间)、sizes(大小)、distances(距离)的标准常量值 。
常用方法:
public int getScaledEdgeSlop()
说明:获得一个触摸移动的最小像素值。也就是说,只有超过了这个值,才代表我们该滑屏处理了。
public static int getLongPressTimeout()
说明:获得一个执行长按事件监听(onLongClickListener)的值。也就是说,对某个View按下触摸时,只有超过了
这个时间值在,才表示我们该对该View回调长按事件了;否则,小于这个时间点松开手指,只执行onClick监听
我能写下来的也就这么多了,更多的东西参考代码注释吧。 在掌握了上面我罗列的知识后(重点scrollTo、Scroller类),
其他方面的知识都是关于点与点之间的计算了以及触摸事件的分发了。这方面感觉也没啥可写的。
-
- public class MultiViewGroup extends ViewGroup {
-
- private static String TAG = "MultiViewGroup";
-
- private int curScreen = 0 ;
- private Scroller mScroller = null ;
-
- public MultiViewGroup(Context context) {
- super(context);
- mContext = context;
- init();
- }
- public MultiViewGroup(Context context, AttributeSet attrs) {
- super(context, attrs);
- mContext = context;
- init();
- }
-
- private void init() {
- ...
-
- mScroller = new Scroller(mContext);
-
- ...
-
- mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
- }
-
- @Override
- public void computeScroll() {
-
- Log.e(TAG, "computeScroll");
-
-
- if (mScroller.computeScrollOffset()) {
- Log.e(TAG, mScroller.getCurrX() + "======" + mScroller.getCurrY());
-
- scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
-
- Log.e(TAG, "### getleft is " + getLeft() + " ### getRight is " + getRight());
-
- postInvalidate();
- }
- else
- Log.i(TAG, "have done the scoller -----");
- }
-
- private static final int TOUCH_STATE_REST = 0;
- private static final int TOUCH_STATE_SCROLLING = 1;
- private int mTouchState = TOUCH_STATE_REST;
-
-
- public static int SNAP_VELOCITY = 600 ;
- private int mTouchSlop = 0 ;
- private float mLastionMotionX = 0 ;
-
- private VelocityTracker mVelocityTracker = null ;
-
-
- @Override
- public boolean onInterceptTouchEvent(MotionEvent ev) {
-
- Log.e(TAG, "onInterceptTouchEvent-slop:" + mTouchSlop);
-
- final int action = ev.getAction();
-
-
- if ((action == MotionEvent.ACTION_MOVE) && (mTouchState != TOUCH_STATE_REST)) {
- return true;
- }
-
- final float x = ev.getX();
- final float y = ev.getY();
-
- switch (action) {
- case MotionEvent.ACTION_MOVE:
- Log.e(TAG, "onInterceptTouchEvent move");
- final int xDiff = (int) Math.abs(mLastionMotionX - x);
-
- if (xDiff > mTouchSlop) {
- mTouchState = TOUCH_STATE_SCROLLING;
- }
- break;
-
- case MotionEvent.ACTION_DOWN:
- Log.e(TAG, "onInterceptTouchEvent down");
- mLastionMotionX = x;
- mLastMotionY = y;
- Log.e(TAG, mScroller.isFinished() + "");
- mTouchState = mScroller.isFinished() ? TOUCH_STATE_REST : TOUCH_STATE_SCROLLING;
-
- break;
-
- case MotionEvent.ACTION_CANCEL:
- case MotionEvent.ACTION_UP:
- Log.e(TAG, "onInterceptTouchEvent up or cancel");
- mTouchState = TOUCH_STATE_REST;
- break;
- }
- Log.e(TAG, mTouchState + "====" + TOUCH_STATE_REST);
- return mTouchState != TOUCH_STATE_REST;
- }
- public boolean onTouchEvent(MotionEvent event){
-
- super.onTouchEvent(event);
-
- Log.i(TAG, "--- onTouchEvent--> " );
-
-
- Log.e(TAG, "onTouchEvent start");
-
- if (mVelocityTracker == null) {
- mVelocityTracker = VelocityTracker.obtain();
- }
- mVelocityTracker.addMovement(event);
-
-
- float x = event.getX();
- float y = event.getY();
- switch(event.getAction()){
- case MotionEvent.ACTION_DOWN:
-
- if(mScroller != null){
- if(!mScroller.isFinished()){
- mScroller.abortAnimation();
- }
- }
- mLastionMotionX = x ;
- break ;
- case MotionEvent.ACTION_MOVE:
- int detaX = (int)(mLastionMotionX - x );
- scrollBy(detaX, 0);
-
- Log.e(TAG, "--- MotionEvent.ACTION_MOVE--> detaX is " + detaX );
- mLastionMotionX = x ;
- break ;
- case MotionEvent.ACTION_UP:
-
- final VelocityTracker velocityTracker = mVelocityTracker ;
- velocityTracker.computeCurrentVelocity(1000);
-
- int velocityX = (int) velocityTracker.getXVelocity() ;
- Log.e(TAG , "---velocityX---" + velocityX);
-
-
- if (velocityX > SNAP_VELOCITY && curScreen > 0) {
-
- Log.e(TAG, "snap left");
- snapToScreen(curScreen - 1);
- }
-
- else if(velocityX < -SNAP_VELOCITY && curScreen < (getChildCount()-1)){
- Log.e(TAG, "snap right");
- snapToScreen(curScreen + 1);
- }
-
- else{
-
- snapToDestination();
- }
-
- if (mVelocityTracker != null) {
- mVelocityTracker.recycle();
- mVelocityTracker = null;
- }
-
- mTouchState = TOUCH_STATE_REST ;
-
- break;
- case MotionEvent.ACTION_CANCEL:
- mTouchState = TOUCH_STATE_REST ;
- break;
- }
-
- return true ;
- }
-
- private void snapToDestination(){
-
- int scrollX = getScrollX() ;
- int scrollY = getScrollY() ;
-
- Log.e(TAG, "### onTouchEvent snapToDestination ### scrollX is " + scrollX);
-
-
-
-
-
- int destScreen = (getScrollX() + MultiScreenActivity.screenWidth / 2 ) / MultiScreenActivity.screenWidth ;
-
- Log.e(TAG, "### onTouchEvent ACTION_UP### dx destScreen " + destScreen);
-
- snapToScreen(destScreen);
- }
-
- private void snapToScreen(int whichScreen){
-
-
-
-
-
-
- curScreen = whichScreen ;
-
- if(curScreen > getChildCount() - 1)
- curScreen = getChildCount() - 1 ;
-
- int dx = curScreen * getWidth() - getScrollX() ;
-
- Log.e(TAG, "### onTouchEvent ACTION_UP### dx is " + dx);
-
- mScroller.startScroll(getScrollX(), 0, dx, 0,Math.abs(dx) * 2);
-
-
- invalidate();
- }
-
- public void startMove(){
- ...
- }
-
- public void stopMove(){
- ...
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- ...
- }
-
- @Override
- protected void onLayout(boolean changed, int l, int t, int r, int b) {
- ...
- }
- }
demo地址:http://download.csdn.net/detail/linghu_java/5573479
最后,希望大家能多多实践,最好能写个小Demo出来。那样才正在的掌握了所学所得。