6
6
import android .graphics .ColorFilter ;
7
7
import android .graphics .LinearGradient ;
8
8
import android .graphics .Paint ;
9
- import android .graphics .Rect ;
10
9
import android .graphics .RectF ;
11
10
import android .graphics .Shader ;
12
- import android .support .annotation .NonNull ;
13
- import android .util .DisplayMetrics ;
14
- import android .util .TypedValue ;
11
+ import android .support .annotation .Size ;
15
12
import android .view .animation .AccelerateInterpolator ;
16
13
import android .view .animation .DecelerateInterpolator ;
17
14
import android .view .animation .Interpolator ;
@@ -23,21 +20,18 @@ public class CollisionLoadingRenderer extends LoadingRenderer {
23
20
private static final Interpolator ACCELERATE_INTERPOLATOR = new AccelerateInterpolator ();
24
21
private static final Interpolator DECELERATE_INTERPOLATOR = new DecelerateInterpolator ();
25
22
26
- private static final int CIRCLE_COUNT = 7 ;
23
+ private static final int DEFAULT_BALL_COUNT = 7 ;
27
24
28
- //the 2 * 2 is the left and right side offset
29
- private static final float DEFAULT_WIDTH = 15.0f * ( CIRCLE_COUNT + 2 * 2 ) ;
30
- //the 2 * 2 is the top and bottom side offset
31
- private static final float DEFAULT_HEIGHT = 15.0f * ( 1 + 2 * 2 ) ;
25
+ private static final float DEFAULT_OVAL_HEIGHT = 1.5f ;
26
+ private static final float DEFAULT_BALL_RADIUS = 7.5f ;
27
+ private static final float DEFAULT_WIDTH = 15.0f * 11 ;
28
+ private static final float DEFAULT_HEIGHT = 15.0f * 4 ;
32
29
33
- private static final float DURATION_OFFSET = 0.25f ;
34
30
private static final float START_LEFT_DURATION_OFFSET = 0.25f ;
35
31
private static final float START_RIGHT_DURATION_OFFSET = 0.5f ;
36
32
private static final float END_RIGHT_DURATION_OFFSET = 0.75f ;
37
33
private static final float END_LEFT_DURATION_OFFSET = 1.0f ;
38
34
39
- private static final float DEFAULT_STROKE_WIDTH = 2.5f ;
40
-
41
35
private static final int [] DEFAULT_COLORS = new int []{
42
36
Color .RED , Color .GREEN
43
37
};
@@ -47,155 +41,240 @@ public class CollisionLoadingRenderer extends LoadingRenderer {
47
41
};
48
42
49
43
private final Paint mPaint = new Paint ();
50
- private final RectF mTempBounds = new RectF ();
44
+ private final RectF mOvalRect = new RectF ();
51
45
46
+ @ Size (2 )
52
47
private int [] mColors ;
53
48
private float [] mPositions ;
54
49
55
50
private float mEndXOffsetProgress ;
56
51
private float mStartXOffsetProgress ;
57
52
58
- private float mStrokeWidth ;
53
+ private float mOvalVerticalRadius ;
54
+
55
+ private float mBallRadius ;
56
+ private float mBallCenterY ;
57
+ private float mSideOffsets ;
58
+ private float mBallMoveXOffsets ;
59
+ private float mBallQuadCoefficient ;
60
+
61
+ private int mBallCount ;
59
62
60
63
private CollisionLoadingRenderer (Context context ) {
61
64
super (context );
62
65
init (context );
66
+ adjustParams ();
63
67
setupPaint ();
64
68
}
65
69
66
70
private void init (Context context ) {
71
+ mBallRadius = DensityUtil .dip2px (context , DEFAULT_BALL_RADIUS );
67
72
mWidth = DensityUtil .dip2px (context , DEFAULT_WIDTH );
68
73
mHeight = DensityUtil .dip2px (context , DEFAULT_HEIGHT );
69
- mStrokeWidth = DensityUtil .dip2px (context , DEFAULT_STROKE_WIDTH );
70
- }
74
+ mOvalVerticalRadius = DensityUtil .dip2px (context , DEFAULT_OVAL_HEIGHT );
75
+
76
+ mBallCount = DEFAULT_BALL_COUNT ;
71
77
72
- private void setupPaint () {
73
78
mColors = DEFAULT_COLORS ;
74
79
mPositions = DEFAULT_POSITIONS ;
75
80
76
- mPaint .setStrokeWidth (mStrokeWidth );
81
+ //mBallMoveYOffsets = mBallQuadCoefficient * mBallMoveXOffsets ^ 2
82
+ // ==> if mBallMoveYOffsets == mBallMoveXOffsets
83
+ // ==> mBallQuadCoefficient = 1.0f / mBallMoveXOffsets;
84
+ mBallMoveXOffsets = 1.5f * (2 * mBallRadius );
85
+ mBallQuadCoefficient = 1.0f / mBallMoveXOffsets ;
86
+ }
87
+
88
+ private void adjustParams () {
89
+ mBallCenterY = mHeight / 2.0f ;
90
+ mSideOffsets = (mWidth - mBallRadius * 2.0f * (mBallCount - 2 )) / 2 ;
91
+ }
92
+
93
+ private void setupPaint () {
77
94
mPaint .setAntiAlias (true );
78
95
mPaint .setStyle (Paint .Style .FILL );
96
+
97
+ mPaint .setShader (new LinearGradient (mSideOffsets , 0 , mWidth - mSideOffsets , 0 ,
98
+ mColors , mPositions , Shader .TileMode .CLAMP ));
79
99
}
80
100
81
101
@ Override
82
- protected void draw (Canvas canvas , Rect bounds ) {
102
+ protected void draw (Canvas canvas ) {
83
103
int saveCount = canvas .save ();
84
104
85
- RectF arcBounds = mTempBounds ;
86
- arcBounds .set (bounds );
87
-
88
- float cy = mHeight / 2 ;
89
- float circleRadius = computeCircleRadius (arcBounds );
90
-
91
- float sideOffset = 2.0f * (2 * circleRadius );
92
- float maxMoveOffset = 1.5f * (2 * circleRadius );
93
-
94
- LinearGradient gradient = new LinearGradient (arcBounds .left + sideOffset , 0 , arcBounds .right - sideOffset , 0 ,
95
- mColors , mPositions , Shader .TileMode .CLAMP );
96
- mPaint .setShader (gradient );
97
-
98
- for (int i = 0 ; i < CIRCLE_COUNT ; i ++) {
105
+ for (int i = 0 ; i < mBallCount ; i ++) {
106
+ //yMoveOffset = mBallQuadCoefficient * xMoveOffset ^ 2
99
107
if (i == 0 && mStartXOffsetProgress != 0 ) {
100
- float xMoveOffset = maxMoveOffset * mStartXOffsetProgress ;
101
- // y = ax^2 --> if x = sideOffset, y = sideOffset ==> a = 1 / sideOffset
102
- float yMoveOffset = (float ) (Math .pow (xMoveOffset , 2 ) / maxMoveOffset );
103
- canvas .drawCircle (circleRadius + sideOffset - xMoveOffset , cy - yMoveOffset , circleRadius , mPaint );
108
+ float xMoveOffset = mBallMoveXOffsets * mStartXOffsetProgress ;
109
+ float yMoveOffset = (float ) (Math .pow (xMoveOffset , 2 ) * mBallQuadCoefficient );
110
+ canvas .drawCircle (mSideOffsets - mBallRadius - xMoveOffset , mBallCenterY - yMoveOffset , mBallRadius , mPaint );
111
+
112
+ float leftStartProgress = 1.0f - mStartXOffsetProgress ;
113
+ mOvalRect .set (mSideOffsets - mBallRadius - mBallRadius * leftStartProgress - xMoveOffset ,
114
+ mHeight - mOvalVerticalRadius - mOvalVerticalRadius * leftStartProgress ,
115
+ mSideOffsets - mBallRadius + mBallRadius * leftStartProgress - xMoveOffset ,
116
+ mHeight - mOvalVerticalRadius + mOvalVerticalRadius * leftStartProgress );
117
+ canvas .drawOval (mOvalRect , mPaint );
104
118
continue ;
105
119
}
106
120
107
- if (i == CIRCLE_COUNT - 1 && mEndXOffsetProgress != 0 ) {
108
- float xMoveOffset = maxMoveOffset * mEndXOffsetProgress ;
109
- // y = ax^2 --> if x = sideOffset, y = sideOffset / 2 ==> a = 1 / sideOffset
110
- float yMoveOffset = (float ) (Math .pow (xMoveOffset , 2 ) / maxMoveOffset );
111
- canvas .drawCircle (circleRadius * (CIRCLE_COUNT * 2 - 1 ) + sideOffset + xMoveOffset , cy - yMoveOffset , circleRadius , mPaint );
121
+ if (i == mBallCount - 1 && mEndXOffsetProgress != 0 ) {
122
+ float xMoveOffset = mBallMoveXOffsets * mEndXOffsetProgress ;
123
+ float yMoveOffset = (float ) (Math .pow (xMoveOffset , 2 ) * mBallQuadCoefficient );
124
+ canvas .drawCircle (mBallRadius * (mBallCount * 2 - 3 ) + mSideOffsets + xMoveOffset , mBallCenterY - yMoveOffset , mBallRadius , mPaint );
125
+
126
+ float leftEndProgress = 1.0f - mEndXOffsetProgress ;
127
+ mOvalRect .set (mBallRadius * (mBallCount * 2 - 3 ) - mBallRadius * leftEndProgress + mSideOffsets + xMoveOffset ,
128
+ mHeight - mOvalVerticalRadius - mOvalVerticalRadius * leftEndProgress ,
129
+ mBallRadius * (mBallCount * 2 - 3 ) + mBallRadius * leftEndProgress + mSideOffsets + xMoveOffset ,
130
+ mHeight - mOvalVerticalRadius + mOvalVerticalRadius * leftEndProgress );
131
+ canvas .drawOval (mOvalRect , mPaint );
112
132
continue ;
113
133
}
114
134
115
- canvas .drawCircle (circleRadius * (i * 2 + 1 ) + sideOffset , cy , circleRadius , mPaint );
135
+ canvas .drawCircle (mBallRadius * (i * 2 - 1 ) + mSideOffsets , mBallCenterY , mBallRadius , mPaint );
136
+
137
+ mOvalRect .set (mBallRadius * (i * 2 - 2 ) + mSideOffsets , mHeight - mOvalVerticalRadius * 2 ,
138
+ mBallRadius * (i * 2 ) + mSideOffsets , mHeight );
139
+ canvas .drawOval (mOvalRect , mPaint );
116
140
}
117
141
118
142
canvas .restoreToCount (saveCount );
119
143
}
120
144
121
- private float computeCircleRadius (RectF rectBounds ) {
122
- float width = rectBounds .width ();
123
- float height = rectBounds .height ();
124
-
125
- //CIRCLE_COUNT + 4 is the sliding distance of both sides
126
- float radius = Math .min (width / (CIRCLE_COUNT + 4 ) / 2 , height / 2 );
127
- return radius ;
128
- }
129
-
130
145
@ Override
131
146
protected void computeRender (float renderProgress ) {
132
-
133
- // Moving the start offset to left only occurs in the first 25% of a
134
- // single ring animation
147
+ // Moving the left ball to the left sides only occurs in the first 25% of a jump animation
135
148
if (renderProgress <= START_LEFT_DURATION_OFFSET ) {
136
- float startLeftOffsetProgress = renderProgress / DURATION_OFFSET ;
149
+ float startLeftOffsetProgress = renderProgress / START_LEFT_DURATION_OFFSET ;
137
150
mStartXOffsetProgress = DECELERATE_INTERPOLATOR .getInterpolation (startLeftOffsetProgress );
138
-
139
-
140
151
return ;
141
152
}
142
153
143
- // Moving the start offset to left only occurs between 25% and 50% of a
144
- // single ring animation
154
+ // Moving the left ball to the origin location only occurs between 25% and 50% of a jump ring animation
145
155
if (renderProgress <= START_RIGHT_DURATION_OFFSET ) {
146
- float startRightOffsetProgress = (renderProgress - START_LEFT_DURATION_OFFSET ) / DURATION_OFFSET ;
156
+ float startRightOffsetProgress = (renderProgress - START_LEFT_DURATION_OFFSET ) / ( START_RIGHT_DURATION_OFFSET - START_LEFT_DURATION_OFFSET ) ;
147
157
mStartXOffsetProgress = ACCELERATE_INTERPOLATOR .getInterpolation (1.0f - startRightOffsetProgress );
148
-
149
-
150
158
return ;
151
159
}
152
160
153
- // Moving the end offset to right starts between 50% and 75% a single ring
154
- // animation completes
161
+ // Moving the right ball to the right sides only occurs between 50% and 75% of a jump animation
155
162
if (renderProgress <= END_RIGHT_DURATION_OFFSET ) {
156
- float endRightOffsetProgress = (renderProgress - START_RIGHT_DURATION_OFFSET ) / DURATION_OFFSET ;
163
+ float endRightOffsetProgress = (renderProgress - START_RIGHT_DURATION_OFFSET ) / ( END_RIGHT_DURATION_OFFSET - START_RIGHT_DURATION_OFFSET ) ;
157
164
mEndXOffsetProgress = DECELERATE_INTERPOLATOR .getInterpolation (endRightOffsetProgress );
158
-
159
-
160
165
return ;
161
166
}
162
167
163
- // Moving the end offset to left starts after 75% of a single ring
164
- // animation completes
168
+ // Moving the right ball to the origin location only occurs after 75% of a jump animation
165
169
if (renderProgress <= END_LEFT_DURATION_OFFSET ) {
166
- float endRightOffsetProgress = (renderProgress - END_RIGHT_DURATION_OFFSET ) / DURATION_OFFSET ;
170
+ float endRightOffsetProgress = (renderProgress - END_RIGHT_DURATION_OFFSET ) / ( END_LEFT_DURATION_OFFSET - END_RIGHT_DURATION_OFFSET ) ;
167
171
mEndXOffsetProgress = ACCELERATE_INTERPOLATOR .getInterpolation (1 - endRightOffsetProgress );
168
-
169
-
170
172
return ;
171
173
}
172
174
}
173
175
174
176
@ Override
175
177
protected void setAlpha (int alpha ) {
176
178
mPaint .setAlpha (alpha );
177
-
178
179
}
179
180
180
181
@ Override
181
182
protected void setColorFilter (ColorFilter cf ) {
182
183
mPaint .setColorFilter (cf );
183
-
184
184
}
185
185
186
186
@ Override
187
187
protected void reset () {
188
188
}
189
189
190
+ private void apply (Builder builder ) {
191
+ this .mWidth = builder .mWidth > 0 ? builder .mWidth : this .mWidth ;
192
+ this .mHeight = builder .mHeight > 0 ? builder .mHeight : this .mHeight ;
193
+
194
+ this .mOvalVerticalRadius = builder .mOvalVerticalRadius > 0 ? builder .mOvalVerticalRadius : this .mOvalVerticalRadius ;
195
+ this .mBallRadius = builder .mBallRadius > 0 ? builder .mBallRadius : this .mBallRadius ;
196
+ this .mBallMoveXOffsets = builder .mBallMoveXOffsets > 0 ? builder .mBallMoveXOffsets : this .mBallMoveXOffsets ;
197
+ this .mBallQuadCoefficient = builder .mBallQuadCoefficient > 0 ? builder .mBallQuadCoefficient : this .mBallQuadCoefficient ;
198
+ this .mBallCount = builder .mBallCount > 0 ? builder .mBallCount : this .mBallCount ;
199
+
200
+ this .mDuration = builder .mDuration > 0 ? builder .mDuration : this .mDuration ;
201
+
202
+ this .mColors = builder .mColors != null ? builder .mColors : this .mColors ;
203
+
204
+ adjustParams ();
205
+ setupPaint ();
206
+ }
207
+
190
208
public static class Builder {
191
209
private Context mContext ;
192
210
211
+ private int mWidth ;
212
+ private int mHeight ;
213
+
214
+ private float mOvalVerticalRadius ;
215
+
216
+ private int mBallCount ;
217
+ private float mBallRadius ;
218
+ private float mBallMoveXOffsets ;
219
+ private float mBallQuadCoefficient ;
220
+
221
+ private int mDuration ;
222
+
223
+ @ Size (2 )
224
+ private int [] mColors ;
225
+
193
226
public Builder (Context mContext ) {
194
227
this .mContext = mContext ;
195
228
}
196
229
230
+ public Builder setWidth (int width ) {
231
+ this .mWidth = width ;
232
+ return this ;
233
+ }
234
+
235
+ public Builder setHeight (int height ) {
236
+ this .mHeight = height ;
237
+ return this ;
238
+ }
239
+
240
+ public Builder setOvalVerticalRadius (int ovalVerticalRadius ) {
241
+ this .mOvalVerticalRadius = ovalVerticalRadius ;
242
+ return this ;
243
+ }
244
+
245
+ public Builder setBallRadius (float ballRadius ) {
246
+ this .mBallRadius = ballRadius ;
247
+ return this ;
248
+ }
249
+
250
+ public Builder setBallMoveXOffsets (float ballMoveXOffsets ) {
251
+ this .mBallMoveXOffsets = ballMoveXOffsets ;
252
+ return this ;
253
+ }
254
+
255
+ public Builder setBallQuadCoefficient (float ballQuadCoefficient ) {
256
+ this .mBallQuadCoefficient = ballQuadCoefficient ;
257
+ return this ;
258
+ }
259
+
260
+ public Builder setBallCount (int ballCount ) {
261
+ this .mBallCount = ballCount ;
262
+ return this ;
263
+ }
264
+
265
+ public Builder setColors (@ Size (2 ) int [] colors ) {
266
+ this .mColors = colors ;
267
+ return this ;
268
+ }
269
+
270
+ public Builder setDuration (int duration ) {
271
+ this .mDuration = duration ;
272
+ return this ;
273
+ }
274
+
197
275
public CollisionLoadingRenderer build () {
198
276
CollisionLoadingRenderer loadingRenderer = new CollisionLoadingRenderer (mContext );
277
+ loadingRenderer .apply (this );
199
278
return loadingRenderer ;
200
279
}
201
280
}
0 commit comments