Skip to content

Commit

Permalink
重构歌词相关代码:驯服 ScrollView
Browse files Browse the repository at this point in the history
  • Loading branch information
Henry-ZHR committed Nov 18, 2024
1 parent e514cfc commit f5b6b7d
Show file tree
Hide file tree
Showing 21 changed files with 156 additions and 129 deletions.
30 changes: 30 additions & 0 deletions app/src/main/java/remix/myplayer/helper/LyricsHelper.kt
Original file line number Diff line number Diff line change
@@ -1,9 +1,39 @@
package remix.myplayer.helper

import android.content.Context
import remix.myplayer.R
import remix.myplayer.lyrics.LyricsLine
import remix.myplayer.theme.Theme
import remix.myplayer.ui.widget.desktop.DesktopLyricsView
import remix.myplayer.util.SPUtil

object LyricsHelper {
fun showLocalLyricsTip(context: Context, action: () -> Unit) {
if (!SPUtil.getValue(
context,
SPUtil.LYRICS_KEY.NAME,
SPUtil.LYRICS_KEY.LOCAL_LYRICS_TIP_SHOWN,
false
)
) {
Theme.getBaseDialog(context)
.positiveText(R.string.confirm)
.onPositive { _, _ ->
SPUtil.putValue(
context,
SPUtil.LYRICS_KEY.NAME,
SPUtil.LYRICS_KEY.LOCAL_LYRICS_TIP_SHOWN,
true
)
action.invoke()
}
.content(R.string.local_lyrics_tip)
.show()
} else {
action.invoke()
}
}

fun getDesktopLyricsContent(
lyrics: List<LyricsLine>, offset: Int, progress: Int, duration: Int
): DesktopLyricsView.Content {
Expand Down
1 change: 0 additions & 1 deletion app/src/main/java/remix/myplayer/lyrics/LrcParser.kt
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,6 @@ object LrcParser {
// [xxx]
if (it.endsWith(']')) {
val tag = it.substring(1, it.lastIndex)
println(tag)
// [offset:+/-xxx]
if (tag.startsWith("offset:")) {
try {
Expand Down
4 changes: 3 additions & 1 deletion app/src/main/java/remix/myplayer/lyrics/LyricsLine.kt
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package remix.myplayer.lyrics

import kotlinx.serialization.Serializable
import remix.myplayer.App
import remix.myplayer.R

abstract class LyricsLine {
@Serializable
sealed class LyricsLine {
/**
* 这行歌词的开始时间
*/
Expand Down
6 changes: 5 additions & 1 deletion app/src/main/java/remix/myplayer/lyrics/LyricsSearcher.kt
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,11 @@ object LyricsSearcher {
when (song) {
is Song.Local -> "local"
is Song.Remote -> "remote"
}, if (song is Song.Local) song.id else song.data, song.title, song.artist, song.album
},
if (song is Song.Local) song.id.toString() else song.data,
song.title,
song.artist,
song.album
)
)
// 要作为文件名,安全起见保证输出长度不超过 127 字节,SHA-384 输出 96 字节
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,14 @@ import remix.myplayer.bean.mp3.Song
import remix.myplayer.db.room.DatabaseRepository
import remix.myplayer.helper.DeleteHelper
import remix.myplayer.helper.EQHelper
import remix.myplayer.helper.LyricsHelper
import remix.myplayer.helper.MusicServiceRemote
import remix.myplayer.helper.MusicServiceRemote.getCurrentSong
import remix.myplayer.lyrics.provider.EmbeddedProvider
import remix.myplayer.lyrics.provider.IgnoredProvider
import remix.myplayer.lyrics.provider.StubProvider
import remix.myplayer.service.Command
import remix.myplayer.theme.Theme.getBaseDialog
import remix.myplayer.ui.ViewCommon
import remix.myplayer.ui.activity.PlayerActivity
import remix.myplayer.ui.dialog.AddtoPlayListDialog
import remix.myplayer.ui.dialog.TimerDialog
Expand All @@ -46,7 +46,7 @@ class AudioPopupListener(activity: PlayerActivity, private val song: Song) :
val activity = ref.get() ?: return true
when (item.itemId) {
R.id.menu_lyric -> {
ViewCommon.showLocalLyricTip(activity) {
LyricsHelper.showLocalLyricsTip(activity) {
onClickLyric(activity)
}
return true
Expand Down Expand Up @@ -169,9 +169,10 @@ class AudioPopupListener(activity: PlayerActivity, private val song: Song) :

7 -> MusicServiceRemote.service?.updateLyrics(IgnoredProvider) // 忽略
8 -> TODO() // 调整字体大小
9 -> activity.showLyricOffsetView()// 调整时间轴
9 -> activity.showLyricOffsetView() // 调整时间轴
}
}.show()
// TODO: update LyricsFragment
}

companion object {
Expand Down
31 changes: 0 additions & 31 deletions app/src/main/java/remix/myplayer/ui/ViewCommon.kt

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.SeekBar
import androidx.activity.result.contract.ActivityResultContracts
import androidx.annotation.RequiresApi
import androidx.appcompat.widget.PopupMenu
import androidx.fragment.app.FragmentManager
import androidx.palette.graphics.Palette
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ import remix.myplayer.glide.UriFetcher.DOWNLOAD_LASTFM
import remix.myplayer.helper.EQHelper
import remix.myplayer.helper.LanguageHelper
import remix.myplayer.helper.LanguageHelper.AUTO
import remix.myplayer.helper.LyricsHelper
import remix.myplayer.helper.M3UHelper.exportPlayListToFile
import remix.myplayer.helper.M3UHelper.importLocalPlayList
import remix.myplayer.helper.M3UHelper.importM3UFile
Expand All @@ -70,7 +71,6 @@ import remix.myplayer.theme.Theme
import remix.myplayer.theme.Theme.getBaseDialog
import remix.myplayer.theme.ThemeStore
import remix.myplayer.theme.TintHelper
import remix.myplayer.ui.ViewCommon
import remix.myplayer.ui.activity.MainActivity.Companion.EXTRA_LIBRARY
import remix.myplayer.ui.activity.MainActivity.Companion.EXTRA_RECREATE
import remix.myplayer.ui.activity.MainActivity.Companion.EXTRA_REFRESH_ADAPTER
Expand Down Expand Up @@ -900,7 +900,7 @@ class SettingActivity : ToolbarActivity(), ColorChooserDialog.ColorCallback,
* 歌词搜索优先级
*/
private fun configLyricPriority() {
ViewCommon.showLocalLyricTip(this) {
LyricsHelper.showLocalLyricsTip(this) {
LyricsOrderDialog().show(supportFragmentManager, "configLyricPriority")
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,13 +118,13 @@ class LyricsFragment : BaseMusicFragment<FragmentLrcBinding>(), View.OnClickList
override fun onMediaStoreChanged() {
super.onMediaStoreChanged()
updateLyrics()
TODO("when is it called?")
// TODO("when is it called?")
}

override fun onMetaChanged() {
super.onMetaChanged()
updateLyrics()
TODO("when is it called?")
// TODO("when is it called?")
}

@UiThread
Expand Down
99 changes: 65 additions & 34 deletions app/src/main/java/remix/myplayer/ui/widget/LyricsView.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package remix.myplayer.ui.widget

import android.annotation.SuppressLint
import android.content.Context
import android.os.Build
import android.util.AttributeSet
import android.view.LayoutInflater
import android.view.MotionEvent
Expand All @@ -11,19 +12,24 @@ import android.widget.LinearLayout
import android.widget.TextView
import androidx.annotation.ColorInt
import androidx.annotation.UiThread
import remix.myplayer.R
import androidx.core.view.updatePadding
import remix.myplayer.databinding.LayoutLyricsLineBinding
import remix.myplayer.databinding.LayoutLyricsViewBinding
import remix.myplayer.lyrics.LyricsLine
import remix.myplayer.lyrics.PerWordLyricsLine
import remix.myplayer.theme.ThemeStore
import timber.log.Timber
import kotlin.math.roundToInt
import kotlin.time.Duration.Companion.milliseconds

class LyricsView @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null
) : FrameLayout(context, attrs), View.OnTouchListener {
companion object {
private const val TAG = "LyricsView"

private val DEACTIVATE_DELAY = 5000.milliseconds
private val AUTO_SCROLL_DELAY = 200.milliseconds

private val normalTextColor
@ColorInt get() = ThemeStore.textColorSecondary
Expand All @@ -41,40 +47,29 @@ class LyricsView @JvmOverloads constructor(

override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
super.onSizeChanged(w, h, oldw, oldh)
Timber.tag(TAG).v("onSizeChanged, h=$h")
if (h != oldh) {
// 给 container 上下加空白,确保第一行和最后一行歌词可以滚动到 view 中间
binding.innerContainer.setPadding(0, h / 2, 0, h / 2)
val padding = (h + 1) / 2
handler.post {
binding.innerContainer.setPadding(0, padding, 0, padding)
}
}
}

private fun newLayoutForLine(line: LyricsLine): LinearLayout {
val params = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)
val padding = resources.getDimensionPixelSize(R.dimen.lyrics_view_lrc_block_vertical_padding)
val textColor = normalTextColor

val layout = LinearLayout(context)
layout.layoutParams = params
layout.setPadding(0, padding, 0, padding)
layout.orientation = LinearLayout.VERTICAL
private fun addLayoutForLine(line: LyricsLine) {
val layout =
LayoutLyricsLineBinding.inflate(LayoutInflater.from(context), binding.innerContainer, true)
if (line.content.isNotBlank()) {
val view = TextView(context)
view.layoutParams = params
view.text = if (line is PerWordLyricsLine) {
line.getSpannedString(0f, textColor)
layout.content.text = if (line is PerWordLyricsLine) {
line.getSpannedString(0f, normalTextColor)
} else {
line.content
}
view.setTextColor(textColor)
layout.addView(view)
}
if (line.translation?.isNotBlank() == true) {
val view = TextView(context)
view.layoutParams = params
view.text = line.translation
view.setTextColor(textColor)
layout.addView(view)
if (!line.translation.isNullOrBlank()) {
layout.translation.text = line.translation
}
return layout
}

/**
Expand All @@ -89,7 +84,7 @@ class LyricsView @JvmOverloads constructor(
binding.innerContainer.removeAllViews()
isClickable = lyrics.isNotEmpty()
value.forEach {
binding.innerContainer.addView(newLayoutForLine(it))
addLayoutForLine(it)
}
rawProgressAndDuration = null
lastHighlightLine = null
Expand All @@ -107,7 +102,7 @@ class LyricsView @JvmOverloads constructor(
}
field = value
if (isActive) {
showTimeIndicator()
updateTimeIndicator()
}
rawProgressAndDuration?.run {
updateProgress(first, second)
Expand Down Expand Up @@ -201,7 +196,8 @@ class LyricsView @JvmOverloads constructor(
set(value) {
field = value
if (value) {
showTimeIndicator()
updateTimeIndicator()
binding.timeIndicator.visibility = View.VISIBLE
handler.removeCallbacks(deactivateRunnable)
handler.postDelayed(deactivateRunnable, DEACTIVATE_DELAY.inWholeMilliseconds)
} else {
Expand All @@ -215,34 +211,69 @@ class LyricsView @JvmOverloads constructor(
private val deactivateRunnable = Runnable {
isActive = false
}
private val scrollToNearestLineRunnable = Runnable {
scrollToLine(getNearestLine())
}

@SuppressLint("SetTextI18n")
private fun showTimeIndicator() {
private fun updateTimeIndicator() {
(lyrics[getNearestLine()].time - offset).coerceAtLeast(0).let {
binding.time.text =
"%02d:%02d.%02d".format(it / 1000 / 60, it / 1000 % 60, (it % 1000 / 10f).roundToInt())
// TODO: don't set every time
binding.playButton.setOnClickListener { _ ->
onSeekToListener?.onSeekTo(it)
}
}
binding.timeIndicator.visibility = View.VISIBLE
}

private var isTouching: Boolean = false

override fun onTouch(v: View, event: MotionEvent): Boolean {
check(v == binding.outerContainer)

isActive = true

when (event.action) {
MotionEvent.ACTION_DOWN -> {
isTouching = true
handler.removeCallbacks(scrollToNearestLineRunnable)
}

MotionEvent.ACTION_UP -> {
isTouching = false
handler.postDelayed(scrollToNearestLineRunnable, AUTO_SCROLL_DELAY.inWholeMilliseconds)
}
}

return false
}

private fun onScrollChange() {
updateTimeIndicator()

handler.removeCallbacks(scrollToNearestLineRunnable)
if (!isTouching) {
handler.postDelayed(scrollToNearestLineRunnable, AUTO_SCROLL_DELAY.inWholeMilliseconds)
}
}

// 在单独函数以忽略警告
@SuppressLint("ClickableViewAccessibility")
private fun setupOnTouchListener() {
private fun init() {
binding.outerContainer.setOnTouchListener(this)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
binding.outerContainer.setOnScrollChangeListener { _, _, _, _, _ ->
onScrollChange()
}
} else {
binding.outerContainer.viewTreeObserver.addOnScrollChangedListener {
onScrollChange()
}
}
}

init {
binding.outerContainer.onFlingEndListener = ResponsiveScrollView.OnFlingEndListener {
scrollToLine(getNearestLine())
}
setupOnTouchListener()
init()
}
}
Loading

0 comments on commit f5b6b7d

Please sign in to comment.