首先先來實現一個Scroller滑動
1.新建一個View,給它畫上一個紅色的矩形,左定點座標是(100,100),並且在構造函數中初始化Scroller
public class MyView extends View {
private Scroller scroller;
private Paint paint = new Paint();
public MyView(Context context) {
super(context);
scroller = new Scroller(context);
}
public MyView(Context context, AttributeSet attrs) {
super(context, attrs);
scroller = new Scroller(context);
}
public MyView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
scroller = new Scroller(context);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
paint.setColor(Color.RED);
canvas.drawRect(100, 100, 300, 300, paint);
}
2.重寫view的computeScroll()方法,系統繪製view的時候會在draw()方法調用computeScroll()。
@Override
public void computeScroll() {
super.computeScroll();
if (scroller.computeScrollOffset()) {
//通過Scroller來獲取當前的滾動值
scrollTo(scroller.getCurrX(), scroller.getCurrY());
//重繪,會重新調用computeScroll() 不斷移動view
invalidate();
}
}
3.寫一個方法開始移動view,裏面調用startScroll()
方法
public void smoothScrollTo(int destX, int destY) {
int scrollX = getScrollX();
int deltaX = destX - scrollX;
scroller.startScroll(scrollX, 0, -deltaX, 0, 10000);
invalidate();
}
4.在佈局文件中引用,Activity加載,寫一個按鈕控制smoothScrollTo()方法
public void startScroller(View view) {
myView.smoothScrollTo(400, 100);
}
5.點擊按鈕後,效果圖
分析源代碼
1.構造函數,有三個構造方法,不過第一種用的最廣,第二種,傳入一個插值器,然後在第三種中判斷了一下插值器是否爲空,不爲空則使用默認的ViscousFluidInterpolator插值器
public Scroller(Context context) {
this(context, null);
}
public Scroller(Context context, Interpolator interpolator) {
this(context, interpolator,
context.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.HONEYCOMB);
}
public Scroller(Context context, Interpolator interpolator, boolean flywheel) {
mFinished = true;
if (interpolator == null) {
//判斷插值器是否爲空
mInterpolator = new ViscousFluidInterpolator();
} else {
mInterpolator = interpolator;
}
mPpi = context.getResources().getDisplayMetrics().density * 160.0f;
mDeceleration = computeDeceleration(ViewConfiguration.getScrollFriction());
mFlywheel = flywheel;
mPhysicalCoeff = computeDeceleration(0.84f); // look and feel tuning
}
2.主要方法
- startScroll(),有4個參數的和5個參數的,4個參數本質上也是調用了5個參數的startScroll(),區別在4個參數的用了默認的duration。
startX
:開始滑動的起點x
startY
:開始滑動的起點y
dx
:滑動的距離x,使用時注意正負
dy
:滑動的距離y,使用時注意正負
public void startScroll(int startX, int startY, int dx, int dy) {
startScroll(startX, startY, dx, dy, DEFAULT_DURATION);
}
public void startScroll(int startX, int startY, int dx, int dy, int duration) {
mMode = SCROLL_MODE;
mFinished = false;
mDuration = duration;
mStartTime = AnimationUtils.currentAnimationTimeMillis();
mStartX = startX;
mStartY = startY;
mFinalX = startX + dx;
mFinalY = startY + dy;
mDeltaX = dx;
mDeltaY = dy;
mDurationReciprocal = 1.0f / (float) mDuration;
}
startScroll()方法是用來保存傳進來的各種參數的,沒有用來具體開啓滑動的邏輯,而我們要使View滑動,startScroll()要用到invalidate()來使view進行重繪,從而調用draw()方法,這樣draw()方法裏的就會call到View中的computeScroll()方法。View中的computeScroll()方法是空實現,我們需要重寫它
3.重寫computeScroll()方法
@Override
public void computeScroll() {
super.computeScroll();
if (scroller.computeScrollOffset()) {
//滾動的邏輯
scrollTo(scroller.getCurrX(), scroller.getCurrY());
//接着重繪
invalidate();
}
}
可以看到computeScroll()裏用到了scrollTo()以滾動View,接着調用invalidate()
會重新call到View中的draw()方法,從而不斷的調用computeScroll()方法,使view一點一點的滑動,從而實現了平滑滾動。
4.其中if判斷框中的computeScrollOffset()
是用來獲取當前位置的ScrollX和ScrollY的,如果返回true這說明滑動未結束
public boolean computeScrollOffset() {
if (mFinished) {
return false;
}
//動畫持續時間
int timePassed = (int)(AnimationUtils.currentAnimationTimeMillis() - mStartTime);
//如果當前的動畫持續時間小於設置的滑動持續時間(其實就是當前view滾動還沒完的意思)
if (timePassed < mDuration) {
switch (mMode) {
case SCROLL_MODE:
//通過插值器計算該時間段移動的距離mCurrX ,mCurrX
final float x = mInterpolator.getInterpolation(timePassed * mDurationReciprocal);
mCurrX = mStartX + Math.round(x * mDeltaX);
mCurrY = mStartY + Math.round(x * mDeltaY);
break;
case FLING_MODE:
...//省略
}
else {
mCurrX = mFinalX;
mCurrY = mFinalY;
mFinished = true;
}
return true;
}
5.總結整個過程就是
end