Use Coroutines in LrcView

This commit is contained in:
Prathamesh More 2022-06-20 14:42:59 +05:30
parent 0f66d005c7
commit dd59459786
4 changed files with 574 additions and 811 deletions

View file

@ -19,7 +19,6 @@ import android.content.Context
import android.graphics.Canvas import android.graphics.Canvas
import android.graphics.Paint import android.graphics.Paint
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
import android.os.AsyncTask
import android.os.Looper import android.os.Looper
import android.text.Layout import android.text.Layout
import android.text.StaticLayout import android.text.StaticLayout
@ -35,14 +34,15 @@ import android.widget.Scroller
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.graphics.withSave import androidx.core.graphics.withSave
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R
import kotlinx.coroutines.*
import java.io.File import java.io.File
import java.lang.Runnable
import kotlin.math.abs import kotlin.math.abs
/** /**
* 歌词 Created by wcy on 2015/11/9. * 歌词 Created by wcy on 2015/11/9.
*/ */
@SuppressLint("StaticFieldLeak") @SuppressLint("StaticFieldLeak")
@Suppress("deprecation")
class CoverLrcView @JvmOverloads constructor( class CoverLrcView @JvmOverloads constructor(
context: Context?, context: Context?,
attrs: AttributeSet? = null, attrs: AttributeSet? = null,
@ -72,7 +72,6 @@ class CoverLrcView @JvmOverloads constructor(
private var mScroller: Scroller? = null private var mScroller: Scroller? = null
private var mOffset = 0f private var mOffset = 0f
private var mCurrentLine = 0 private var mCurrentLine = 0
private var flag: Any? = null
private var isShowTimeline = false private var isShowTimeline = false
private var isTouching = false private var isTouching = false
private var isFling = false private var isFling = false
@ -85,9 +84,8 @@ class CoverLrcView @JvmOverloads constructor(
} }
} }
/** private val viewScope = CoroutineScope(Dispatchers.Main + Job())
* 手势监听器
*/
private val mSimpleOnGestureListener: SimpleOnGestureListener = private val mSimpleOnGestureListener: SimpleOnGestureListener =
object : SimpleOnGestureListener() { object : SimpleOnGestureListener() {
override fun onDown(e: MotionEvent): Boolean { override fun onDown(e: MotionEvent): Boolean {
@ -251,42 +249,31 @@ class CoverLrcView @JvmOverloads constructor(
mScroller = Scroller(context) mScroller = Scroller(context)
} }
/** 设置非当前行歌词字体颜色 */
fun setNormalColor(normalColor: Int) { fun setNormalColor(normalColor: Int) {
mNormalTextColor = normalColor mNormalTextColor = normalColor
postInvalidate() postInvalidate()
} }
/** 设置当前行歌词的字体颜色 */
fun setCurrentColor(currentColor: Int) { fun setCurrentColor(currentColor: Int) {
mCurrentTextColor = currentColor mCurrentTextColor = currentColor
postInvalidate() postInvalidate()
} }
/** 设置拖动歌词时选中歌词的字体颜色 */
fun setTimelineTextColor(timelineTextColor: Int) { fun setTimelineTextColor(timelineTextColor: Int) {
mTimelineTextColor = timelineTextColor mTimelineTextColor = timelineTextColor
postInvalidate() postInvalidate()
} }
/** 设置拖动歌词时时间线的颜色 */
fun setTimelineColor(timelineColor: Int) { fun setTimelineColor(timelineColor: Int) {
mTimelineColor = timelineColor mTimelineColor = timelineColor
postInvalidate() postInvalidate()
} }
/** 设置拖动歌词时右侧时间字体颜色 */
fun setTimeTextColor(timeTextColor: Int) { fun setTimeTextColor(timeTextColor: Int) {
mTimeTextColor = timeTextColor mTimeTextColor = timeTextColor
postInvalidate() postInvalidate()
} }
/**
* 设置歌词是否允许拖动
*
* @param draggable 是否允许拖动
* @param onPlayClickListener 设置歌词拖动后播放按钮点击监听器如果允许拖动则不能为 null
*/
fun setDraggable(draggable: Boolean, onPlayClickListener: OnPlayClickListener?) { fun setDraggable(draggable: Boolean, onPlayClickListener: OnPlayClickListener?) {
mOnPlayClickListener = if (draggable) { mOnPlayClickListener = if (draggable) {
requireNotNull(onPlayClickListener) { "if draggable == true, onPlayClickListener must not be null" } requireNotNull(onPlayClickListener) { "if draggable == true, onPlayClickListener must not be null" }
@ -296,17 +283,6 @@ class CoverLrcView @JvmOverloads constructor(
} }
} }
/**
* 设置播放按钮点击监听器
*
* @param onPlayClickListener 如果为非 null 则激活歌词拖动功能否则将将禁用歌词拖动功能
*/
@Deprecated("use {@link #setDraggable(boolean, OnPlayClickListener)} instead")
fun setOnPlayClickListener(onPlayClickListener: OnPlayClickListener?) {
mOnPlayClickListener = onPlayClickListener
}
/** 设置歌词为空时屏幕中央显示的文字,如“暂无歌词” */
fun setLabel(label: String?) { fun setLabel(label: String?) {
runOnUi { runOnUi {
mDefaultLabel = label mDefaultLabel = label
@ -314,106 +290,40 @@ class CoverLrcView @JvmOverloads constructor(
} }
} }
/**
* 加载歌词文件
*
* @param lrcFile 歌词文件
*/
fun loadLrc(lrcFile: File) { fun loadLrc(lrcFile: File) {
loadLrc(lrcFile, null)
}
/**
* 加载双语歌词文件两种语言的歌词时间戳需要一致
*
* @param mainLrcFile 第一种语言歌词文件
* @param secondLrcFile 第二种语言歌词文件
*/
private fun loadLrc(mainLrcFile: File, secondLrcFile: File?) {
runOnUi { runOnUi {
reset() reset()
val sb = StringBuilder("file://") viewScope.launch(Dispatchers.IO) {
sb.append(mainLrcFile.path) val lrcEntries = LrcUtils.parseLrc(arrayOf(lrcFile, null))
if (secondLrcFile != null) { withContext(Dispatchers.Main) {
sb.append("#").append(secondLrcFile.path)
}
val flag = sb.toString()
this.flag = flag
object : AsyncTask<File?, Int?, List<LrcEntry>>() {
override fun doInBackground(vararg params: File?): List<LrcEntry>? {
return LrcUtils.parseLrc(params)
}
override fun onPostExecute(lrcEntries: List<LrcEntry>) {
if (flag == flag) {
onLrcLoaded(lrcEntries) onLrcLoaded(lrcEntries)
this@CoverLrcView.flag = null
} }
} }
}.execute(mainLrcFile, secondLrcFile)
} }
} }
/**
* 加载歌词文本
*
* @param lrcText 歌词文本
*/
fun loadLrc(lrcText: String?) { fun loadLrc(lrcText: String?) {
loadLrc(lrcText, null)
}
/**
* 加载双语歌词文本两种语言的歌词时间戳需要一致
*
* @param mainLrcText 第一种语言歌词文本
* @param secondLrcText 第二种语言歌词文本
*/
private fun loadLrc(mainLrcText: String?, secondLrcText: String?) {
runOnUi { runOnUi {
reset() reset()
val sb = StringBuilder("file://") viewScope.launch(Dispatchers.IO) {
sb.append(mainLrcText) val lrcEntries = LrcUtils.parseLrc(arrayOf(lrcText, null))
if (secondLrcText != null) { withContext(Dispatchers.Main) {
sb.append("#").append(secondLrcText)
}
val flag = sb.toString()
this.flag = flag
object : AsyncTask<String?, Int?, List<LrcEntry>>() {
override fun doInBackground(vararg params: String?): List<LrcEntry>? {
return LrcUtils.parseLrc(params)
}
override fun onPostExecute(lrcEntries: List<LrcEntry>) {
if (flag == flag) {
onLrcLoaded(lrcEntries) onLrcLoaded(lrcEntries)
this@CoverLrcView.flag = null
} }
} }
}.execute(mainLrcText, secondLrcText)
} }
} }
/**
* 歌词是否有效
*
* @return true如果歌词有效否则false
*/
fun hasLrc(): Boolean { fun hasLrc(): Boolean {
return mLrcEntryList.isNotEmpty() return mLrcEntryList.isNotEmpty()
} }
/**
* 刷新歌词
*
* @param time 当前播放时间
*/
fun updateTime(time: Long) { fun updateTime(time: Long) {
runOnUi { runOnUi {
if (!hasLrc()) { if (!hasLrc()) {
return@runOnUi return@runOnUi
} }
val line = findShowLine(time) val line = findShowLine(time - 300L)
if (line != mCurrentLine) { if (line != mCurrentLine) {
mCurrentLine = line mCurrentLine = line
if (!isShowTimeline) { if (!isShowTimeline) {
@ -441,9 +351,9 @@ class CoverLrcView @JvmOverloads constructor(
super.onDraw(canvas) super.onDraw(canvas)
val centerY = height / 2 val centerY = height / 2
// 无歌词文件
if (!hasLrc()) { if (!hasLrc()) {
mLrcPaint.color = mCurrentTextColor mLrcPaint.color = mCurrentTextColor
@Suppress("Deprecation")
@SuppressLint("DrawAllocation") val staticLayout = StaticLayout( @SuppressLint("DrawAllocation") val staticLayout = StaticLayout(
mDefaultLabel, mDefaultLabel,
mLrcPaint, mLrcPaint,
@ -485,11 +395,6 @@ class CoverLrcView @JvmOverloads constructor(
} }
} }
/**
* 画一行歌词
*
* @param y 歌词中心 Y 坐标
*/
private fun drawText(canvas: Canvas, staticLayout: StaticLayout, y: Float) { private fun drawText(canvas: Canvas, staticLayout: StaticLayout, y: Float) {
canvas.withSave { canvas.withSave {
translate(mLrcPadding, y - (staticLayout.height shr 1)) translate(mLrcPadding, y - (staticLayout.height shr 1))
@ -539,6 +444,7 @@ class CoverLrcView @JvmOverloads constructor(
override fun onDetachedFromWindow() { override fun onDetachedFromWindow() {
removeCallbacks(hideTimelineRunnable) removeCallbacks(hideTimelineRunnable)
viewScope.cancel()
super.onDetachedFromWindow() super.onDetachedFromWindow()
} }
@ -582,12 +488,10 @@ class CoverLrcView @JvmOverloads constructor(
invalidate() invalidate()
} }
/** 将中心行微调至正中心 */
private fun adjustCenter() { private fun adjustCenter() {
smoothScrollTo(centerLine, ADJUST_DURATION) smoothScrollTo(centerLine, ADJUST_DURATION)
} }
/** 滚动到某一行 */
private fun smoothScrollTo(line: Int, duration: Long = mAnimationDuration) { private fun smoothScrollTo(line: Int, duration: Long = mAnimationDuration) {
val offset = getOffset(line) val offset = getOffset(line)
endAnimation() endAnimation()
@ -602,14 +506,12 @@ class CoverLrcView @JvmOverloads constructor(
} }
} }
/** 结束滚动动画 */
private fun endAnimation() { private fun endAnimation() {
if (mAnimator != null && mAnimator!!.isRunning) { if (mAnimator != null && mAnimator!!.isRunning) {
mAnimator!!.end() mAnimator!!.end()
} }
} }
/** 二分法查找当前时间应该显示的行数(最后一个 <= time 的行数) */
private fun findShowLine(time: Long): Int { private fun findShowLine(time: Long): Int {
var left = 0 var left = 0
var right = mLrcEntryList.size var right = mLrcEntryList.size
@ -628,7 +530,6 @@ class CoverLrcView @JvmOverloads constructor(
return 0 return 0
} }
/** 获取当前在视图中央的行数 */
private val centerLine: Int private val centerLine: Int
get() { get() {
var centerLine = 0 var centerLine = 0
@ -642,7 +543,6 @@ class CoverLrcView @JvmOverloads constructor(
return centerLine return centerLine
} }
/** 获取歌词距离视图顶部的距离 采用懒加载方式 */
private fun getOffset(line: Int): Float { private fun getOffset(line: Int): Float {
if (mLrcEntryList.isEmpty()) return 0F if (mLrcEntryList.isEmpty()) return 0F
if (mLrcEntryList[line].offset == Float.MIN_VALUE) { if (mLrcEntryList[line].offset == Float.MIN_VALUE) {
@ -656,11 +556,9 @@ class CoverLrcView @JvmOverloads constructor(
return mLrcEntryList[line].offset return mLrcEntryList[line].offset
} }
/** 获取歌词宽度 */
private val lrcWidth: Float private val lrcWidth: Float
get() = width - mLrcPadding * 2 get() = width - mLrcPadding * 2
/** 在主线程中运行 */
private fun runOnUi(r: Runnable) { private fun runOnUi(r: Runnable) {
if (Looper.myLooper() == Looper.getMainLooper()) { if (Looper.myLooper() == Looper.getMainLooper()) {
r.run() r.run()
@ -669,13 +567,7 @@ class CoverLrcView @JvmOverloads constructor(
} }
} }
/** 播放按钮点击监听器,点击后应该跳转到指定播放位置 */
fun interface OnPlayClickListener { fun interface OnPlayClickListener {
/**
* 播放按钮被点击应该跳转到指定播放位置
*
* @return 是否成功消费该事件如果成功消费则会更新UI
*/
fun onPlayClick(time: Long): Boolean fun onPlayClick(time: Long): Boolean
} }

View file

@ -35,6 +35,8 @@ import android.view.View;
import android.view.animation.LinearInterpolator; import android.view.animation.LinearInterpolator;
import android.widget.Scroller; import android.widget.Scroller;
import androidx.core.content.ContextCompat;
import java.io.File; import java.io.File;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
@ -90,9 +92,7 @@ public class LrcView extends View {
} }
} }
}; };
/**
* 手势监听器
*/
private final GestureDetector.SimpleOnGestureListener mSimpleOnGestureListener = private final GestureDetector.SimpleOnGestureListener mSimpleOnGestureListener =
new GestureDetector.SimpleOnGestureListener() { new GestureDetector.SimpleOnGestureListener() {
@Override @Override
@ -111,7 +111,7 @@ public class LrcView extends View {
@Override @Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
if (hasLrc()) { if (hasLrc()) {
mOffset += -distanceY; mOffset -= distanceY;
mOffset = Math.min(mOffset, getOffset(0)); mOffset = Math.min(mOffset, getOffset(0));
mOffset = Math.max(mOffset, getOffset(mLrcEntryList.size() - 1)); mOffset = Math.max(mOffset, getOffset(mLrcEntryList.size() - 1));
invalidate(); invalidate();
@ -219,7 +219,7 @@ public class LrcView extends View {
mPlayDrawable = ta.getDrawable(R.styleable.LrcView_lrcPlayDrawable); mPlayDrawable = ta.getDrawable(R.styleable.LrcView_lrcPlayDrawable);
mPlayDrawable = mPlayDrawable =
(mPlayDrawable == null) (mPlayDrawable == null)
? getResources().getDrawable(R.drawable.ic_play_arrow) ? ContextCompat.getDrawable(getContext(), R.drawable.ic_play_arrow)
: mPlayDrawable; : mPlayDrawable;
mTimeTextColor = mTimeTextColor =
ta.getColor( ta.getColor(
@ -252,52 +252,28 @@ public class LrcView extends View {
mScroller = new Scroller(getContext()); mScroller = new Scroller(getContext());
} }
/** 设置非当前行歌词字体颜色 */
public void setNormalColor(int normalColor) {
mNormalTextColor = normalColor;
postInvalidate();
}
/** 普通歌词文本字体大小 */
public void setNormalTextSize(float size) {
mNormalTextSize = size;
}
/** 当前歌词文本字体大小 */
public void setCurrentTextSize(float size) {
mCurrentTextSize = size;
}
/** 设置当前行歌词的字体颜色 */
public void setCurrentColor(int currentColor) { public void setCurrentColor(int currentColor) {
mCurrentTextColor = currentColor; mCurrentTextColor = currentColor;
postInvalidate(); postInvalidate();
} }
/** 设置拖动歌词时选中歌词的字体颜色 */
public void setTimelineTextColor(int timelineTextColor) { public void setTimelineTextColor(int timelineTextColor) {
mTimelineTextColor = timelineTextColor; mTimelineTextColor = timelineTextColor;
postInvalidate(); postInvalidate();
} }
/** 设置拖动歌词时时间线的颜色 */
public void setTimelineColor(int timelineColor) { public void setTimelineColor(int timelineColor) {
mTimelineColor = timelineColor; mTimelineColor = timelineColor;
postInvalidate(); postInvalidate();
} }
/** 设置拖动歌词时右侧时间字体颜色 */
public void setTimeTextColor(int timeTextColor) { public void setTimeTextColor(int timeTextColor) {
mTimeTextColor = timeTextColor; mTimeTextColor = timeTextColor;
postInvalidate(); postInvalidate();
} }
/**
* 设置歌词是否允许拖动
*
* @param draggable 是否允许拖动
* @param onPlayClickListener 设置歌词拖动后播放按钮点击监听器如果允许拖动则不能为 null
*/
public void setDraggable(boolean draggable, OnPlayClickListener onPlayClickListener) { public void setDraggable(boolean draggable, OnPlayClickListener onPlayClickListener) {
if (draggable) { if (draggable) {
if (onPlayClickListener == null) { if (onPlayClickListener == null) {
@ -310,18 +286,6 @@ public class LrcView extends View {
} }
} }
/**
* 设置播放按钮点击监听器
*
* @param onPlayClickListener 如果为非 null 则激活歌词拖动功能否则将将禁用歌词拖动功能
* @deprecated use {@link #setDraggable(boolean, OnPlayClickListener)} instead
*/
@Deprecated
public void setOnPlayClickListener(OnPlayClickListener onPlayClickListener) {
mOnPlayClickListener = onPlayClickListener;
}
/** 设置歌词为空时屏幕中央显示的文字,如“暂无歌词” */
public void setLabel(String label) { public void setLabel(String label) {
runOnUi( runOnUi(
() -> { () -> {
@ -330,24 +294,12 @@ public class LrcView extends View {
}); });
} }
/**
* 加载歌词文件
*
* @param lrcFile 歌词文件
*/
public void loadLrc(File lrcFile) { public void loadLrc(File lrcFile) {
loadLrc(lrcFile, null); loadLrc(lrcFile, null);
} }
/**
* 加载双语歌词文件两种语言的歌词时间戳需要一致
*
* @param mainLrcFile 第一种语言歌词文件
* @param secondLrcFile 第二种语言歌词文件
*/
public void loadLrc(File mainLrcFile, File secondLrcFile) { public void loadLrc(File mainLrcFile, File secondLrcFile) {
runOnUi( runOnUi(() -> {
() -> {
reset(); reset();
StringBuilder sb = new StringBuilder("file://"); StringBuilder sb = new StringBuilder("file://");
@ -374,21 +326,10 @@ public class LrcView extends View {
}); });
} }
/**
* 加载歌词文本
*
* @param lrcText 歌词文本
*/
public void loadLrc(String lrcText) { public void loadLrc(String lrcText) {
loadLrc(lrcText, null); loadLrc(lrcText, null);
} }
/**
* 加载双语歌词文本两种语言的歌词时间戳需要一致
*
* @param mainLrcText 第一种语言歌词文本
* @param secondLrcText 第二种语言歌词文本
*/
public void loadLrc(String mainLrcText, String secondLrcText) { public void loadLrc(String mainLrcText, String secondLrcText) {
runOnUi( runOnUi(
() -> { () -> {
@ -418,53 +359,10 @@ public class LrcView extends View {
}); });
} }
/**
* 加载在线歌词默认使用 utf-8 编码
*
* @param lrcUrl 歌词文件的网络地址
*/
public void loadLrcByUrl(String lrcUrl) {
loadLrcByUrl(lrcUrl, "utf-8");
}
/**
* 加载在线歌词
*
* @param lrcUrl 歌词文件的网络地址
* @param charset 编码格式
*/
public void loadLrcByUrl(String lrcUrl, String charset) {
String flag = "url://" + lrcUrl;
setFlag(flag);
new AsyncTask<String, Integer, String>() {
@Override
protected String doInBackground(String... params) {
return LrcUtils.getContentFromNetwork(params[0], params[1]);
}
@Override
protected void onPostExecute(String lrcText) {
if (getFlag() == flag) {
loadLrc(lrcText);
}
}
}.execute(lrcUrl, charset);
}
/**
* 歌词是否有效
*
* @return true如果歌词有效否则false
*/
public boolean hasLrc() { public boolean hasLrc() {
return !mLrcEntryList.isEmpty(); return !mLrcEntryList.isEmpty();
} }
/**
* 刷新歌词
*
* @param time 当前播放时间
*/
public void updateTime(long time) { public void updateTime(long time) {
runOnUi( runOnUi(
() -> { () -> {
@ -484,12 +382,6 @@ public class LrcView extends View {
}); });
} }
/**
* 将歌词滚动到指定时间
*
* @param time 指定的时间
* @deprecated 请使用 {@link #updateTime(long)} 代替
*/
@Deprecated @Deprecated
public void onDrag(long time) { public void onDrag(long time) {
updateTime(time); updateTime(time);
@ -513,7 +405,6 @@ public class LrcView extends View {
int centerY = getHeight() / 2; int centerY = getHeight() / 2;
// 无歌词文件
if (!hasLrc()) { if (!hasLrc()) {
mLrcPaint.setColor(mCurrentTextColor); mLrcPaint.setColor(mCurrentTextColor);
@SuppressLint("DrawAllocation") @SuppressLint("DrawAllocation")
@ -570,11 +461,6 @@ public class LrcView extends View {
} }
} }
/**
* 画一行歌词
*
* @param y 歌词中心 Y 坐标
*/
private void drawText(Canvas canvas, StaticLayout staticLayout, float y) { private void drawText(Canvas canvas, StaticLayout staticLayout, float y) {
canvas.save(); canvas.save();
canvas.translate(mLrcPadding, y - (staticLayout.getHeight() >> 1)); canvas.translate(mLrcPadding, y - (staticLayout.getHeight() >> 1));
@ -661,17 +547,14 @@ public class LrcView extends View {
invalidate(); invalidate();
} }
/** 将中心行微调至正中心 */
private void adjustCenter() { private void adjustCenter() {
smoothScrollTo(getCenterLine(), ADJUST_DURATION); smoothScrollTo(getCenterLine(), ADJUST_DURATION);
} }
/** 滚动到某一行 */
private void smoothScrollTo(int line) { private void smoothScrollTo(int line) {
smoothScrollTo(line, mAnimationDuration); smoothScrollTo(line, mAnimationDuration);
} }
/** 滚动到某一行 */
private void smoothScrollTo(int line, long duration) { private void smoothScrollTo(int line, long duration) {
float offset = getOffset(line); float offset = getOffset(line);
endAnimation(); endAnimation();
@ -687,14 +570,12 @@ public class LrcView extends View {
mAnimator.start(); mAnimator.start();
} }
/** 结束滚动动画 */
private void endAnimation() { private void endAnimation() {
if (mAnimator != null && mAnimator.isRunning()) { if (mAnimator != null && mAnimator.isRunning()) {
mAnimator.end(); mAnimator.end();
} }
} }
/** 二分法查找当前时间应该显示的行数(最后一个 <= time 的行数) */
private int findShowLine(long time) { private int findShowLine(long time) {
int left = 0; int left = 0;
int right = mLrcEntryList.size(); int right = mLrcEntryList.size();
@ -716,7 +597,6 @@ public class LrcView extends View {
return 0; return 0;
} }
/** 获取当前在视图中央的行数 */
private int getCenterLine() { private int getCenterLine() {
int centerLine = 0; int centerLine = 0;
float minDistance = Float.MAX_VALUE; float minDistance = Float.MAX_VALUE;
@ -729,7 +609,6 @@ public class LrcView extends View {
return centerLine; return centerLine;
} }
/** 获取歌词距离视图顶部的距离 采用懒加载方式 */
private float getOffset(int line) { private float getOffset(int line) {
if (mLrcEntryList.get(line).getOffset() == Float.MIN_VALUE) { if (mLrcEntryList.get(line).getOffset() == Float.MIN_VALUE) {
float offset = getHeight() / 2; float offset = getHeight() / 2;
@ -744,12 +623,10 @@ public class LrcView extends View {
return mLrcEntryList.get(line).getOffset(); return mLrcEntryList.get(line).getOffset();
} }
/** 获取歌词宽度 */
private float getLrcWidth() { private float getLrcWidth() {
return getWidth() - mLrcPadding * 2; return getWidth() - mLrcPadding * 2;
} }
/** 在主线程中运行 */
private void runOnUi(Runnable r) { private void runOnUi(Runnable r) {
if (Looper.myLooper() == Looper.getMainLooper()) { if (Looper.myLooper() == Looper.getMainLooper()) {
r.run(); r.run();
@ -766,13 +643,7 @@ public class LrcView extends View {
this.mFlag = flag; this.mFlag = flag;
} }
/** 播放按钮点击监听器,点击后应该跳转到指定播放位置 */
public interface OnPlayClickListener { public interface OnPlayClickListener {
/**
* 播放按钮被点击应该跳转到指定播放位置
*
* @return 是否成功消费该事件如果成功消费则会更新UI
*/
boolean onPlayClick(long time); boolean onPlayClick(long time);
} }
} }

View file

@ -117,7 +117,7 @@ object LyricUtil {
return "$lrcRootPath$title - $artist.lrc" return "$lrcRootPath$title - $artist.lrc"
} }
fun getLrcOriginalPath(filePath: String): String { private fun getLrcOriginalPath(filePath: String): String {
return filePath.replace(filePath.substring(filePath.lastIndexOf(".") + 1), "lrc") return filePath.replace(filePath.substring(filePath.lastIndexOf(".") + 1), "lrc")
} }
@ -160,9 +160,9 @@ object LyricUtil {
} }
fun getEmbeddedSyncedLyrics(data: String): String? { fun getEmbeddedSyncedLyrics(data: String): String? {
val embeddedLyrics = try{ val embeddedLyrics = try {
AudioFileIO.read(File(data)).tagOrCreateDefault.getFirst(FieldKey.LYRICS) AudioFileIO.read(File(data)).tagOrCreateDefault.getFirst(FieldKey.LYRICS)
} catch(e: Exception){ } catch (e: Exception) {
return null return null
} }
return if (AbsSynchronizedLyrics.isSynchronized(embeddedLyrics)) { return if (AbsSynchronizedLyrics.isSynchronized(embeddedLyrics)) {