@@ -35,8 +35,7 @@ public class LrcView extends View {
35
35
private String mLabel ;
36
36
private float mLrcPadding ;
37
37
private ValueAnimator mAnimator ;
38
- private float mAnimateOffset ;
39
- private long mNextTime = 0L ;
38
+ private float mOffset ;
40
39
private int mCurrentLine = 0 ;
41
40
private Object mFlag ;
42
41
@@ -92,62 +91,40 @@ protected void onLayout(boolean changed, int left, int top, int right, int botto
92
91
protected void onDraw (Canvas canvas ) {
93
92
super .onDraw (canvas );
94
93
95
- canvas .translate (0 , mAnimateOffset );
96
-
97
- // 中心Y坐标
98
- float centerY = getHeight () / 2 ;
99
-
100
- mPaint .setColor (mCurrentColor );
94
+ canvas .translate (0 , mOffset );
101
95
102
96
// 无歌词文件
103
97
if (!hasLrc ()) {
98
+ mPaint .setColor (mCurrentColor );
104
99
@ SuppressLint ("DrawAllocation" )
105
100
StaticLayout staticLayout = new StaticLayout (mLabel , mPaint , (int ) getLrcWidth (),
106
101
Layout .Alignment .ALIGN_CENTER , 1f , 0f , false );
107
- drawText (canvas , staticLayout , centerY - staticLayout . getHeight () / 2 );
102
+ drawText (canvas , staticLayout , getHeight () / 2 );
108
103
return ;
109
104
}
110
105
111
- // 画当前行
112
- float currY = centerY - mLrcEntryList .get (mCurrentLine ).getHeight () / 2 ;
113
- drawText (canvas , mLrcEntryList .get (mCurrentLine ).getStaticLayout (), currY );
114
-
115
- // 画当前行上面的
116
- mPaint .setColor (mNormalColor );
117
- float upY = currY ;
118
- for (int i = mCurrentLine - 1 ; i >= 0 ; i --) {
119
- upY -= mDividerHeight + mLrcEntryList .get (i ).getHeight ();
120
- drawText (canvas , mLrcEntryList .get (i ).getStaticLayout (), upY );
121
-
122
- if (upY <= 0 ) {
123
- break ;
106
+ float y = 0 ;
107
+ for (int i = 0 ; i < mLrcEntryList .size (); i ++) {
108
+ if (i > 0 ) {
109
+ y += (mLrcEntryList .get (i - 1 ).getHeight () + mLrcEntryList .get (i ).getHeight ()) / 2 + mDividerHeight ;
124
110
}
125
- }
126
-
127
- // 画当前行下面的
128
- float downY = currY + mLrcEntryList .get (mCurrentLine ).getHeight () + mDividerHeight ;
129
- for (int i = mCurrentLine + 1 ; i < mLrcEntryList .size (); i ++) {
130
- drawText (canvas , mLrcEntryList .get (i ).getStaticLayout (), downY );
131
-
132
- if (downY + mLrcEntryList .get (i ).getHeight () >= getHeight ()) {
133
- break ;
134
- }
135
-
136
- downY += mLrcEntryList .get (i ).getHeight () + mDividerHeight ;
111
+ mPaint .setColor ((i == mCurrentLine ) ? mCurrentColor : mNormalColor );
112
+ drawText (canvas , mLrcEntryList .get (i ).getStaticLayout (), y );
137
113
}
138
114
}
139
115
116
+ /**
117
+ * 画一行歌词
118
+ *
119
+ * @param y 歌词中心 Y 坐标
120
+ */
140
121
private void drawText (Canvas canvas , StaticLayout staticLayout , float y ) {
141
122
canvas .save ();
142
- canvas .translate (mLrcPadding , y );
123
+ canvas .translate (mLrcPadding , y - staticLayout . getHeight () / 2 );
143
124
staticLayout .draw (canvas );
144
125
canvas .restore ();
145
126
}
146
127
147
- private float getLrcWidth () {
148
- return getWidth () - mLrcPadding * 2 ;
149
- }
150
-
151
128
/**
152
129
* 设置歌词为空时屏幕中央显示的文字,如“暂无歌词”
153
130
*/
@@ -226,11 +203,7 @@ private void onLrcLoaded(List<LrcEntry> entryList) {
226
203
mLrcEntryList .addAll (entryList );
227
204
}
228
205
229
- if (hasLrc ()) {
230
- initEntryList ();
231
- initNextTime ();
232
- }
233
-
206
+ initEntryList ();
234
207
invalidate ();
235
208
}
236
209
@@ -243,23 +216,14 @@ public void updateTime(final long time) {
243
216
runOnUi (new Runnable () {
244
217
@ Override
245
218
public void run () {
246
- // 避免重复绘制
247
- if (time < mNextTime ) {
219
+ if (!hasLrc ()) {
248
220
return ;
249
221
}
250
- for (int i = mCurrentLine ; i < mLrcEntryList .size (); i ++) {
251
- if (mLrcEntryList .get (i ).getTime () > time ) {
252
- mNextTime = mLrcEntryList .get (i ).getTime ();
253
- mCurrentLine = (i < 1 ) ? 0 : (i - 1 );
254
- newline (i , true );
255
- break ;
256
- } else if (i == mLrcEntryList .size () - 1 ) {
257
- // 最后一行
258
- mCurrentLine = mLrcEntryList .size () - 1 ;
259
- mNextTime = Long .MAX_VALUE ;
260
- newline (i , true );
261
- break ;
262
- }
222
+
223
+ int line = findShowLine (time );
224
+ if (line != mCurrentLine ) {
225
+ mCurrentLine = line ;
226
+ newline (line );
263
227
}
264
228
}
265
229
});
@@ -269,26 +233,34 @@ public void run() {
269
233
* 将歌词滚动到指定时间
270
234
*
271
235
* @param time 指定的时间
236
+ * @deprecated 请使用 {@link #updateTime(long)} 代替
272
237
*/
273
- public void onDrag (final long time ) {
274
- runOnUi (new Runnable () {
275
- @ Override
276
- public void run () {
277
- for (int i = 0 ; i < mLrcEntryList .size (); i ++) {
278
- if (mLrcEntryList .get (i ).getTime () > time ) {
279
- if (i == 0 ) {
280
- mCurrentLine = i ;
281
- initNextTime ();
282
- } else {
283
- mCurrentLine = i - 1 ;
284
- mNextTime = mLrcEntryList .get (i ).getTime ();
285
- }
286
- newline (i , false );
287
- break ;
288
- }
238
+ public void onDrag (long time ) {
239
+ updateTime (time );
240
+ }
241
+
242
+ /**
243
+ * 二分法查找当前应该显示的行数(最后一个 <= time 的行数)
244
+ */
245
+ private int findShowLine (long time ) {
246
+ int left = 0 ;
247
+ int right = mLrcEntryList .size ();
248
+ while (left <= right ) {
249
+ int middle = (left + right ) / 2 ;
250
+ long middleTime = mLrcEntryList .get (middle ).getTime ();
251
+
252
+ if (time < middleTime ) {
253
+ right = middle - 1 ;
254
+ } else {
255
+ if (middle + 1 >= mLrcEntryList .size () || time < mLrcEntryList .get (middle + 1 ).getTime ()) {
256
+ return middle ;
289
257
}
258
+
259
+ left = middle + 1 ;
290
260
}
291
- });
261
+ }
262
+
263
+ return 0 ;
292
264
}
293
265
294
266
/**
@@ -301,16 +273,14 @@ public boolean hasLrc() {
301
273
}
302
274
303
275
private void reset () {
276
+ endAnimation ();
304
277
mLrcEntryList .clear ();
305
278
mCurrentLine = 0 ;
306
- mNextTime = 0L ;
307
-
308
- stopAnimation ();
309
279
invalidate ();
310
280
}
311
281
312
282
private void initEntryList () {
313
- if (getWidth () == 0 ) {
283
+ if (! hasLrc () || getWidth () == 0 ) {
314
284
return ;
315
285
}
316
286
@@ -319,51 +289,54 @@ private void initEntryList() {
319
289
for (LrcEntry lrcEntry : mLrcEntryList ) {
320
290
lrcEntry .init (mPaint , (int ) getLrcWidth ());
321
291
}
322
- }
323
292
324
- private void initNextTime () {
325
- if (mLrcEntryList .size () > 1 ) {
326
- mNextTime = mLrcEntryList .get (1 ).getTime ();
327
- } else {
328
- mNextTime = Long .MAX_VALUE ;
329
- }
293
+ mOffset = getHeight () / 2 ;
330
294
}
331
295
332
296
/**
333
297
* 换行动画<br>
334
298
* 属性动画只能在主线程使用
335
299
*/
336
- private void newline (int line , boolean animate ) {
337
- stopAnimation ();
300
+ private void newline (int line ) {
301
+ endAnimation ();
338
302
339
- if (line <= 0 || !animate ) {
340
- invalidate ();
341
- return ;
342
- }
343
-
344
- float prevHeight = mLrcEntryList .get (line - 1 ).getHeight ();
345
- float currHeight = mLrcEntryList .get (line ).getHeight ();
346
- float totalOffset = (prevHeight + currHeight ) / 2 + mDividerHeight ;
303
+ int offset = getOffset (line );
347
304
348
- mAnimator = ValueAnimator .ofFloat (totalOffset , 0.0f );
349
- mAnimator .setDuration (mAnimationDuration * mLrcEntryList . get ( line ). getStaticLayout (). getLineCount () );
305
+ mAnimator = ValueAnimator .ofFloat (mOffset , offset );
306
+ mAnimator .setDuration (mAnimationDuration );
350
307
mAnimator .setInterpolator (new LinearInterpolator ());
351
308
mAnimator .addUpdateListener (new ValueAnimator .AnimatorUpdateListener () {
352
309
@ Override
353
310
public void onAnimationUpdate (ValueAnimator animation ) {
354
- mAnimateOffset = (float ) animation .getAnimatedValue ();
311
+ mOffset = (float ) animation .getAnimatedValue ();
355
312
invalidate ();
356
313
}
357
314
});
358
315
mAnimator .start ();
359
316
}
360
317
361
- private void stopAnimation () {
318
+ private void endAnimation () {
362
319
if (mAnimator != null && mAnimator .isRunning ()) {
363
320
mAnimator .end ();
364
321
}
365
322
}
366
323
324
+ private int getOffset (int line ) {
325
+ if (mLrcEntryList .get (line ).getOffset () == Integer .MIN_VALUE ) {
326
+ int offset = getHeight () / 2 ;
327
+ for (int i = 1 ; i <= line ; i ++) {
328
+ offset -= (mLrcEntryList .get (i - 1 ).getHeight () + mLrcEntryList .get (i ).getHeight ()) / 2 + mDividerHeight ;
329
+ }
330
+ mLrcEntryList .get (line ).setOffset (offset );
331
+ }
332
+
333
+ return mLrcEntryList .get (line ).getOffset ();
334
+ }
335
+
336
+ private float getLrcWidth () {
337
+ return getWidth () - mLrcPadding * 2 ;
338
+ }
339
+
367
340
private void runOnUi (Runnable r ) {
368
341
if (Looper .myLooper () == Looper .getMainLooper ()) {
369
342
r .run ();
0 commit comments