Scroller 源码
public class Scroller {
//....
private int mStartX; //起始坐标点 , X轴方向
private int mStartY; //起始坐标点 , Y轴方向
private int mCurrX; //当前坐标点 X轴, 即调用startScroll函数后,经过一定时间所达到的值
private int mCurrY; //当前坐标点 Y轴, 即调用startScroll函数后,经过一定时间所达到的值
private float mDeltaX; //应该继续滑动的距离, X轴方向
private float mDeltaY; //应该继续滑动的距离, Y轴方向
private boolean mFinished; //是否已经完成本次滑动操作, 如果完成则为 true
//构造函数
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;
}
/* Call this when you want to know the new location. If it returns true,
* the animation is not yet finished. loc will be altered to provide the
* new location. */
//根据当前已经消逝的时间计算当前的坐标点,保存在mCurrX和mCurrY值中
public boolean computeScrollOffset() {
if (mFinished) { //已经完成了本次动画控制,直接返回为false
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;
}
//开始一个动画控制,由(startX , startY)在duration时间内前进(dx,dy)个单位,即到达坐标为(startX+dx , startY+dy)出
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 fling(int startX, int startY, int velocityX, int velocityY,
int minX, int maxX, int minY, int maxY)
}
Scroller的原理
Scroller的用法
基本可概括为“三部曲”:
1、创建一个Scroller对象,一般在View的构造器中创建:
public PageLayout(Context context, AttributeSet attrs) {
super(context, attrs);
mScroller = new Scroller(context);
}
2、重写View的computeScroll()方法,下面的代码基本是不会变化的:
@Override
public void computeScroll() {
super.computeScroll();
if (mScroller.computeScrollOffset()) {
scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
postInvalidate();
}
}
3、调用startScroll()方法,startX和startY为开始滚动的坐标点,dx和dy为对应的偏移量 或
filling 一般在touch事件中处理
case MotionEvent.ACTION_UP:
...
// mScroller.startScroll(0, getScrollY(), 0, mDisScreenHeight - dScrollY);
mScroller.fling(scrollX, scrollY, -xVelocity, -yVelocity, 0, getWidth() -
chileView.getWidth(), 0, getHeight() - chileView.getHeight());
int startX = mScroller.getStartX();
...
break;
//简单demo
package com.example.myapplication.touch;
import android.content.Context;
import android.graphics.PointF;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.widget.Scroller;
public class TestScroller extends ViewGroup {
private RectF mChildeRectF;
private VelocityTracker velocityTracker;
private Scroller mScroller;
private int mMaxFlintVelocity, mMinFlintVelocity;
private int mChildMeasuredWidth, mChildMeasuredHeight;
private View mChildView;
private float mLastX, mLastY;
public TestScroller(Context context) {
this(context, null);
}
public TestScroller(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public TestScroller(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initData(context);
}
private void initData(Context context) {
mChildeRectF = new RectF();
mScroller = new Scroller(context, null, true);
ViewConfiguration viewConfiguration = ViewConfiguration.get(context);
mMaxFlintVelocity = viewConfiguration.getScaledMaximumFlingVelocity();
mMinFlintVelocity = viewConfiguration.getScaledMinimumFlingVelocity();
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
mChildView = getChildAt(0);
int measuredWidth = getMeasuredWidth();
int measuredHeight = getMeasuredHeight();
mChildMeasuredWidth = mChildView.getMeasuredWidth();
mChildMeasuredHeight = mChildView.getMeasuredHeight();
mChildeRectF.set(measuredWidth / 2 - mChildMeasuredWidth / 2, measuredHeight / 2 - mChildMeasuredHeight / 2, measuredWidth / 2 + mChildMeasuredWidth / 2, measuredHeight / 2 + mChildMeasuredHeight / 2);
mChildView.layout(measuredWidth / 2 - mChildMeasuredWidth / 2, measuredHeight / 2 - mChildMeasuredHeight / 2, measuredWidth / 2 + mChildMeasuredWidth / 2, measuredHeight / 2 + mChildMeasuredHeight / 2);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
measureChildren(widthMeasureSpec, heightMeasureSpec);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
super.onScrollChanged(l, t, oldl, oldt);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
return true;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (velocityTracker == null) {
velocityTracker = VelocityTracker.obtain();
}
velocityTracker.addMovement(event);
int action = event.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
mLastX = event.getX();
mLastY = event.getY();
if (!mScroller.isFinished()) {
mScroller.abortAnimation();
}
break;
case MotionEvent.ACTION_MOVE:
float ev_x = event.getX();
float ev_y = event.getY();
int disx = (int) (-ev_x + mLastX);
int disy = (int) (-ev_y + mLastY);
scrollBy(disx, disy);
mLastX = ev_x;
mLastY = ev_y;
break;
case MotionEvent.ACTION_UP:
//手指抬起,计算当前速率
float up_x = event.getX();
float up_y = event.getY();
velocityTracker.computeCurrentVelocity(1000, mMaxFlintVelocity);
int xVelocity = (int) velocityTracker.getXVelocity();
int yVelocity = (int) velocityTracker.getYVelocity();
int scrollX = getScrollX();
int scrollY = getScrollY();
if (Math.abs(xVelocity) > mMinFlintVelocity && Math.abs(yVelocity) > mMinFlintVelocity) {
mScroller.fling(scrollX, scrollY, -xVelocity, -yVelocity, 0, getWidth() - mChildView.getWidth(), 0, getHeight() - mChildView.getHeight());
int startX = mScroller.getStartX();
int startY = mScroller.getStartY();
int finalX = mScroller.getFinalX();
int finalY = mScroller.getFinalY();
mChildeRectF.set(mChildView.getLeft() - finalX, mChildView.getTop() - finalY, mChildView.getRight() - finalX, mChildView.getBottom() - finalY);
int dex_x, dex_y;
if (up_x > mLastX) {
//证明往右滑动
dex_x = finalX - startX;
} else {
dex_x = startX - finalX;
}
if (up_y > mLastY) {
dex_y = finalY - startY;
} else {
dex_y = startY - finalY;
}
// awakenScrollBars(mScroller.getDuration());
invalidate();
} else {
mChildeRectF.set(mChildView.getLeft() - scrollX, mChildView.getTop() - scrollY, mChildView.getRight() - scrollX, mChildView.getBottom() - scrollY);
}
if (velocityTracker != null) {
velocityTracker.clear();
}
break;
}
return true;
}
@Override
public void computeScroll() {
super.computeScroll();
if (mScroller.computeScrollOffset()) {
scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
postInvalidate();
}
}
}