初步分析Scroller源码

158 阅读4分钟
Scroller的构造函数

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
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
一般使用第一个构造函数,如果不传入插值器的话,会调用默认的插值器ViscousFluidInterpolator。

Scroller的startScroll()方法

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;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
保存传进来的各种参数,startX和startY表示滑动开始的起点,mDeltax和mDeltay表示滑动的偏移量。startScroll方法只是用来保存一些数据。在startScroll方法后调用invalidate方法,会导致View重绘,View的draw方法会调用computeScroll方法。在computeScroller方法中,可以调用scrollTo方法,进行View的滑动。scrollTo方法中需要传入的参数,是通过前面调用computeScrollOffset方法完成。

Scroller的computecrollOffset方法

public boolean computeScrollOffset() {
if (mFinished) {
return false;
}

int timePassed = (int)(AnimationUtils.currentAnimationTimeMillis() - mStartTime);

if (timePassed < mDuration) {
switch (mMode) {
case SCROLL_MODE:
...
break;
case FLING_MODE:

final float t = (float) timePassed / mDuration;
final int index = (int) (NB_SAMPLES * t);

float distanceCoef = 1.f;

float velocityCoef = 0.f;
if (index < NB_SAMPLES) {
final float t_inf = (float) index / NB_SAMPLES;
final float t_sup = (float) (index + 1) / NB_SAMPLES;
final float d_inf = SPLINE_POSITION[index];
final float d_sup = SPLINE_POSITION[index + 1];
velocityCoef = (d_sup - d_inf) / (t_sup - t_inf);
distanceCoef = d_inf + (t - t_inf) * velocityCoef;
}


mCurrVelocity = velocityCoef * mDistance / mDuration * 1000.0f;

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;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
首先会计算动画持续时间,小于设置的mDuration时间,执行switch语句。在startScroll方法中的mMode值为SCROLL_MODE,通过插值器判断出移动的距离,并赋值给mCurrX和mCurrY。这样,后续就可以调用getCurrentX方法得到当前坐标值。

Scroller的fling方法

public void fling(int startX, int startY, int velocityX, int velocityY,
int minX, int maxX, int minY, int maxY) {

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;

mDuration = getSplineFlingDuration(velocity);
mStartTime = AnimationUtils.currentAnimationTimeMillis();
mStartX = startX;
mStartY = startY;

float coeffX = velocity == 0 ? 1.0f : velocityX / velocity;
float coeffY = velocity == 0 ? 1.0f : velocityY / velocity;

double totalDistance = getSplineFlingDistance(velocity);
mDistance = (int) (totalDistance * Math.signum(velocity));

mMinX = minX;
mMaxX = maxX;
mMinY = minY;
mMaxY = maxY;

mFinalX = startX + (int) Math.round(totalDistance * coeffX);
mFinalX = Math.min(mFinalX, mMaxX);
mFinalX = Math.max(mFinalX, mMinX);

mFinalY = startY + (int) Math.round(totalDistance * coeffY);
mFinalY = Math.min(mFinalY, mMaxY);
mFinalY = Math.max(mFinalY, mMinY);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
先判断一次滑动未结束又触发另一次滑动,是否需要累加加速度。设置mMode为FLING_MODE。根据velocityX和
velocityY方法算出总加速度。算出在总加速下可以滑动的距离。最后通过x和y方向上的加速度比值和设定的最大值和最小值给mFinalX或mFinalY赋值。然后调用invalidate方法,最后也会调用computeScrollOffset方法。

public boolean computeScrollOffset() {
if (mFinished) {
return false;
}

int timePassed = (int)(AnimationUtils.currentAnimationTimeMillis() - mStartTime);

if (timePassed < mDuration) {
switch (mMode) {
case SCROLL_MODE:
...
break;
case FLING_MODE:

final float t = (float) timePassed / mDuration;
final int index = (int) (NB_SAMPLES * t);

float distanceCoef = 1.f;

float velocityCoef = 0.f;
if (index < NB_SAMPLES) {
final float t_inf = (float) index / NB_SAMPLES;
final float t_sup = (float) (index + 1) / NB_SAMPLES;
final float d_inf = SPLINE_POSITION[index];
final float d_sup = SPLINE_POSITION[index + 1];
velocityCoef = (d_sup - d_inf) / (t_sup - t_inf);
distanceCoef = d_inf + (t - t_inf) * velocityCoef;
}


mCurrVelocity = velocityCoef * mDistance / mDuration * 1000.0f;

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;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
根据当前滑动时间与总滑动时间的比例。再根据一个SPLINE_POSITION数组计算出了距离系数distanceCoef与加速度系数velocityCoef。再根据这两个系数计算出当前加速度与当前的mCurrX与mCurrY。

最后如果该方法返回了true表示绘制未完成,需要持续的调用invalidate方法和scrollTo方法。返回false,表示绘制完成。
---------------------
作者:qq_38256015
来源:CSDN
原文:blog.csdn.net/qq_38256015…
版权声明:本文为博主原创文章,转载请附上博文链接!