編輯:關於Android編程
Scroller是Android中View平滑移動的一個輔助類,對於剛接觸Scroller的人群來說它可能難以理解:
1、它是怎樣滑動View的(如何與View關聯的)?
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;
}
簡單到讓人無法相信,僅僅是設置了一下動畫開始時間,起始坐標,終點坐標、時間倒數(僅僅是方便計算而已)等變量而已;其實這樣我們的動畫可以說已經開始了,只是沒有根據坐標繪制到界面上而已;因為它這裡保存了開始時間,當平滑開始的時候,Scroller就可以根據滑動的時間差來計算當前坐標應該處的位置,View根據坐標invalidate就可以滑動了;
當然這裡影響到坐標計算的還有一個就是加速器,在重載的構造方法方法 public Scroller(Context context, Interpolator interpolator) 裡面有詳述:這裡我們也可以自定義自己的加速器,具體原理與如何自定義這裡就不闡述了;
public boolean computeScrollOffset() {
if (mFinished) {
return false;
}
int timePassed = (int)(AnimationUtils.currentAnimationTimeMillis() - mStartTime);
if (timePassed < mDuration) {
switch (mMode) {
case SCROLL_MODE:
float x = timePassed * mDurationReciprocal;
if (mInterpolator == null)
x = viscousFluid(x);
else
x = mInterpolator.getInterpolation(x);
mCurrX = mStartX + Math.round(x * mDeltaX);
mCurrY = mStartY + Math.round(x * mDeltaY);
break;
case FLING_MODE:
final float t = (float) timePassed / mDuration;
final int index = (int) (NB_SAMPLES * t);
final float t_inf = (float) index / NB_SAMPLES;
final float t_sup = (float) (index + 1) / NB_SAMPLES;
final float d_inf = SPLINE[index];
final float d_sup = SPLINE[index + 1];
final float distanceCoef = d_inf + (t - t_inf) / (t_sup - t_inf) * (d_sup - d_inf);
mCurrX = mStartX + Math.round(distanceCoef * (mFinalX - mStartX));
// Pin to mMinX <= mCurrX <= mMaxX
mCurrX = Math.min(mCurrX, mMaxX);
mCurrX = Math.max(mCurrX, mMinX);
mCurrY = mStartY + Math.round(distanceCoef * (mFinalY - mStartY));
// Pin to mMinY <= mCurrY <= mMaxY
mCurrY = Math.min(mCurrY, mMaxY);
mCurrY = Math.max(mCurrY, mMinY);
if (mCurrX == mFinalX && mCurrY == mFinalY) {
mFinished = true;
}
break;
}
}
else {
mCurrX = mFinalX;
mCurrY = mFinalY;
mFinished = true;
}
return true;
}
3、fling()方法:
public void fling(int startX, int startY, int velocityX, int velocityY,
int minX, int maxX, int minY, int maxY) {
// Continue a scroll or fling in progress
if (mFlywheel && !mFinished) {
float oldVel = getCurrVelocity();
float dx = (float) (mFinalX - mStartX);
float dy = (float) (mFinalY - mStartY);
float hyp = FloatMath.sqrt(dx * dx + dy * dy);
float ndx = dx / hyp;
float ndy = dy / hyp;
float oldVelocityX = ndx * oldVel;
float oldVelocityY = ndy * oldVel;
if (Math.signum(velocityX) == Math.signum(oldVelocityX) &&
Math.signum(velocityY) == Math.signum(oldVelocityY)) {
velocityX += oldVelocityX;
velocityY += oldVelocityY;
}
}
mMode = FLING_MODE;
mFinished = false;
float velocity = FloatMath.sqrt(velocityX * velocityX + velocityY * velocityY);
mVelocity = velocity;
final double l = Math.log(START_TENSION * velocity / ALPHA);
mDuration = (int) (1000.0 * Math.exp(l / (DECELERATION_RATE - 1.0)));
mStartTime = AnimationUtils.currentAnimationTimeMillis();
mStartX = startX;
mStartY = startY;
float coeffX = velocity == 0 ? 1.0f : velocityX / velocity;
float coeffY = velocity == 0 ? 1.0f : velocityY / velocity;
int totalDistance =
(int) (ALPHA * Math.exp(DECELERATION_RATE / (DECELERATION_RATE - 1.0) * l));
mMinX = minX;
mMaxX = maxX;
mMinY = minY;
mMaxY = maxY;
mFinalX = startX + Math.round(totalDistance * coeffX);
// Pin to mMinX <= mFinalX <= mMaxX
mFinalX = Math.min(mFinalX, mMaxX);
mFinalX = Math.max(mFinalX, mMinX);
mFinalY = startY + Math.round(totalDistance * coeffY);
// Pin to mMinY <= mFinalY <= mMaxY
mFinalY = Math.min(mFinalY, mMaxY);
mFinalY = Math.max(mFinalY, mMinY);
}
滑動,這個滑就跟GestureDetector.OnGestureListener這個接口中的onFling事件一樣,根據滑動速率來判斷一些事件。主要處理一些一些慣性坐標變化(慣性行為)。用得比較少;
4、abortAnimation()方法:
public void abortAnimation() {
mCurrX = mFinalX;
mCurrY = mFinalY;
mFinished = true;
}
看源碼,停止動畫(就是設置一個標記而已,不再計算坐標的變化值)
static {
float x_min = 0.0f;
for (int i = 0; i <= NB_SAMPLES; i++) {
final float t = (float) i / NB_SAMPLES;
float x_max = 1.0f;
float x, tx, coef;
while (true) {
x = x_min + (x_max - x_min) / 2.0f;
coef = 3.0f * x * (1.0f - x);
tx = coef * ((1.0f - x) * START_TENSION + x * END_TENSION) + x * x * x;
if (Math.abs(tx - t) < 1E-5) break;
if (tx > t) x_max = x;
else x_min = x;
}
final float d = coef + x * x * x;
SPLINE[i] = d;
}
SPLINE[NB_SAMPLES] = 1.0f;
// This controls the viscous fluid effect (how much of it)
sViscousFluidScale = 8.0f;
// must be set to 1.0 (used in viscousFluid())
sViscousFluidNormalize = 1.0f;
sViscousFluidNormalize = 1.0f / viscousFluid(1.0f);
}
這一段代碼讓我糾結了好久,照我的理解應該是把慣性滑動模式中的滑動距離比率(不好描述)分成100份,精確到0.00001,在慣性滑動的時候根據當前時間比率來計算當前坐標處於的位置;
其實說到這裡,感覺Android裡面的動畫,其實就是對於坐標的精確計算,這裡只是簡單的平滑滑動,有能力者我們也可以根據自己的需求寫出自己的坐標計算類來達到我們的需求;
Android 塗鴉最佳實踐
Android中實現手勢畫圖一般都兩種方式,一是直接在View上繪制,而是使用SurfaceView。兩者還是有一些差別的。簡介下。View:顯示視圖,內置畫布,提供圖形
Android自定義WaveView實現波浪進度效果
實現原理首先就是自定義個WaveView 繼承View,然後再WaveView 內部實現代碼邏輯: ① 水波就
Android自定義組合控件---教你如何自定義下拉刷新和左滑刪除
緒論最近項目裡面用到了下拉刷新和左滑刪除,網上找了找並沒有可以用的,有比較好的左滑刪除,但是並沒有和下拉刷新上拉加載結合到一起,要不就是一些比較水的結合,並不能在項目裡面
Android M Launcher3主流程源碼淺析
背景關於Launcher是啥的問題我想這裡就沒必要再強調了。由於一些原因迫使最近開始需要研究一下Launcher3源碼,為了不再像以前那麼傻逼(研究Settings等代碼