From 073fd37da711f026ba669d0c431a5e4a96aad56b Mon Sep 17 00:00:00 2001 From: muqing <1966944300@qq.com> Date: Wed, 21 Feb 2024 18:24:34 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8DBUG=EF=BC=8C=E6=9B=B4?= =?UTF-8?q?=E6=96=B0UI?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/build.gradle | 2 +- .../java/com/dirror/lyricviewx/ILyricViewX.kt | 257 ++++ .../java/com/dirror/lyricviewx/LyricEntry.kt | 82 ++ .../java/com/dirror/lyricviewx/LyricUtil.kt | 289 +++++ .../java/com/dirror/lyricviewx/LyricViewX.kt | 1097 +++++++++++++++++ .../java/com/dirror/lyricviewx/ReadyHelper.kt | 51 + .../lyricviewx/extension/BlurMaskFilterExt.kt | 15 + app/src/main/res/mipmap-hdpi/icon.png | Bin 0 -> 107974 bytes lyricviewx/.gitignore | 1 + lyricviewx/build.gradle | 31 + 10 files changed, 1824 insertions(+), 1 deletion(-) create mode 100644 app/src/main/java/com/dirror/lyricviewx/ILyricViewX.kt create mode 100644 app/src/main/java/com/dirror/lyricviewx/LyricEntry.kt create mode 100644 app/src/main/java/com/dirror/lyricviewx/LyricUtil.kt create mode 100644 app/src/main/java/com/dirror/lyricviewx/LyricViewX.kt create mode 100644 app/src/main/java/com/dirror/lyricviewx/ReadyHelper.kt create mode 100644 app/src/main/java/com/dirror/lyricviewx/extension/BlurMaskFilterExt.kt create mode 100644 app/src/main/res/mipmap-hdpi/icon.png create mode 100644 lyricviewx/.gitignore create mode 100644 lyricviewx/build.gradle diff --git a/app/build.gradle b/app/build.gradle index 3e5de3f..7245777 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -19,7 +19,7 @@ android { //noinspection ExpiredTargetSdkVersion,OldTargetApi targetSdk 31 versionCode 1 - versionName "2.0.0" + versionName "2.1.0" } compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 diff --git a/app/src/main/java/com/dirror/lyricviewx/ILyricViewX.kt b/app/src/main/java/com/dirror/lyricviewx/ILyricViewX.kt new file mode 100644 index 0000000..d0422ee --- /dev/null +++ b/app/src/main/java/com/dirror/lyricviewx/ILyricViewX.kt @@ -0,0 +1,257 @@ +package com.dirror.lyricviewx + +import android.graphics.Typeface +import android.graphics.drawable.Drawable +import android.text.Layout +import androidx.annotation.ColorInt +import androidx.annotation.FloatRange +import androidx.annotation.Px +import java.io.File + +const val GRAVITY_CENTER = 0 // 居中 +const val GRAVITY_LEFT = 1 // 左 +const val GRAVITY_RIGHT = 2 // 右 + +fun Int.toLayoutAlign(): Layout.Alignment { + return when (this) { + GRAVITY_LEFT -> Layout.Alignment.ALIGN_NORMAL + GRAVITY_CENTER -> Layout.Alignment.ALIGN_CENTER + GRAVITY_RIGHT -> Layout.Alignment.ALIGN_OPPOSITE + else -> Layout.Alignment.ALIGN_CENTER + } +} + +/** + * LyricViewX 接口 + * 从 LyricViewX 提取,方便管理 + * + * @author Moriafly + * @since 2021年1月28日16:29:16 + */ +interface LyricViewXInterface { + + /** + * 设置整句之间的间隔高度 + * @param height px + */ + fun setSentenceDividerHeight(@Px height: Float) + + /** + * 设置原句与翻译之间的间隔高度 + * @param height px + */ + fun setTranslateDividerHeight(@Px height: Float) + + /** + * 设置歌词整体的垂直偏移值,配合[setHorizontalOffsetPercent]使用 + * @param offset px + * + * @see [setHorizontalOffsetPercent] + */ + fun setHorizontalOffset(@Px offset: Float) + + /** + * 设置歌词整体的垂直偏移,相对于控件高度的百分比,0.5f即表示居中,配合[setHorizontalOffset]使用 + * + * @param percent 0.0f ~ 1.0f + * + * @see [setHorizontalOffset] + */ + fun setHorizontalOffsetPercent(@FloatRange(from = 0.0, to = 1.0) percent: Float) + + /** + * 设置翻译相对与原词之间的缩放比例值 + * @param scaleValue 一般来说 0.8f 是个不错的值 + */ + fun setTranslateTextScaleValue(@FloatRange(from = 0.1, to = 2.0) scaleValue: Float) + + /** + * 设置文字的对齐方向 + */ + fun setTextGravity(gravity: Int) + + /** + * 设置非当前行歌词字体颜色 [normalColor] + */ + fun setNormalColor(@ColorInt normalColor: Int) + + /** + * 普通歌词文本字体大小 [size],单位 px + */ + fun setNormalTextSize(@Px size: Float) + + /** + * 当前歌词文本字体大小 + */ + fun setCurrentTextSize(size: Float) + + /** + * 设置当前行歌词的字体颜色 + */ + fun setCurrentColor(currentColor: Int) + + /** + * 设置拖动歌词时选中歌词的字体颜色 + */ + fun setTimelineTextColor(timelineTextColor: Int) + + /** + * 设置拖动歌词时时间线的颜色 + */ + fun setTimelineColor(timelineColor: Int) + + /** + * 设置拖动歌词时右侧时间字体颜色 + */ + fun setTimeTextColor(timeTextColor: Int) + + /** + * 设置歌词为空时屏幕中央显示的文字 [label],如“暂无歌词” + */ + fun setLabel(label: String) + + /** + * 加载歌词文本 + * 两种语言的歌词时间戳需要一致 + * + * @param mainLyricText 第一种语言歌词文本 + * @param secondLyricText 可选,第二种语言歌词文本 + */ + fun loadLyric(mainLyricText: String?, secondLyricText: String? = null) + + /** + * 加载歌词 [LyricEntry] 集合 + * 如果你在 Service 等地方自行解析歌词包装成 [LyricEntry] 集合,那么可以使用此方法载入歌词 + * + * @param lyricEntries 歌词集合 + * @since 1.3.1 + */ + fun loadLyric(lyricEntries: List) + + /** + * 刷新歌词 + * + * @param time 当前播放时间 + */ + fun updateTime(time: Long, force: Boolean = false) + + /** + * 设置歌词是否允许拖动 + * + * @param draggable 是否允许拖动 + * @param onPlayClickListener 设置歌词拖动后播放按钮点击监听器,如果允许拖动,则不能为 null + */ + fun setDraggable(draggable: Boolean, onPlayClickListener: OnPlayClickListener?) + + /** + * 设置单击 + */ + fun setOnSingerClickListener(onSingerClickListener: OnSingleClickListener?) + + /** + * 获取当前歌词每句实体,可用于歌词分享 + * + * @return LyricEntry 集合 + */ + fun getLyricEntryList(): List + + /** + * 设置当前歌词每句实体 + */ + fun setLyricEntryList(newList: List) + + /** + * 获取当前行歌词 + */ + fun getCurrentLineLyricEntry(): LyricEntry? + + /** + * 为歌词设置自定义的字体 + * + * @param file 字体文件 + */ + fun setLyricTypeface(file: File) + + /** + * 为歌词设置自定义的字体 + * + * @param path 字体文件路径 + */ + fun setLyricTypeface(path: String) + + /** + * 为歌词设置自定义的字体,可为空,若为空则应清除字体 + * + * @param typeface 字体对象 + */ + fun setLyricTypeface(typeface: Typeface?) + + /** + * 为歌词的过渡动画设置阻尼比(数值越大,回弹次数越多) + * + * @param dampingRatio 阻尼比 详见[androidx.dynamicanimation.animation.SpringForce] + */ + fun setDampingRatioForLyric(dampingRatio: Float) + + /** + * 为歌词视图的滚动动画设置阻尼比(数值越大,回弹次数越多) + * + * @param dampingRatio 阻尼比 详见[androidx.dynamicanimation.animation.SpringForce] + */ + fun setDampingRatioForViewPort(dampingRatio: Float) + + /** + * 为歌词的过渡动画设置刚度(数值越大,动画越短) + * + * @param stiffness 刚度 详见[androidx.dynamicanimation.animation.SpringForce] + */ + fun setStiffnessForLyric(stiffness: Float) + + /** + * 为歌词视图的滚动动画设置刚度(数值越大,动画越短) + * + * @param stiffness 刚度 详见[androidx.dynamicanimation.animation.SpringForce] + */ + fun setStiffnessForViewPort(stiffness: Float) + + /** + * 设置跳转播放按钮 + */ + fun setPlayDrawable(drawable: Drawable) + + /** + * 设置是否绘制歌词翻译 + */ + fun setIsDrawTranslation(isDrawTranslation: Boolean) + + /** + * 是否开启特定的模糊效果 + */ + fun setIsEnableBlurEffect(isEnableBlurEffect: Boolean) + + /** + * 设置元素的偏移百分比,0.5f即表示居中 + * + * @param itemOffsetPercent 0f ~ 1f 偏移百分比 + */ + fun setItemOffsetPercent(@FloatRange(from = 0.0, to = 1.0) itemOffsetPercent: Float) +} + +/** + * 播放按钮点击监听器,点击后应该跳转到指定播放位置 + */ +interface OnPlayClickListener { + /** + * 播放按钮被点击,应该跳转到指定播放位置 + * + * @return 是否成功消费该事件,如果成功消费,则会更新UI + */ + fun onPlayClick(time: Long): Boolean +} + +/** + * 点击歌词布局 + */ +interface OnSingleClickListener { + fun onClick() +} \ No newline at end of file diff --git a/app/src/main/java/com/dirror/lyricviewx/LyricEntry.kt b/app/src/main/java/com/dirror/lyricviewx/LyricEntry.kt new file mode 100644 index 0000000..b92499d --- /dev/null +++ b/app/src/main/java/com/dirror/lyricviewx/LyricEntry.kt @@ -0,0 +1,82 @@ +package com.dirror.lyricviewx + +import android.os.Build +import android.text.Layout +import android.text.StaticLayout +import android.text.TextPaint + +/** + * 一行歌词实体 + * @since 2021年1月19日09:51:40 Moriafly 基于 LrcEntry 改造,转换为 kt ,移除部分过时方法 + * @param time 歌词时间 + * @param text 歌词文本 + */ +class LyricEntry(@JvmField val time: Long, @JvmField val text: String) : Comparable { + + /** + * 第二文本 + */ + @JvmField + var secondText: String? = null + + /** + * staticLayout + */ + var staticLayout: StaticLayout? = null + private set + var secondStaticLayout: StaticLayout? = null + private set + + @Deprecated("存在不显示翻译的情况,会导致offset发生改变,故不再固定存储offset") + /** + * 歌词距离视图顶部的距离 + */ + var offset = Float.MIN_VALUE + + /** + * 初始化 + * @param textPaint 文本画笔 + * @param width 宽度 + * @param align 位置 + */ + fun init( + textPaint: TextPaint, + secondTextPaint: TextPaint, + width: Int, align: Layout.Alignment + ) { + staticLayout = createStaticLayout(text, textPaint, width, align) + secondStaticLayout = createStaticLayout(secondText, secondTextPaint, width, align) + offset = Float.MIN_VALUE + } + + /** + * 继承 Comparable 比较 + * @param other LyricEntry + * @return 时间差 + */ + override fun compareTo(other: LyricEntry): Int { + return (time - other.time).toInt() + } + + companion object { + fun createStaticLayout( + text: String?, + paint: TextPaint, + width: Number, + align: Layout.Alignment + ): StaticLayout? { + if (text.isNullOrEmpty()) return null + return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + StaticLayout.Builder + .obtain(text, 0, text.length, paint, width.toInt()) + .setAlignment(align) + .setLineSpacing(0f, 1f) + .setIncludePad(false) + .build() + } else { + StaticLayout(text, paint, width.toInt(), align, 1f, 0f, false) + } + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/dirror/lyricviewx/LyricUtil.kt b/app/src/main/java/com/dirror/lyricviewx/LyricUtil.kt new file mode 100644 index 0000000..cb3b4b4 --- /dev/null +++ b/app/src/main/java/com/dirror/lyricviewx/LyricUtil.kt @@ -0,0 +1,289 @@ +package com.dirror.lyricviewx + +import android.animation.ArgbEvaluator +import android.animation.ValueAnimator +import android.annotation.SuppressLint +import android.graphics.Rect +import android.text.TextUtils +import android.text.format.DateUtils +import android.view.MotionEvent +import java.io.* +import java.net.HttpURLConnection +import java.net.URL +import java.nio.charset.StandardCharsets +import java.util.* +import java.util.regex.Pattern + +/** + * 工具类 + * 原 LrcUtils 转 Kotlin + */ +object LyricUtil { + + private val PATTERN_LINE = Pattern.compile("((\\[\\d\\d:\\d\\d\\.\\d{2,3}])+)(.+)") + private val PATTERN_TIME = Pattern.compile("\\[(\\d\\d):(\\d\\d)\\.(\\d{2,3})]") + private val argbEvaluator = ArgbEvaluator() + + /** + * 从文件解析双语歌词 + */ + fun parseLrc(lrcFiles: Array?): List? { + if (lrcFiles == null || lrcFiles.size != 2 || lrcFiles[0] == null) { + return null + } + val mainLrcFile = lrcFiles[0] + val secondLrcFile = lrcFiles[1] + val mainEntryList = parseLrc(mainLrcFile) + val secondEntryList = parseLrc(secondLrcFile) + if (mainEntryList != null && secondEntryList != null) { + for (mainEntry in mainEntryList) { + for (secondEntry in secondEntryList) { + if (mainEntry.time == secondEntry.time) { + mainEntry.secondText = secondEntry.text + } + } + } + } + return mainEntryList + } + + /** + * 从文件解析歌词 + */ + private fun parseLrc(lrcFile: File?): List? { + if (lrcFile == null || !lrcFile.exists()) { + return null + } + val entryList: MutableList = ArrayList() + try { + val br = + BufferedReader(InputStreamReader(FileInputStream(lrcFile), StandardCharsets.UTF_8)) + var line: String + while (br.readLine().also { line = it } != null) { + val list = parseLine(line) + if (list != null && list.isNotEmpty()) { + entryList.addAll(list) + } + } + br.close() + } catch (e: IOException) { + e.printStackTrace() + } + entryList.sort() + return entryList + } + + /** + * 从文本解析双语歌词 + */ + fun parseLrc(lrcTexts: Array?): List? { + if (lrcTexts == null || lrcTexts.size != 2 || TextUtils.isEmpty(lrcTexts[0])) { + return null + } + val mainLrcText = lrcTexts[0] + val secondLrcText = lrcTexts[1] + val mainEntryList = mainLrcText?.let { parseLrc(it) } + + /** + * 当输入的secondLrcText为空时,按如下格式解析歌词 + * (音乐标签下载的第二种歌词格式) + * + * [00:21.11]いつも待ち合わせより15分前集合 + * [00:21.11]总会比相约时间早15分钟集合 + * [00:28.32]駅の改札ぬける + * [00:28.32]穿过车站的检票口 + * [00:31.39]ざわめきにわくわくだね + * [00:31.39]嘈杂声令内心兴奋不已 + * [00:35.23]どこへ向かうかなんて + * [00:35.23]不在意接下来要去哪里 + */ + if (TextUtils.isEmpty(secondLrcText)) { + var lastEntry: LyricEntry? = null + return mainEntryList?.filter { now -> + if (lastEntry == null) { + lastEntry = now + return@filter true + } + + if (lastEntry!!.time == now.time) { + lastEntry!!.secondText = now.text + lastEntry = null + return@filter false + } + + lastEntry = now + true + } + } + + val secondEntryList = secondLrcText?.let { parseLrc(it) } + if (mainEntryList != null && secondEntryList != null) { + for (mainEntry in mainEntryList) { + for (secondEntry in secondEntryList) { + if (mainEntry.time == secondEntry.time) { + mainEntry.secondText = secondEntry.text + } + } + } + } + return mainEntryList + } + + /** + * 从文本解析歌词 + */ + private fun parseLrc(lrcText: String): List? { + var lyricText = lrcText.trim() + if (TextUtils.isEmpty(lyricText)) return null + + if (lyricText.startsWith("\uFEFF")) { + lyricText = lyricText.replace("\uFEFF", "") + } + + // 针对传入 Language="Media Monkey Format"; Lyrics="......"; 的情况 + lyricText = lyricText.substringAfter("Lyrics=\"") + .substringBeforeLast("\";") + + val entryList: MutableList = ArrayList() + val array = lyricText.split("\\n".toRegex()).toTypedArray() + for (line in array) { + val list = parseLine(line) + if (!list.isNullOrEmpty()) { + entryList.addAll(list) + } + } + entryList.sort() + return entryList + } + + /** + * 获取网络文本,需要在工作线程中执行 + */ + fun getContentFromNetwork(url: String?, charset: String?): String? { + var lrcText: String? = null + try { + val url = URL(url) + val conn = url.openConnection() as HttpURLConnection + conn.requestMethod = "GET" + conn.connectTimeout = 10000 + conn.readTimeout = 10000 + if (conn.responseCode == 200) { + val `is` = conn.inputStream + val bos = ByteArrayOutputStream() + val buffer = ByteArray(1024) + var len: Int + while (`is`.read(buffer).also { len = it } != -1) { + bos.write(buffer, 0, len) + } + `is`.close() + bos.close() + lrcText = bos.toString(charset) + } + } catch (e: Exception) { + e.printStackTrace() + } + return lrcText + } + + /** + * 解析一行歌词 + */ + private fun parseLine(line: String): List? { + var lyricLine = line + if (TextUtils.isEmpty(lyricLine)) { + return null + } + lyricLine = lyricLine.trim { it <= ' ' } + // [00:17.65]让我掉下眼泪的 + val lineMatcher = PATTERN_LINE.matcher(lyricLine) + if (!lineMatcher.matches()) { + return null + } + val times = lineMatcher.group(1)!! + val text = lineMatcher.group(3)!! + val entryList: MutableList = ArrayList() + + // [00:17.65] + val timeMatcher = PATTERN_TIME.matcher(times) + while (timeMatcher.find()) { + val min = timeMatcher.group(1)!!.toLong() + val sec = timeMatcher.group(2)!!.toLong() + val milString = timeMatcher.group(3)!! + var mil = milString.toLong() + // 如果毫秒是两位数,需要乘以 10,when 新增支持 1 - 6 位毫秒,很多获取的歌词存在不同的毫秒位数 + when (milString.length) { + 1 -> mil *= 100 + 2 -> mil *= 10 + 4 -> mil /= 10 + 5 -> mil /= 100 + 6 -> mil /= 1000 + } + val time = min * DateUtils.MINUTE_IN_MILLIS + sec * DateUtils.SECOND_IN_MILLIS + mil + entryList.add(LyricEntry(time, text)) + } + return entryList + } + + /** + * 转为[分:秒] + */ + fun formatTime(milli: Long): String { + val m = (milli / DateUtils.MINUTE_IN_MILLIS).toInt() + val s = (milli / DateUtils.SECOND_IN_MILLIS % 60).toInt() + val mm = String.format(Locale.getDefault(), "%02d", m) + val ss = String.format(Locale.getDefault(), "%02d", s) + return "$mm:$ss" + } + + /** + * BUG java.lang.NoSuchFieldException: No field sDurationScale in class Landroid/animation/ValueAnimator; #3 + */ + @SuppressLint("SoonBlockedPrivateApi") + @Deprecated("") + fun resetDurationScale() { + try { + val mField = ValueAnimator::class.java.getDeclaredField("sDurationScale") + mField.isAccessible = true + mField.setFloat(null, 1f) + } catch (e: Exception) { + e.printStackTrace() + } + } + + /** + * 结合fraction,计算两个值之间的比例 + */ + fun calcScaleValue(a: Float, b: Float, f: Float, reverse: Boolean = false): Float { + if (b == 0f) return 1f + return 1f + ((a - b) / b) * (if (reverse) 1f - f else f) + } + + /** + * 颜色值插值函数 + */ + fun lerpColor(a: Int, b: Int, f: Float): Int { + return argbEvaluator.evaluate(f, a, b) as Int + } + + /** + * 简单的插值函数 + */ + fun lerp(from: Float, to: Float, fraction: Float): Float { + return from + (to - from) * fraction + } + + /** + * 判断MotionEvent是否发生在Rect中 + */ + fun MotionEvent.insideOf(rect: Rect?): Boolean { + rect ?: return false + return rect.contains(x.toInt(), y.toInt()) + } + + fun normalize(min: Float, max: Float, value: Float, limit: Boolean = false): Float { + if (min == max) return 1f + return ((value - min) / (max - min)).let { + if (limit) it.coerceIn(0f, 1f) else it + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/dirror/lyricviewx/LyricViewX.kt b/app/src/main/java/com/dirror/lyricviewx/LyricViewX.kt new file mode 100644 index 0000000..7c0cd01 --- /dev/null +++ b/app/src/main/java/com/dirror/lyricviewx/LyricViewX.kt @@ -0,0 +1,1097 @@ +package com.dirror.lyricviewx +import android.animation.ValueAnimator +import android.annotation.SuppressLint +import android.content.Context +import android.graphics.Canvas +import android.graphics.Paint +import android.graphics.Typeface +import android.graphics.drawable.Drawable +import android.os.Looper +import android.text.Layout +import android.text.StaticLayout +import android.text.TextPaint +import android.text.format.DateUtils +import android.util.AttributeSet +import android.view.GestureDetector +import android.view.GestureDetector.SimpleOnGestureListener +import android.view.MotionEvent +import android.widget.Scroller +import androidx.annotation.FloatRange +import androidx.core.content.ContextCompat +import androidx.dynamicanimation.animation.SpringForce +import androidx.dynamicanimation.animation.springAnimationOf +import androidx.dynamicanimation.animation.withSpringForceProperties +import com.dirror.lyricviewx.LyricUtil.calcScaleValue +import com.dirror.lyricviewx.LyricUtil.formatTime +import com.dirror.lyricviewx.LyricUtil.insideOf +import com.dirror.lyricviewx.LyricUtil.lerp +import com.dirror.lyricviewx.LyricUtil.lerpColor +import com.dirror.lyricviewx.LyricUtil.normalize +import com.dirror.lyricviewx.extension.BlurMaskFilterExt +import com.lalilu.easeview.EaseView +import com.lalilu.easeview.animatevalue.BoolValue +import com.lalilu.easeview.animatevalue.FloatListAnimateValue +import com.muqingbfq.R +import java.io.File +import kotlin.concurrent.thread +import kotlin.math.abs +import kotlin.math.max +open class LyricViewX : EaseView, LyricViewXInterface { + constructor(context: Context) : super(context) { + init(null) + } + + constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { + init(attrs) + } + + companion object { + private const val TAG = "LyricViewX" + // 时间线持续时间 + private const val TIMELINE_KEEP_TIME = 3 * DateUtils.SECOND_IN_MILLIS + + /** 单句歌词集合 */ + @JvmField + val lyricEntryList: MutableList = ArrayList() + + @JvmStatic + fun lrc(a: String?, b: String?) { + val lrcEntries = LyricUtil.parseLrc(arrayOf(a, b)) + if (!lrcEntries.isNullOrEmpty()) { + lyricEntryList.addAll(lrcEntries) + } + lyricEntryList.sort() + } + } + + private val readyHelper = ReadyHelper() + private val blurMaskFilterExt = BlurMaskFilterExt() + + + /** 主歌词画笔 */ + private val lyricPaint = TextPaint() + + /** 副歌词(一般为翻译歌词)画笔 */ + private val secondLyricPaint = TextPaint() + + /** 时间文字画笔 */ + private val timePaint = TextPaint() + + private var timeFontMetrics: Paint.FontMetrics? = null + + /** 跳转播放按钮 */ + private var playDrawable: Drawable? = null + + private var translateDividerHeight = 0f + private var sentenceDividerHeight = 0f + private var animationDuration: Long = 0 + private var normalTextColor = 0 + private var normalTextSize = 0f + private var currentTextColor = 0 + private var currentTextSize = 0f + private var translateTextScaleValue = 1f + private var timelineTextColor = 0 + private var timelineColor = 0 + private var timeTextColor = 0 + private var drawableWidth = 0 + private var timeTextWidth = 0 + private var defaultLabel: String? = null + private var lrcPadding = 0f + private var onPlayClickListener: OnPlayClickListener? = null + private var onSingerClickListener: OnSingleClickListener? = null + private var animator: ValueAnimator? = null + private var gestureDetector: GestureDetector? = null + private var scroller: Scroller? = null + private var flag: Any? = null + private var isTouching = false + private var isFling = false + private var textGravity = GRAVITY_CENTER // 歌词显示位置,靠左 / 居中 / 靠右 + private var horizontalOffset: Float = 0f + private var horizontalOffsetPercent: Float = 0.5f + private var itemOffsetPercent: Float = 0.5f + private var dampingRatioForLyric: Float = SpringForce.DAMPING_RATIO_LOW_BOUNCY + private var dampingRatioForViewPort: Float = SpringForce.DAMPING_RATIO_NO_BOUNCY + private var stiffnessForLyric: Float = SpringForce.STIFFNESS_LOW + private var stiffnessForViewPort: Float = SpringForce.STIFFNESS_VERY_LOW + + private var currentLine = 0 // 当前高亮显示的歌词 + private val focusLine: Int // 当前焦点歌词 + get() = if (isTouching || isFling) centerLine else currentLine + + /** + * 获取当前在视图中央的行数 + */ + private val centerLine: Int + get() { + var centerLine = 0 + var minDistance = Float.MAX_VALUE + var tempDistance: Float + + for (i in lyricEntryList.indices) { + tempDistance = abs(mViewPortOffset - getOffset(i)) + if (tempDistance < minDistance) { + minDistance = tempDistance + centerLine = i + } + } + return centerLine + } + + /** + * 获取歌词宽度 + */ + open val lrcWidth: Float + get() = width - lrcPadding * 2 + + /** + * 歌词整体的垂直偏移值 + */ + open val startOffset: Float + get() = height.toFloat() * horizontalOffsetPercent + horizontalOffset + + + /** + * 原有的mOffset被拆分成两个独立的offset,这样可以更好地让进度和拖拽滚动独立开来 + */ + private var mCurrentOffset = 0f // 实际的歌词进度Offset + private var mViewPortOffset = 0f // 歌词显示窗口的Offset + + private var animateProgress = 0f // 动画进度 + private var animateTargetOffset = 0f // 动画目标Offset + private var animateStartOffset = 0f // 动画起始Offset + + private val viewPortSpringAnimator = springAnimationOf( + getter = { mViewPortOffset }, + setter = { value -> + if (!isShowTimeline.value && !isTouching && !isFling) { + mViewPortOffset = value + invalidate() + } + } + ).withSpringForceProperties { + dampingRatio = dampingRatioForViewPort + stiffness = stiffnessForViewPort + finalPosition = 0f + } + + /** + * 弹性动画Scroller + */ + private val progressSpringAnimator = springAnimationOf( + getter = { mCurrentOffset }, + setter = { value -> + animateProgress = normalize(animateStartOffset, animateTargetOffset, value) + mCurrentOffset = value + + if (!isShowTimeline.value && !isTouching && !isFling) { + viewPortSpringAnimator.animateToFinalPosition(animateTargetOffset) + } + invalidate() + } + ).withSpringForceProperties { + dampingRatio = dampingRatioForLyric + stiffness = stiffnessForLyric + finalPosition = 0f + } + + @SuppressLint("CustomViewStyleable") + private fun init(attrs: AttributeSet?) { + readyHelper.readyState = STATE_INITIALIZING + val typedArray = context.obtainStyledAttributes(attrs, R.styleable.LyricView) + currentTextSize = typedArray.getDimension(R.styleable.LyricView_lrcTextSize, resources.getDimension(R.dimen.lrc_text_size)) + normalTextSize = typedArray.getDimension(R.styleable.LyricView_lrcNormalTextSize, resources.getDimension(R.dimen.lrc_text_size)) + if (normalTextSize == 0f) { + normalTextSize = currentTextSize + } + + sentenceDividerHeight = + typedArray.getDimension(R.styleable.LyricView_lrcSentenceDividerHeight, resources.getDimension(R.dimen.lrc_sentence_divider_height)) + translateDividerHeight = + typedArray.getDimension(R.styleable.LyricView_lrcTranslateDividerHeight, resources.getDimension(R.dimen.lrc_translate_divider_height)) + val defDuration = resources.getInteger(R.integer.lrc_animation_duration) + animationDuration = typedArray.getInt(R.styleable.LyricView_lrcAnimationDuration, defDuration).toLong() + animationDuration = + if (animationDuration < 0) defDuration.toLong() else animationDuration + + normalTextColor = typedArray.getColor( + R.styleable.LyricView_lrcNormalTextColor, + ContextCompat.getColor(context, R.color.lrc_normal_text_color) + ) + currentTextColor = typedArray.getColor( + R.styleable.LyricView_lrcCurrentTextColor, + ContextCompat.getColor(context, R.color.lrc_current_text_color) + ) + timelineTextColor = typedArray.getColor( + R.styleable.LyricView_lrcTimelineTextColor, + ContextCompat.getColor(context, R.color.lrc_timeline_text_color) + ) + defaultLabel = typedArray.getString(R.styleable.LyricView_lrcLabel) + defaultLabel = if (defaultLabel.isNullOrEmpty()) "暂无歌词" else defaultLabel + lrcPadding = typedArray.getDimension(R.styleable.LyricView_lrcPadding, 0f) + timelineColor = typedArray.getColor( + R.styleable.LyricView_lrcTimelineColor, + ContextCompat.getColor(context, R.color.lrc_timeline_color) + ) + val timelineHeight = typedArray.getDimension( + R.styleable.LyricView_lrcTimelineHeight, + resources.getDimension(R.dimen.lrc_timeline_height) + ) + playDrawable = typedArray.getDrawable(R.styleable.LyricView_lrcPlayDrawable) + playDrawable = if (playDrawable == null) ContextCompat.getDrawable( + context, + R.drawable.lrc_play + ) else playDrawable + timeTextColor = typedArray.getColor( + R.styleable.LyricView_lrcTimeTextColor, + ContextCompat.getColor(context, R.color.lrc_time_text_color) + ) + val timeTextSize = typedArray.getDimension( + R.styleable.LyricView_lrcTimeTextSize, + resources.getDimension(R.dimen.lrc_time_text_size) + ) + textGravity = typedArray.getInteger(R.styleable.LyricView_lrcTextGravity, GRAVITY_CENTER) + translateTextScaleValue = typedArray.getFloat(R.styleable.LyricView_lrcTranslateTextScaleValue, 1f) + horizontalOffset = typedArray.getDimension(R.styleable.LyricView_lrcHorizontalOffset, 0f) + horizontalOffsetPercent = typedArray.getDimension(R.styleable.LyricView_lrcHorizontalOffsetPercent, 0.5f) + itemOffsetPercent = typedArray.getDimension(R.styleable.LyricView_lrcItemOffsetPercent, 0.5f) + isDrawTranslation = typedArray.getBoolean(R.styleable.LyricView_lrcIsDrawTranslation, false) + typedArray.recycle() + drawableWidth = resources.getDimension(R.dimen.lrc_drawable_width).toInt() + timeTextWidth = resources.getDimension(R.dimen.lrc_time_width).toInt() + lyricPaint.isAntiAlias = true + lyricPaint.textSize = currentTextSize + lyricPaint.textAlign = Paint.Align.LEFT +// lyricPaint.setShadowLayer(0.1f, 0f, 1f, Color.DKGRAY) + secondLyricPaint.isAntiAlias = true + secondLyricPaint.textSize = currentTextSize + secondLyricPaint.textAlign = Paint.Align.LEFT +// secondLyricPaint.setShadowLayer(0.1f, 0f, 1f, Color.DKGRAY) + timePaint.isAntiAlias = true + timePaint.textSize = timeTextSize + timePaint.textAlign = Paint.Align.CENTER + timePaint.strokeWidth = timelineHeight + timePaint.strokeCap = Paint.Cap.ROUND + timeFontMetrics = timePaint.fontMetrics + gestureDetector = GestureDetector(context, mSimpleOnGestureListener) + gestureDetector!!.setIsLongpressEnabled(false) + scroller = Scroller(context) + } + + /** + * 歌词是否有效 + * @return true,如果歌词有效,否则false + */ + private fun hasLrc(): Boolean { + return lyricEntryList.isNotEmpty() + } + + override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) { + super.onLayout(changed, left, top, right, bottom) + if (changed) { + initPlayDrawable() + initEntryList() + if (hasLrc()) { + smoothScrollTo(currentLine) + } + } + readyHelper.readyState = STATE_INITIALIZED + } + + private val isShowTimeline = BoolValue().also(::registerValue) + private val isEnableBlurEffect = BoolValue().also(::registerValue) + private val progressKeeper = FloatListAnimateValue().also(::registerValue) + private val blurProgressKeeper = FloatListAnimateValue().also(::registerValue) + + private val heightKeeper = LinkedHashMap() + private val offsetKeeper = LinkedHashMap() + private val minOffsetKeeper = LinkedHashMap() + private val maxOffsetKeeper = LinkedHashMap() + + private var viewPortStartOffset: Float = 0f + private var isDrawTranslationValue = 0f + private var isDrawTranslation: Boolean = false + set(value) { + if (field == value) return + field = value + viewPortStartOffset = mViewPortOffset + isDrawTranslationAnimator.animateToFinalPosition(if (value) 1000f else 0f) + } + private val isDrawTranslationAnimator = springAnimationOf( + getter = { isDrawTranslationValue * 1000f }, + setter = { + isDrawTranslationValue = it / 1000f + + if (!isTouching && !isFling) { + viewPortSpringAnimator.cancel() + + val targetOffset = if (isDrawTranslation) getMaxOffset(focusLine) else getMinOffset(focusLine) + val animateValue = if (isDrawTranslation) isDrawTranslationValue else 1f - isDrawTranslationValue + + mViewPortOffset = lerp(viewPortStartOffset, targetOffset, animateValue) + } + invalidate() + }, + ).withSpringForceProperties { + dampingRatio = SpringForce.DAMPING_RATIO_NO_BOUNCY + stiffness = SpringForce.STIFFNESS_LOW + finalPosition = if (isDrawTranslation) 1000f else 0f + } + + override fun onPreDraw(canvas: Canvas): Boolean { + // 无歌词,只渲染一句无歌词的提示语句 + if (!hasLrc()) { + lyricPaint.color = currentTextColor + lyricPaint.textSize = normalTextSize + LyricEntry.createStaticLayout( + defaultLabel, + lyricPaint, + lrcWidth, + Layout.Alignment.ALIGN_CENTER + )?.let { + drawText( + canvas = canvas, + staticLayout = it, + calcHeightOnly = false, + yOffset = startOffset, + yClipPercentage = 1f + ) + } + return false + } + return super.onPreDraw(canvas) + } + + override fun onDoDraw(canvas: Canvas): Boolean { + val centerY = startOffset + val currentCenterLine = centerLine + + // 当显示时间线时,需要绘制时间线 + if (isShowTimeline.value || isShowTimeline.animateValue > 0f) { + val alpha = (isShowTimeline.animateValue * 255f).toInt() + + // 绘制播放按钮 + playDrawable?.let { + it.alpha = alpha + it.draw(canvas) + } + + // 绘制时间线 + timePaint.color = timelineColor + timePaint.alpha = alpha + canvas.drawLine( + timeTextWidth.toFloat(), centerY, + (width - timeTextWidth).toFloat(), centerY, timePaint + ) + + // 绘制当前时间 + val timeText = formatTime(lyricEntryList[currentCenterLine].time) + val timeX = width - timeTextWidth.toFloat() / 2 + val timeY = centerY - (timeFontMetrics!!.descent + timeFontMetrics!!.ascent) / 2 + timePaint.color = timeTextColor + timePaint.alpha = alpha + canvas.drawText(timeText, timeX, timeY, timePaint) + } + + canvas.translate(0f, mViewPortOffset) + + var yOffset = 0f + var yMinOffset = 0f + var yMaxOffset = 0f + var scaleValue: Float + var progress: Float + var radius: Int + var calcHeightOnly: Boolean + + for (i in lyricEntryList.indices) { + // 根据上一项所计算得到的offset值,判断当前元素是否在需要绘制的区间,如果不在,则只需要计算高度不进行绘制相关计算 + calcHeightOnly = getOffset(i - 1) !in (mViewPortOffset - height)..(mViewPortOffset + height) + progressKeeper.updateTargetValue(i, if (currentLine == i) animateProgress else 0f) + progress = progressKeeper.getValueByIndex(i) + scaleValue = 1f + radius = 0 + + if (!calcHeightOnly) { + when { + // 当前行动画未结束 + progress > 0f -> { + scaleValue = calcScaleValue(currentTextSize, normalTextSize, progress) + lyricPaint.color = lerpColor(normalTextColor, currentTextColor, progress.coerceIn(0f, 1f)) + } + + isShowTimeline.value && i == currentCenterLine -> { + lyricPaint.color = timelineTextColor + } + + else -> { + lyricPaint.color = normalTextColor + } + } + lyricPaint.textSize = normalTextSize + secondLyricPaint.textSize = lyricPaint.textSize * translateTextScaleValue + secondLyricPaint.color = lyricPaint.color + + if (isEnableBlurEffect.value || isEnableBlurEffect.animateValue > 0f) { + radius = when (i) { + currentCenterLine -> 0 + currentCenterLine + 1 -> 3 + currentCenterLine + 2, currentCenterLine - 1 -> 7 + currentCenterLine + 3, currentCenterLine - 2 -> 11 + currentCenterLine + 4, currentCenterLine - 3 -> 20 + else -> 20 + } + blurProgressKeeper.updateTargetValue(i, radius.toFloat()) + radius = blurProgressKeeper.getValueByIndex(i).toInt() + radius = (radius * isEnableBlurEffect.animateValue).toInt() + } + } + + val itemHeight = drawLyricEntry( + canvas = canvas, + entry = lyricEntryList[i], + calcHeightOnly = calcHeightOnly, + yOffset = yOffset, + scaleValue = scaleValue, + blurRadius = radius, + ) { minHeight, maxHeight -> + minOffsetKeeper[i] = yMinOffset + calcOffsetOfItem(minHeight, sentenceDividerHeight) + yMinOffset += minHeight + + maxOffsetKeeper[i] = yMaxOffset + calcOffsetOfItem(maxHeight, sentenceDividerHeight) + yMaxOffset += maxHeight + } + heightKeeper[i] = itemHeight + offsetKeeper[i] = yOffset + calcOffsetOfItem(itemHeight, sentenceDividerHeight) + yOffset += itemHeight + } + return super.onDoDraw(canvas) + } + + /** + * 画一组歌词语句 + * + * @param calcHeightOnly 是否只计算高度 + * @param yOffset 歌词中心 Y 坐标 + * @param scaleValue 缩放比例 + * @param blurRadius 模糊半径 + * + * @return 该组歌词的实际绘制高度 + */ + private fun drawLyricEntry( + canvas: Canvas, + entry: LyricEntry, + calcHeightOnly: Boolean, + yOffset: Float, + scaleValue: Float, + blurRadius: Int, + callback: (minHeight: Float, maxHeight: Float) -> Unit = { _, _ -> } + ): Float { + var tempHeight = 0f + var minTempHeight = 0f + var maxTempHeight = 0f + + entry.staticLayout?.let { + tempHeight += drawText( + canvas = canvas, + staticLayout = it, + calcHeightOnly = calcHeightOnly, + yOffset = yOffset, + yClipPercentage = 1f, + scale = scaleValue, + blurRadius = blurRadius + ) + minTempHeight = tempHeight + maxTempHeight = tempHeight + + entry.secondStaticLayout?.let { second -> + tempHeight += translateDividerHeight * isDrawTranslationValue + maxTempHeight += translateDividerHeight + + tempHeight += drawText( + canvas = canvas, + staticLayout = second, + calcHeightOnly = calcHeightOnly, + yOffset = yOffset + tempHeight, + yClipPercentage = isDrawTranslationValue, + alpha = isDrawTranslationValue, + scale = scaleValue, + blurRadius = blurRadius + ) { _, max -> + maxTempHeight += max + } + } + tempHeight += sentenceDividerHeight + minTempHeight += sentenceDividerHeight + maxTempHeight += sentenceDividerHeight + } + callback(minTempHeight, maxTempHeight) + return tempHeight + } + + /** + * 画一行歌词 + * + * @param calcHeightOnly 是否只计算高度 + * @param yOffset 歌词中心 Y 坐标 + * @param yClipPercentage 垂直裁剪比例 + * @param scale 缩放比例 + * @param alpha 透明度 + * @param blurRadius 模糊半径 实现类似AppleMusic的歌词语句的模糊效果 + * + * @return 实际绘制高度 + */ + private fun drawText( + canvas: Canvas, + staticLayout: StaticLayout, + calcHeightOnly: Boolean = false, + yOffset: Float, + @FloatRange(from = 0.0, to = 1.0) + yClipPercentage: Float = 1f, + scale: Float = 1f, + alpha: Float = 1f, + blurRadius: Int = 0, + callback: (minHeight: Float, maxHeight: Float) -> Unit = { _, _ -> } + ): Float { + if (staticLayout.lineCount == 0) { + callback(0f, 0f) + return 0f + } + if (calcHeightOnly) { + callback(0f, staticLayout.height.toFloat()) + return staticLayout.height * yClipPercentage + } + val lineHeight = staticLayout.height.toFloat() / staticLayout.lineCount.toFloat() + + var yTemp = 0f // y轴临时偏移量 + var pivotYTemp: Float // 缩放中心Y坐标 + var itemActualHeight: Float // 单行实际绘制高度 + var actualHeight = 0f // 实际绘制高度 + + staticLayout.paint.alpha = (alpha * 255f).toInt() + staticLayout.paint.maskFilter = blurMaskFilterExt.get(blurRadius) + + /** + * 由于对StaticLayout整个缩放会使其中间的行间距也被缩放(通过TextPaint的textSize缩放则不会), + * 导致其真实渲染高度大于StaticLayout的height属性的值,同时也没有其他的接口能实现相同的缩放效果(对TextSize缩放会显得卡卡的) + * + * 所以通过Canvas的clipRect,来分别对StaticLayout的每一行文字进行缩放和绘制(StaticLayout的各行高度是一致的) + */ + repeat(staticLayout.lineCount) { + itemActualHeight = lineHeight * yClipPercentage + pivotYTemp = yTemp + itemActualHeight - staticLayout.paint.descent() // TextPaint修改textSize所实现的缩放效果应该就是descent线上的缩放(感觉效果差不多) + + canvas.save() + canvas.translate(lrcPadding, yOffset) + canvas.clipRect(-lrcPadding, yTemp, staticLayout.width.toFloat() + lrcPadding, yTemp + itemActualHeight) + + // 根据文字的gravity设置缩放基点坐标 + when (textGravity) { + GRAVITY_LEFT -> canvas.scale(scale, scale, 0f, pivotYTemp) + GRAVITY_RIGHT -> { + canvas.scale(scale, scale, staticLayout.width.toFloat(), pivotYTemp) + } + + GRAVITY_CENTER -> { + canvas.scale(scale, scale, staticLayout.width / 2f, pivotYTemp) + } + } + staticLayout.draw(canvas) + canvas.restore() + yTemp += itemActualHeight + actualHeight += itemActualHeight + } + callback(0f, staticLayout.height.toFloat()) + return actualHeight + } + + @SuppressLint("ClickableViewAccessibility") + override fun onTouchEvent(event: MotionEvent): Boolean { + if (event.action == MotionEvent.ACTION_UP || event.action == MotionEvent.ACTION_CANCEL) { + isTouching = false + if (hasLrc() && !isFling) { + // TODO 应该为Timeline独立设置一个Enable开关, 这样就可以不需要等待Timeline消失 + postDelayed(hideTimelineRunnable, TIMELINE_KEEP_TIME) + } + } + return gestureDetector!!.onTouchEvent(event) + } + + /** + * 手势监听器 + */ + private val mSimpleOnGestureListener: SimpleOnGestureListener = + object : SimpleOnGestureListener() { + + override fun onDown(e: MotionEvent): Boolean { + // 有歌词并且设置了 mOnPlayClickListener + if (onPlayClickListener != null) { + scroller!!.forceFinished(true) + removeCallbacks(hideTimelineRunnable) + isTouching = true + invalidate() + return true + } + return super.onDown(e) + } + + override fun onScroll( + e1: MotionEvent, + e2: MotionEvent, + distanceX: Float, + distanceY: Float + ): Boolean { + if (hasLrc()) { + // 如果没显示 Timeline 的时候,distanceY 一段距离后再显示时间线 + if (!isShowTimeline.value && abs(distanceY) >= 10) { + // 滚动显示时间线 + isShowTimeline.value = true + } + mViewPortOffset += -distanceY + mViewPortOffset.coerceIn(getOffset(lyricEntryList.size - 1), getOffset(0)) + invalidate() + return true + } + return super.onScroll(e1, e2, distanceX, distanceY) + } + + override fun onFling( + e1: MotionEvent, + e2: MotionEvent, + velocityX: Float, + velocityY: Float + ): Boolean { + if (hasLrc()) { + scroller!!.fling( + 0, mViewPortOffset.toInt(), 0, + velocityY.toInt(), 0, 0, + getOffset(lyricEntryList.size - 1).toInt(), + getOffset(0).toInt() + ) + isFling = true + return true + } + return super.onFling(e1, e2, velocityX, velocityY) + } + + override fun onSingleTapConfirmed(e: MotionEvent): Boolean { + if (!hasLrc() || !isShowTimeline.value || !e.insideOf(playDrawable?.bounds)) { + onSingerClickListener?.onClick() + return super.onSingleTapConfirmed(e) + } + + val centerLine = centerLine + val centerLineTime = lyricEntryList[centerLine].time + // onPlayClick 消费了才更新 UI + if (onPlayClickListener?.onPlayClick(centerLineTime) == true) { + isShowTimeline.value = false + removeCallbacks(hideTimelineRunnable) + smoothScrollTo(centerLine) + invalidate() + return true + } + return super.onSingleTapConfirmed(e) + } + } + + private val hideTimelineRunnable = Runnable { + if (hasLrc() && isShowTimeline.value) { + isShowTimeline.value = false + smoothScrollTo(currentLine) + } + } + + override fun computeScroll() { + if (scroller!!.computeScrollOffset()) { + mViewPortOffset = scroller!!.currY.toFloat() + invalidate() + } + if (isFling && scroller!!.isFinished) { + isFling = false + if (hasLrc() && !isTouching) { + adjustCenter() + postDelayed(hideTimelineRunnable, TIMELINE_KEEP_TIME) + } + } + } + + override fun onDetachedFromWindow() { + removeCallbacks(hideTimelineRunnable) + super.onDetachedFromWindow() + } + + private fun onLrcLoaded(entryList: List?) { + if (!entryList.isNullOrEmpty()) { + lyricEntryList.addAll(entryList) + } + lyricEntryList.sort() + initEntryList() + invalidate() + } + + private fun initPlayDrawable() { + val l = (timeTextWidth - drawableWidth) / 2 + val t = startOffset.toInt() - drawableWidth / 2 + val r = l + drawableWidth + val b = t + drawableWidth + playDrawable!!.setBounds(l, t, r, b) + } + + private fun initEntryList() { + if (!hasLrc() || width == 0) { + return + } + /** + * StaticLayout 根据初始化时传入的 TextSize 计算换行的位置 + * 如果 [currentTextSize] 与 [normalTextSize] 相差较大, + * 则会导致歌词渲染时溢出边界,或行间距不足挤压在一起 + * + * 故计算出可能的最大 TextSize 以后,用其初始化,使 StaticLayout 拥有足够的高度 + */ + lyricPaint.textSize = max(currentTextSize, normalTextSize) + secondLyricPaint.textSize = lyricPaint.textSize * translateTextScaleValue + for (lrcEntry in lyricEntryList) { + lrcEntry.init( + lyricPaint, secondLyricPaint, + lrcWidth.toInt(), textGravity.toLayoutAlign() + ) + } + mCurrentOffset = startOffset + mViewPortOffset = startOffset + } + + private fun reset() { + // TODO 待完善reset的逻辑 + scroller!!.forceFinished(true) + isShowTimeline.value = false + isTouching = false + isFling = false + removeCallbacks(hideTimelineRunnable) + lyricEntryList.clear() + mCurrentOffset = 0f + mViewPortOffset = 0f + currentLine = 0 + invalidate() + } + + /** + * 将中心行微调至正中心 + */ + private fun adjustCenter() { + smoothScrollTo(currentLine) + } + + /** + * 平滑滚动过渡到某一行 + * + * @param line 行号 + */ + private fun smoothScrollTo(line: Int) { + val offset = getOffset(line) + animateStartOffset = mCurrentOffset + animateTargetOffset = offset + progressSpringAnimator.animateToFinalPosition(offset) + } + + /** + * 二分法查找当前时间应该显示的行数(最后一个 <= time 的行数) + */ + private fun findShowLine(time: Long): Int { + var left = 0 + var right = lyricEntryList.size + while (left <= right) { + val middle = (left + right) / 2 + val middleTime = lyricEntryList[middle].time + if (time < middleTime) { + right = middle - 1 + } else { + if (middle + 1 >= lyricEntryList.size || time < lyricEntryList[middle + 1].time) { + return middle + } + left = middle + 1 + } + } + return 0 + } + + /** + * 计算单个歌词元素的偏移量,用于控制歌词对其中线的位置 + * + * 计算出来的歌词高度包含了分割线的高度,所以需要减去分割线的高度 + * + * @param itemHeight 歌词元素的高度 + * @param dividerHeight 分割线的高度 + * + * @return 歌词元素的偏移量 + */ + protected open fun calcOffsetOfItem(itemHeight: Float, dividerHeight: Float): Float { + return (itemHeight - dividerHeight) * itemOffsetPercent + } + + /** + * 因为添加了 [translateDividerHeight] 用来间隔开歌词与翻译, + * 所以直接从 [LyricEntry] 获取高度不可行, + * 故使用该 [getLyricHeight] 方法来计算 [LyricEntry] 的高度 + */ + @Deprecated("不再单独计算歌词的高度,在绘制时计算并进行更新缓存,所见即所得") + open fun getLyricHeight(line: Int): Int { + var height = lyricEntryList[line].staticLayout?.height ?: return 0 + lyricEntryList[line].secondStaticLayout?.height?.let { + height += (it + translateDividerHeight).toInt() + } + return height + } + + /** + * 获取歌词距离视图顶部的距离 + */ + private fun getOffset(line: Int): Float { + return startOffset - (offsetKeeper[line] ?: 0f) + } + + private fun getMinOffset(line: Int): Float { + return startOffset - (minOffsetKeeper[line] ?: 0f) + } + + private fun getMaxOffset(line: Int): Float { + return startOffset - (maxOffsetKeeper[line] ?: 0f) + } + + /** + * 在主线程中运行 + */ + private fun runOnMain(r: Runnable) { + if (Looper.myLooper() == Looper.getMainLooper()) { + r.run() + } else { + post(r) + } + } + + /** + * 以下是公共部分 + * 用法见接口 [LyricViewXInterface] + */ + + override fun setSentenceDividerHeight(height: Float) { + sentenceDividerHeight = height + if (hasLrc()) { + smoothScrollTo(currentLine) + } + postInvalidate() + } + + override fun setTranslateDividerHeight(height: Float) { + translateDividerHeight = height + if (hasLrc()) { + smoothScrollTo(currentLine) + } + postInvalidate() + } + + override fun setHorizontalOffset(offset: Float) { + horizontalOffset = offset + initPlayDrawable() + postInvalidate() + } + + override fun setHorizontalOffsetPercent(percent: Float) { + horizontalOffsetPercent = percent + initPlayDrawable() + postInvalidate() + } + + override fun setTranslateTextScaleValue(scaleValue: Float) { + translateTextScaleValue = scaleValue + initEntryList() + if (hasLrc()) { + smoothScrollTo(currentLine) + } + } + + override fun setTextGravity(gravity: Int) { + textGravity = gravity + initEntryList() + if (hasLrc()) { + smoothScrollTo(currentLine) + } + } + + override fun setNormalColor(normalColor: Int) { + normalTextColor = normalColor + postInvalidate() + } + + override fun setNormalTextSize(size: Float) { + normalTextSize = size + initEntryList() + if (hasLrc()) { + smoothScrollTo(currentLine) + } + } + + override fun setCurrentTextSize(size: Float) { + currentTextSize = size + initEntryList() + if (hasLrc()) { + smoothScrollTo(currentLine) + } + } + + override fun setCurrentColor(currentColor: Int) { + currentTextColor = currentColor + postInvalidate() + } + + override fun setTimelineTextColor(timelineTextColor: Int) { + this.timelineTextColor = timelineTextColor + postInvalidate() + } + + override fun setTimelineColor(timelineColor: Int) { + this.timelineColor = timelineColor + postInvalidate() + } + + override fun setTimeTextColor(timeTextColor: Int) { + this.timeTextColor = timeTextColor + postInvalidate() + } + + override fun setLabel(label: String) { + runOnMain { + defaultLabel = label + this@LyricViewX.invalidate() + } + } + + override fun loadLyric(mainLyricText: String?, secondLyricText: String?) { + runOnMain { + reset() + val sb = StringBuilder("file://") + sb.append(mainLyricText) + if (secondLyricText != null) { + sb.append("#").append(secondLyricText) + } + val flag = sb.toString() + this@LyricViewX.flag = flag + thread { + val lrcEntries = LyricUtil.parseLrc(arrayOf(mainLyricText, secondLyricText)) + runOnMain { + onLrcLoaded(lrcEntries) + this@LyricViewX.flag = null + } + } + } + } + + override fun loadLyric(lyricEntries: List) { + runOnMain { + reset() + onLrcLoaded(lyricEntries) + } + } + + override fun updateTime(time: Long, force: Boolean) { + // 将方法的执行延后至 View 创建完成后执行 + readyHelper.whenReady { + if (!it) return@whenReady + if (hasLrc()) { + val line = findShowLine(time) + if (line != currentLine) { + runOnMain { + currentLine = line + smoothScrollTo(line) + } + } + } + } + } + + override fun setDraggable(draggable: Boolean, onPlayClickListener: OnPlayClickListener?) { + this.onPlayClickListener = if (draggable) { + requireNotNull(onPlayClickListener) { + "if draggable == true, onPlayClickListener must not be null" } + onPlayClickListener + } else { + null + } + } + + override fun setOnSingerClickListener(onSingerClickListener: OnSingleClickListener?) { + this.onSingerClickListener = onSingerClickListener + } + + override fun getLyricEntryList(): List { + return lyricEntryList.toList() + } + + override fun setLyricEntryList(newList: List) { + reset() + onLrcLoaded(newList) + this@LyricViewX.flag = null + } + + override fun getCurrentLineLyricEntry(): LyricEntry? { + if (currentLine <= lyricEntryList.lastIndex) { + return lyricEntryList[currentLine] + } + return null + } + + override fun setLyricTypeface(file: File) { + val typeface = file.takeIf { it.exists() } + ?.runCatching { Typeface.createFromFile(this) } + ?.getOrNull() ?: return + + setLyricTypeface(typeface) + } + + override fun setLyricTypeface(path: String) { + setLyricTypeface(File(path)) + } + + override fun setLyricTypeface(typeface: Typeface?) { + lyricPaint.typeface = typeface + secondLyricPaint.typeface = typeface + postInvalidate() + } + + override fun setDampingRatioForLyric(dampingRatio: Float) { + dampingRatioForLyric = dampingRatio + progressSpringAnimator.spring.dampingRatio = dampingRatio + } + + override fun setDampingRatioForViewPort(dampingRatio: Float) { + dampingRatioForViewPort = dampingRatio + viewPortSpringAnimator.spring.dampingRatio = dampingRatio + } + + override fun setStiffnessForLyric(stiffness: Float) { + stiffnessForLyric = stiffness + progressSpringAnimator.spring.stiffness = stiffness + } + + override fun setStiffnessForViewPort(stiffness: Float) { + stiffnessForViewPort = stiffness + viewPortSpringAnimator.spring.stiffness = stiffness + } + + override fun setPlayDrawable(drawable: Drawable) { + playDrawable = drawable + } + + override fun setIsDrawTranslation(isDrawTranslation: Boolean) { + this.isDrawTranslation = isDrawTranslation + postInvalidate() + } + + override fun setIsEnableBlurEffect(isEnableBlurEffect: Boolean) { + this.isEnableBlurEffect.value = isEnableBlurEffect + postInvalidate() + } + + override fun setItemOffsetPercent(itemOffsetPercent: Float) { + this.itemOffsetPercent = itemOffsetPercent + postInvalidate() + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/dirror/lyricviewx/ReadyHelper.kt b/app/src/main/java/com/dirror/lyricviewx/ReadyHelper.kt new file mode 100644 index 0000000..9b81331 --- /dev/null +++ b/app/src/main/java/com/dirror/lyricviewx/ReadyHelper.kt @@ -0,0 +1,51 @@ +package com.dirror.lyricviewx + +import androidx.annotation.IntDef + +const val STATE_CREATED = 1 +const val STATE_INITIALIZING = 2 +const val STATE_INITIALIZED = 3 +const val STATE_ERROR = 4 + +@IntDef( + STATE_CREATED, + STATE_INITIALIZING, + STATE_INITIALIZED, + STATE_ERROR +) +@Retention(AnnotationRetention.SOURCE) +annotation class ReadyState + +/** + * 简单的状态机,根据 [readyState] 的状态决定当前任务的执行或延后与否 + */ +open class ReadyHelper { + private var readyCallback: (Boolean) -> Unit = {} + + @ReadyState + var readyState: Int = STATE_CREATED + set(value) { + if (field == value) return + when (value) { + STATE_INITIALIZED, + STATE_ERROR -> synchronized(readyCallback) { + field = value + readyCallback.invoke(value != STATE_ERROR) + } + else -> field = value + } + } + + fun whenReady(performAction: (Boolean) -> Unit): Boolean { + return when (readyState) { + STATE_CREATED, STATE_INITIALIZING -> { + readyCallback = performAction + false + } + else -> { + performAction(readyState != STATE_ERROR) + true + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/dirror/lyricviewx/extension/BlurMaskFilterExt.kt b/app/src/main/java/com/dirror/lyricviewx/extension/BlurMaskFilterExt.kt new file mode 100644 index 0000000..f5a8558 --- /dev/null +++ b/app/src/main/java/com/dirror/lyricviewx/extension/BlurMaskFilterExt.kt @@ -0,0 +1,15 @@ +package com.dirror.lyricviewx.extension + +import android.graphics.BlurMaskFilter +import android.util.SparseArray + +class BlurMaskFilterExt { + private val maskFilterCache = SparseArray() + + fun get(radius: Int): BlurMaskFilter? { + if (radius == 0 || radius > 25) return null + + return maskFilterCache[radius] ?: BlurMaskFilter(radius.toFloat(), BlurMaskFilter.Blur.NORMAL) + .also { maskFilterCache.put(radius, it) } + } +} \ No newline at end of file diff --git a/app/src/main/res/mipmap-hdpi/icon.png b/app/src/main/res/mipmap-hdpi/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..22103ca0c13b0c9ce1d0f0938da09435d882654c GIT binary patch literal 107974 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 GgZ~37gWksg literal 0 HcmV?d00001 diff --git a/lyricviewx/.gitignore b/lyricviewx/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/lyricviewx/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/lyricviewx/build.gradle b/lyricviewx/build.gradle new file mode 100644 index 0000000..679ac30 --- /dev/null +++ b/lyricviewx/build.gradle @@ -0,0 +1,31 @@ +plugins { + id 'com.android.library' +} +android { + compileSdkVersion 32 + namespace 'com.jaeger.library' + defaultConfig { + minSdk 23 + //noinspection ExpiredTargetSdkVersion,OldTargetApi + targetSdk 31 + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } +} + + +dependencies { +// implementation "org.jetbrains.kotlin:kotlin-stdlib:1.8.0" + implementation 'androidx.core:core-ktx:1.7.0' + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation 'com.google.android.material:material:1.2.1' +} \ No newline at end of file