增加书籍详情界面与有声书界面的根据封面生成主题色功能

This commit is contained in:
HapeLee
2025-09-06 04:23:06 +08:00
parent 090b380d2e
commit f0b2d6cf21
6 changed files with 813 additions and 401 deletions
@@ -1,8 +1,15 @@
package io.legato.kazusa.ui.book.audio
import android.animation.ValueAnimator
import android.annotation.SuppressLint
import android.content.Context
import android.content.res.ColorStateList
import android.graphics.Color
import android.graphics.RenderEffect
import android.graphics.Shader
import android.graphics.drawable.BitmapDrawable
import android.graphics.drawable.Drawable
import android.graphics.drawable.TransitionDrawable
import android.icu.text.SimpleDateFormat
import android.os.Build
import android.os.Bundle
@@ -11,10 +18,15 @@ import android.view.Menu
import android.view.MenuItem
import androidx.activity.viewModels
import androidx.appcompat.content.res.AppCompatResources
import androidx.core.graphics.scale
import androidx.core.view.isVisible
import androidx.lifecycle.lifecycleScope
import androidx.transition.TransitionManager
import com.google.android.material.color.DynamicColors
import com.google.android.material.color.DynamicColorsOptions
import com.google.android.material.color.MaterialColors
import com.google.android.material.slider.Slider
import com.google.android.material.transition.platform.MaterialContainerTransform
import io.legato.kazusa.R
import io.legato.kazusa.base.VMBaseActivity
import io.legato.kazusa.constant.BookType
@@ -50,7 +62,10 @@ import io.legato.kazusa.utils.startActivityForBook
import io.legato.kazusa.utils.startAnimation
import io.legato.kazusa.utils.viewbindingdelegate.viewBinding
import io.legato.kazusa.utils.visible
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import splitties.views.onLongClick
@@ -70,6 +85,13 @@ class AudioPlayActivity :
private var adjustProgress = false
private var playMode = AudioPlay.PlayMode.LIST_END_STOP
private var playSpeed = 1f
private var primaryFinalColor: Int = 0
private var surfaceFinalColor: Int = 0
private var secondaryFinalColor: Int = 0
private var onSurfaceFinalColor: Int = 0
private var secondaryContainerFinalColor: Int = 0
private var currentJob: Job? = null
private var wrappedContext: Context? = null
private val progressTimeFormat by lazy {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
@@ -95,7 +117,27 @@ class AudioPlayActivity :
}
override fun onCreate(savedInstanceState: Bundle?) {
val transform = MaterialContainerTransform().apply {
addTarget(binding.root)
scrimColor = Color.TRANSPARENT
}
window.sharedElementEnterTransition = transform
window.sharedElementReturnTransition = transform
super.onCreate(savedInstanceState)
binding.root.transitionName = intent.getStringExtra("transitionName")
primaryFinalColor =
MaterialColors.getColor(this, androidx.appcompat.R.attr.colorPrimary, -1)
surfaceFinalColor =
MaterialColors.getColor(this, com.google.android.material.R.attr.colorSurface, -1)
secondaryFinalColor =
MaterialColors.getColor(this, com.google.android.material.R.attr.colorSecondary, -1)
onSurfaceFinalColor =
MaterialColors.getColor(this, com.google.android.material.R.attr.colorOnSurface, -1)
secondaryContainerFinalColor = MaterialColors.getColor(
this,
com.google.android.material.R.attr.colorSecondaryContainer,
-1
)
binding.titleBar.setBackgroundResource(R.color.transparent)
binding.titleBar.title = ""
AudioPlay.register(this)
@@ -318,25 +360,29 @@ class AudioPlayActivity :
}
private fun upCover(path: String?) {
BookCover.load(this, path, sourceOrigin = AudioPlay.bookSource?.bookSourceUrl) {
if (!AppConfig.isEInkMode) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
BookCover.load(this, path, sourceOrigin = AudioPlay.bookSource?.bookSourceUrl)
.into(binding.ivBg)
val blurEffect =
RenderEffect.createBlurEffect(120f, 120f, Shader.TileMode.CLAMP)
binding.ivBg.setRenderEffect(blurEffect)
} else {
// 低版本直接加载模糊图
BookCover.loadBlur(
this,
path,
sourceOrigin = AudioPlay.bookSource?.bookSourceUrl
)
.into(binding.ivBg)
BookCover.load(
this, path, sourceOrigin = AudioPlay.bookSource?.bookSourceUrl,
onLoadFinish = {
binding.ivCover.post {
val drawable = binding.ivCover.drawable
if (drawable != null) {
addColorScheme(drawable)
}
}
}).into(binding.ivCover)
if (!AppConfig.isEInkMode) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
BookCover.load(this, path, sourceOrigin = AudioPlay.bookSource?.bookSourceUrl)
.into(binding.ivBg)
val blurEffect = RenderEffect.createBlurEffect(120f, 120f, Shader.TileMode.CLAMP)
binding.ivBg.setRenderEffect(blurEffect)
} else {
// 低版本直接加载模糊图
BookCover.loadBlur(this, path, sourceOrigin = AudioPlay.bookSource?.bookSourceUrl)
.into(binding.ivBg)
}
}.into(binding.ivCover)
}
addColorScheme(binding.ivCover.drawable)
}
private fun playButton() {
@@ -347,6 +393,145 @@ class AudioPlayActivity :
}
}
private fun addColorScheme(drawable: Drawable?) {
currentJob?.cancel()
currentJob = CoroutineScope(Dispatchers.Default).launch {
val bitmap = when (drawable) {
is BitmapDrawable -> drawable.bitmap
is TransitionDrawable -> (drawable.getDrawable(1) as? BitmapDrawable)?.bitmap
else -> null
} ?: return@launch
val colorAccuracy = true
val targetWidth = if (colorAccuracy) (bitmap.width / 4).coerceAtMost(256) else 16
val targetHeight = if (colorAccuracy) (bitmap.height / 4).coerceAtMost(256) else 16
val scaledBitmap = bitmap.scale(targetWidth, targetHeight, false)
val options = DynamicColorsOptions.Builder()
.setContentBasedSource(scaledBitmap)
.build()
wrappedContext = DynamicColors.wrapContextIfAvailable(
this@AudioPlayActivity,
options
).apply {
resources.configuration.uiMode =
this@AudioPlayActivity.resources.configuration.uiMode
}
withContext(Dispatchers.Main) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
applyColorScheme()
}
}
}
}
private suspend fun applyColorScheme() {
val ctx = wrappedContext ?: this
val colorPrimary = MaterialColors.getColor(ctx, androidx.appcompat.R.attr.colorPrimary, -1)
val colorSecondary =
MaterialColors.getColor(ctx, com.google.android.material.R.attr.colorSecondary, -1)
val colorOnSurface =
MaterialColors.getColor(ctx, com.google.android.material.R.attr.colorOnSurface, -1)
val colorSurface =
MaterialColors.getColor(ctx, com.google.android.material.R.attr.colorSurface, -1)
val colorSurfaceContainer = MaterialColors.getColor(
ctx,
com.google.android.material.R.attr.colorSurfaceContainer,
-1
)
val colorSecondaryContainer = MaterialColors.getColor(
ctx,
com.google.android.material.R.attr.colorSecondaryContainer,
-1
)
val primaryTransition = ValueAnimator.ofArgb(primaryFinalColor, colorPrimary).apply {
duration = 400L
addUpdateListener { animation ->
val color = animation.animatedValue as Int
binding.settingSlider.thumbTintList = ColorStateList.valueOf(color)
binding.settingSlider.trackActiveTintList = ColorStateList.valueOf(color)
binding.playerProgress.thumbTintList = ColorStateList.valueOf(color)
binding.playerProgress.trackActiveTintList = ColorStateList.valueOf(color)
}
}
val secondaryContainerTransition =
ValueAnimator.ofArgb(secondaryContainerFinalColor, colorSecondaryContainer).apply {
duration = 400L
addUpdateListener { animation ->
val color = animation.animatedValue as Int
binding.fabPlayStop.backgroundTintList = ColorStateList.valueOf(color)
binding.btnReset.backgroundTintList = ColorStateList.valueOf(color)
binding.settingSlider.trackInactiveTintList = ColorStateList.valueOf(color)
binding.playerProgress.trackInactiveTintList = ColorStateList.valueOf(color)
}
}
val surfaceTransition =
ValueAnimator.ofArgb(surfaceFinalColor, colorSurfaceContainer).apply {
duration = 400L
addUpdateListener { animation ->
val color = animation.animatedValue as Int
binding.vwBg2.setBackgroundColor(color)
binding.cdTimer.setBackgroundColor(color)
binding.cdSpeed.setBackgroundColor(color)
}
}
val textTransition = ValueAnimator.ofArgb(onSurfaceFinalColor, colorOnSurface).apply {
duration = 400L
addUpdateListener { animation ->
val color = animation.animatedValue as Int
binding.tvSpeed.setTextColor(color)
binding.tvTimer.setTextColor(color)
binding.tvTitle.setTextColor(color)
binding.tvDurTime.setTextColor(color)
binding.tvAllTime.setTextColor(color)
binding.tvSubTitle.setTextColor(color)
}
}
withContext(Dispatchers.Main) {
surfaceTransition.start()
textTransition.start()
primaryTransition.start()
secondaryContainerTransition.start()
}
surfaceFinalColor = colorSurface
secondaryFinalColor = colorSecondary
onSurfaceFinalColor = colorOnSurface
primaryFinalColor = colorPrimary
secondaryContainerFinalColor = colorSecondaryContainer
binding.progressLoading.setIndicatorColor(colorPrimary)
val states = arrayOf(
intArrayOf(android.R.attr.state_enabled, android.R.attr.state_checked),
intArrayOf(android.R.attr.state_enabled, -android.R.attr.state_checked),
intArrayOf(-android.R.attr.state_enabled)
)
val colors = intArrayOf(
colorSurface,
colorOnSurface,
colorOnSurface.copy(alpha = 0.3f)
)
val stateList = ColorStateList(states, colors)
listOf(
binding.ivSkipNext, binding.ivSkipPrevious, binding.ivPlayMode, binding.ivTimer,
binding.ivChapter, binding.ivFastForward, binding.btnReset, binding.fabPlayStop
).forEach { btn ->
btn.setTextColor(stateList)
btn.iconTint = stateList
}
}
override val oldBook: Book?
get() = AudioPlay.book
@@ -487,4 +672,10 @@ class AudioPlayActivity :
}
}
}
}
private fun Int.copy(alpha: Float): Int {
val alpha = (alpha * 255).toInt()
return (this and 0x00FFFFFF) or (alpha shl 24)
}
@@ -1,10 +1,17 @@
package io.legato.kazusa.ui.book.info
import android.animation.ValueAnimator
import android.annotation.SuppressLint
import android.content.Context
import android.content.Intent
import android.content.res.ColorStateList
import android.graphics.Color
import android.graphics.RenderEffect
import android.graphics.Shader
import android.graphics.drawable.BitmapDrawable
import android.graphics.drawable.Drawable
import android.graphics.drawable.GradientDrawable
import android.graphics.drawable.TransitionDrawable
import android.net.Uri
import android.os.Build
import android.os.Bundle
@@ -15,8 +22,12 @@ import android.widget.LinearLayout
import androidx.activity.result.contract.ActivityResultContracts
import androidx.activity.viewModels
import androidx.core.content.ContextCompat
import androidx.core.graphics.scale
import androidx.core.view.doOnPreDraw
import androidx.lifecycle.lifecycleScope
import com.google.android.material.color.DynamicColors
import com.google.android.material.color.DynamicColorsOptions
import com.google.android.material.color.MaterialColors
import com.google.android.material.transition.platform.MaterialContainerTransform
import io.legato.kazusa.R
import io.legato.kazusa.base.VMBaseActivity
@@ -75,7 +86,10 @@ import io.legato.kazusa.utils.startActivity
import io.legato.kazusa.utils.toastOnUi
import io.legato.kazusa.utils.viewbindingdelegate.viewBinding
import io.legato.kazusa.utils.visible
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
@@ -144,6 +158,13 @@ class BookInfoActivity :
viewModel.refreshBook(book)
}
}
private var surfaceFinalColor: Int = 0
private var secondaryFinalColor: Int = 0
private var onSurfaceFinalColor: Int = 0
private var secondaryContainerFinalColor: Int = 0
private var currentJob: Job? = null
private var wrappedContext: Context? = null
private var chapterChanged = false
private val waitDialog by lazy { WaitDialog(this) }
private var editMenuItem: MenuItem? = null
@@ -156,15 +177,26 @@ class BookInfoActivity :
override fun onCreate(savedInstanceState: Bundle?) {
postponeEnterTransition()
val transform = MaterialContainerTransform().apply {
addTarget(binding.ivCover)
addTarget(binding.cdCov)
scrimColor = Color.TRANSPARENT
}
window.sharedElementEnterTransition = transform
window.sharedElementReturnTransition = transform
window.sharedElementsUseOverlay = false
super.onCreate(savedInstanceState)
binding.ivCover.transitionName = intent.getStringExtra("transitionName")
binding.ivCover.doOnPreDraw {
surfaceFinalColor =
MaterialColors.getColor(this, com.google.android.material.R.attr.colorSurface, -1)
secondaryFinalColor =
MaterialColors.getColor(this, com.google.android.material.R.attr.colorSecondary, -1)
onSurfaceFinalColor =
MaterialColors.getColor(this, com.google.android.material.R.attr.colorOnSurface, -1)
secondaryContainerFinalColor = MaterialColors.getColor(
this,
com.google.android.material.R.attr.colorSecondaryContainer,
-1
)
binding.cdCov.transitionName = intent.getStringExtra("transitionName")
binding.cdCov.doOnPreDraw {
startPostponedEnterTransition()
}
setSupportActionBar(binding.topBar)
@@ -342,6 +374,7 @@ class BookInfoActivity :
private fun showBook(book: Book) = binding.run {
showCover(book)
addColorScheme(binding.ivCover.drawable)
tvName.text = book.name
tvAuthor.text = getString(R.string.author_show, book.getRealAuthor())
tvOrigin.text = getString(R.string.origin_show, book.originName)
@@ -377,7 +410,21 @@ class BookInfoActivity :
}
private fun showCover(book: Book) {
binding.ivCover.load(book.getDisplayCover(), book.name, book.author, false, book.origin) {
binding.ivCover.load(
book.getDisplayCover(),
book.name,
book.author,
false,
book.origin,
onLoadFinish = {
binding.ivCover.post {
val drawable = binding.ivCover.drawable
if (drawable != null) {
addColorScheme(drawable)
}
}
}
)
if (!AppConfig.isEInkMode) {
//高版本使用RenderEffect
BookCover.load(this, book.getDisplayCover(), false, book.origin)
@@ -392,9 +439,139 @@ class BookInfoActivity :
.into(binding.bgBook)
}
}
addColorScheme(binding.ivCover.drawable)
}
private fun addColorScheme(drawable: Drawable?) {
currentJob?.cancel()
currentJob = CoroutineScope(Dispatchers.Default).launch {
val bitmap = when (drawable) {
is BitmapDrawable -> drawable.bitmap
is TransitionDrawable -> (drawable.getDrawable(1) as? BitmapDrawable)?.bitmap
else -> null
} ?: return@launch
val colorAccuracy = true
val targetWidth = if (colorAccuracy) (bitmap.width / 4).coerceAtMost(256) else 16
val targetHeight = if (colorAccuracy) (bitmap.height / 4).coerceAtMost(256) else 16
val scaledBitmap = bitmap.scale(targetWidth, targetHeight, false)
val options = DynamicColorsOptions.Builder()
.setContentBasedSource(scaledBitmap)
.build()
wrappedContext = DynamicColors.wrapContextIfAvailable(
this@BookInfoActivity,
options
).apply {
resources.configuration.uiMode =
this@BookInfoActivity.resources.configuration.uiMode
}
withContext(Dispatchers.Main) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
applyColorScheme()
}
}
}
}
private suspend fun applyColorScheme() {
val ctx = wrappedContext ?: this
val colorPrimary = MaterialColors.getColor(ctx, androidx.appcompat.R.attr.colorPrimary, -1)
val colorSecondary =
MaterialColors.getColor(ctx, com.google.android.material.R.attr.colorSecondary, -1)
val colorTertiary =
MaterialColors.getColor(ctx, com.google.android.material.R.attr.colorTertiary, -1)
val colorOnSurface =
MaterialColors.getColor(ctx, com.google.android.material.R.attr.colorOnSurface, -1)
val colorSurface =
MaterialColors.getColor(ctx, com.google.android.material.R.attr.colorSurface, -1)
val colorSurfaceContainer = MaterialColors.getColor(
ctx,
com.google.android.material.R.attr.colorSurfaceContainer,
-1
)
val colorSecondaryContainer = MaterialColors.getColor(
ctx,
com.google.android.material.R.attr.colorSecondaryContainer,
-1
)
val surfaceTransition = ValueAnimator.ofArgb(surfaceFinalColor, colorSurface).apply {
duration = 400L
addUpdateListener { animation ->
val color = animation.animatedValue as Int
binding.cdInfo?.setCardBackgroundColor(color)
binding.llInfo.setBackgroundColor(color)
}
}
val buttonTransition = ValueAnimator.ofArgb(secondaryFinalColor, colorSecondary).apply {
duration = 400L
addUpdateListener { animation ->
val color = animation.animatedValue as Int
val tint = ColorStateList.valueOf(color)
listOf(
binding.btnShelf,
binding.tvTocView,
binding.tvChangeGroup,
binding.btnChangeSource
).forEach { btn ->
btn.setTextColor(color)
btn.iconTint = tint
}
}
}
val backTransition =
ValueAnimator.ofArgb(secondaryContainerFinalColor, colorSecondaryContainer).apply {
duration = 400L
addUpdateListener { animation ->
val color = animation.animatedValue as Int
binding.bgBookMask.setBackgroundColor(color)
}
}
val textTransition = ValueAnimator.ofArgb(onSurfaceFinalColor, colorOnSurface).apply {
duration = 400L
addUpdateListener { animation ->
val color = animation.animatedValue as Int
binding.tvName.setTextColor(color)
binding.tvAuthor.setTextColor(color)
binding.tvOrigin.setTextColor(color)
binding.tvDetail.setTextColor(color)
binding.ivName.imageTintList = ColorStateList.valueOf(color)
binding.ivWeb.imageTintList = ColorStateList.valueOf(color)
}
}
withContext(Dispatchers.Main) {
surfaceTransition.start()
buttonTransition.start()
textTransition.start()
backTransition.start()
}
surfaceFinalColor = colorSurface
secondaryFinalColor = colorSecondary
onSurfaceFinalColor = colorOnSurface
(binding.llCover.background as? GradientDrawable)?.colors =
intArrayOf(Color.TRANSPARENT, colorSurface)
binding.collTopBar.setContentScrimColor(colorSurfaceContainer)
binding.lbKind.applyColorScheme(colorSurfaceContainer, colorOnSurface)
binding.tvChapter.setTextColor(colorPrimary)
binding.div.setTextColor(colorSecondary)
binding.tvChapterIndex.setTextColor(colorSecondary)
binding.btnRead.setBackgroundColor(colorTertiary)
binding.tvToc.setTextColor(colorOnSurface)
binding.tvLasted.setTextColor(colorOnSurface)
}
private fun upLoading(isLoading: Boolean, chapterList: List<BookChapter>? = null) {
when {
isLoading -> {
@@ -47,9 +47,20 @@ class LabelsBar @JvmOverloads constructor(
usedChips.add(it)
}
}
style(chip) // 在这里应用外部传入的样式
style(chip)
chip.text = label
addView(chip)
}
fun applyColorScheme(colorPrimaryContainer: Int, colorOnPrimaryContainer: Int) {
usedChips.forEach { chip ->
chip.chipBackgroundColor = ColorStateList.valueOf(colorPrimaryContainer)
chip.setTextColor(colorOnPrimaryContainer)
}
unUsedChips.forEach { chip ->
chip.chipBackgroundColor = ColorStateList.valueOf(colorPrimaryContainer)
chip.setTextColor(colorOnPrimaryContainer)
}
}
}
@@ -10,9 +10,16 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:contentDescription="@string/bg_image"
android:foreground="@drawable/bg_book_mask_land"
android:scaleType="centerCrop" />
<ImageView
android:id="@+id/bg_book_mask"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:alpha="0.80"
android:background="@drawable/bg_gradient_mask"
android:contentDescription="@string/bg_image" />
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/app_bar"
android:layout_width="match_parent"
@@ -21,6 +28,7 @@
android:fitsSystemWindows="true">
<com.google.android.material.appbar.CollapsingToolbarLayout
android:id="@+id/coll_top_bar"
android:layout_width="match_parent"
android:layout_height="120dp"
style="?attr/collapsingToolbarLayoutLargeSize"
@@ -58,302 +66,301 @@
android:scrollbars="vertical">
<LinearLayout
android:id="@+id/ll_cover"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:baselineAligned="false"
android:orientation="horizontal">
<LinearLayout
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:layout_weight="3"
android:orientation="vertical"
android:paddingHorizontal="32dp"
tools:ignore="RtlSymmetry">
<androidx.cardview.widget.CardView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
<com.google.android.material.card.MaterialCardView
android:id="@+id/cd_cov"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginTop="16dp"
android:layout_marginStart="32dp"
app:cardCornerRadius="16dp">
app:cardCornerRadius="8dp"
app:layout_constraintDimensionRatio="5:7"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.3"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintWidth_percent="0.5"
app:strokeWidth="0dp">
<io.legato.kazusa.ui.widget.image.CoverImageView
android:id="@+id/iv_cover"
android:layout_width="150dp"
android:layout_height="210dp"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:contentDescription="@string/img_cover"
android:scaleType="centerCrop"
android:src="@drawable/image_cover_default" />
</androidx.cardview.widget.CardView>
</com.google.android.material.card.MaterialCardView>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:orientation="vertical"
android:paddingTop="16dp"
android:paddingHorizontal="30dp">
<TextView
android:id="@+id/tv_name"
style="@style/TextAppearance.Material3.ActionBar.Title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="32dp"
android:text="@string/book_name"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@id/cd_cov"
app:layout_constraintTop_toBottomOf="@id/cd_cov" />
<TextView
android:id="@+id/tv_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/book_name"
style="@style/TextAppearance.Material3.ActionBar.Title"
tools:ignore="RtlHardcoded" />
<ImageView
android:id="@+id/iv_name"
android:layout_width="18sp"
android:layout_height="18sp"
android:layout_marginTop="8dp"
android:contentDescription="@string/author"
android:src="@drawable/ic_author"
app:layout_constraintStart_toStartOf="@id/cd_cov"
app:layout_constraintTop_toBottomOf="@id/tv_name"
tools:ignore="RtlHardcoded,RtlSymmetry" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="horizontal"
android:paddingTop="6dp"
android:paddingBottom="2dp"
tools:ignore="UseCompoundDrawables">
<TextView
android:id="@+id/tv_author"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="4dp"
android:ellipsize="end"
android:singleLine="true"
android:text="@string/author"
android:textSize="14sp"
app:layout_constraintBottom_toBottomOf="@id/iv_name"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/iv_name"
app:layout_constraintTop_toTopOf="@id/iv_name"
tools:ignore="NestedWeights,RtlHardcoded,RtlSymmetry" />
<ImageView
android:layout_width="18sp"
android:layout_height="18sp"
android:contentDescription="@string/author"
android:src="@drawable/ic_author"
tools:ignore="RtlHardcoded,RtlSymmetry" />
<TextView
android:id="@+id/tv_author"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginStart="2dp"
android:ellipsize="end"
android:singleLine="true"
android:text="@string/author"
android:textSize="14sp"
tools:ignore="NestedWeights,RtlHardcoded,RtlSymmetry" />
<ImageView
android:id="@+id/iv_web"
android:layout_width="18sp"
android:layout_height="18sp"
android:layout_marginTop="8dp"
android:contentDescription="@string/origin_format"
android:src="@drawable/ic_web_outline"
app:layout_constraintStart_toStartOf="@id/cd_cov"
app:layout_constraintTop_toBottomOf="@id/iv_name"
tools:ignore="RtlHardcoded,RtlSymmetry" />
</LinearLayout>
<TextView
android:id="@+id/tv_origin"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="4dp"
android:layout_weight="1"
android:ellipsize="end"
android:singleLine="true"
android:textSize="14sp"
app:layout_constraintBottom_toBottomOf="@id/iv_web"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/iv_web"
app:layout_constraintTop_toTopOf="@id/iv_web"
tools:text="@string/origin_format" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal"
tools:ignore="UseCompoundDrawables">
</androidx.constraintlayout.widget.ConstraintLayout>
<ImageView
android:id="@+id/iv_web"
android:layout_width="18sp"
android:layout_height="18sp"
android:contentDescription="@string/origin_format"
android:src="@drawable/ic_web_outline"
tools:ignore="RtlHardcoded,RtlSymmetry" />
<TextView
android:id="@+id/tv_origin"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginStart="2dp"
android:ellipsize="end"
android:paddingRight="6dp"
android:singleLine="true"
android:textSize="14sp"
tools:ignore="NestedWeights,RtlHardcoded,RtlSymmetry"
tools:text="@string/origin_format" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
<LinearLayout
android:id="@+id/ll_info"
<com.google.android.material.card.MaterialCardView
android:id="@+id/cd_info"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="2"
android:background="@drawable/bg_gradient_mask_land"
android:orientation="vertical"
android:padding="8dp">
android:layout_marginHorizontal="32dp"
android:layout_marginVertical="16dp"
android:layout_weight="5"
app:strokeWidth="0dp">
<HorizontalScrollView
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/ll_info"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:scrollbars="none">
android:layout_height="match_parent"
android:layout_margin="8dp"
android:background="@drawable/bg_gradient_mask_land"
android:padding="8dp">
<io.legato.kazusa.ui.widget.LabelsBar
android:id="@+id/lb_kind"
android:layout_width="wrap_content"
<HorizontalScrollView
android:id="@+id/scroll_labels"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:visibility="visible" />
android:scrollbars="none"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
</HorizontalScrollView>
<io.legato.kazusa.ui.widget.LabelsBar
android:id="@+id/lb_kind"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="visible" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingHorizontal="8dp"
android:paddingBottom="8dp">
</HorizontalScrollView>
<LinearLayout
<com.google.android.material.button.MaterialButtonGroup
android:id="@+id/cl_buttons"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/scroll_labels">
<com.google.android.material.button.MaterialButton
android:id="@+id/btn_shelf"
style="@style/Widget.Material3Expressive.Button.TonalButton"
android:backgroundTint="?attr/colorSurfaceContainer"
style="@style/Widget.Material3Expressive.Button.TextButton"
app:cornerRadius="16dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="6dp"
android:padding="8dp"
android:text="@string/remove_from_bookshelf"
android:textSize="13sp"
app:icon="@drawable/ic_clear_all"
app:iconGravity="top"
app:iconPadding="4dp"
app:iconSize="20dp" />
app:iconSize="20dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<!-- 查看目录按钮 -->
<com.google.android.material.button.MaterialButton
android:id="@+id/tv_toc_view"
style="@style/Widget.Material3Expressive.Button.TonalButton"
android:backgroundTint="?attr/colorSurfaceContainer"
style="@style/Widget.Material3Expressive.Button.TextButton"
app:cornerRadius="16dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginHorizontal="8dp"
android:layout_marginStart="8dp"
android:padding="8dp"
android:text="@string/view_toc"
android:textSize="13sp"
app:icon="@drawable/ic_toc"
app:iconGravity="top"
app:iconPadding="4dp"
app:iconSize="20dp" />
app:iconSize="20dp"
app:layout_constraintStart_toEndOf="@id/btn_shelf"
app:layout_constraintTop_toTopOf="@id/btn_shelf" />
<!-- 更换分组按钮 -->
<com.google.android.material.button.MaterialButton
android:id="@+id/tv_change_group"
style="@style/Widget.Material3Expressive.Button.TonalButton"
android:backgroundTint="?attr/colorSurfaceContainer"
style="@style/Widget.Material3Expressive.Button.TextButton"
app:cornerRadius="16dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginHorizontal="8dp"
android:layout_marginStart="8dp"
android:padding="8dp"
android:text="@string/change_group"
android:textSize="13sp"
app:icon="@drawable/ic_groups"
app:iconGravity="top"
app:iconPadding="4dp"
app:iconSize="20dp" />
app:iconSize="20dp"
app:layout_constraintStart_toEndOf="@id/tv_toc_view"
app:layout_constraintTop_toTopOf="@id/tv_toc_view" />
<!-- 更换来源按钮(保持原样) -->
<com.google.android.material.button.MaterialButton
android:id="@+id/btn_change_source"
style="@style/Widget.Material3Expressive.Button.TonalButton"
android:backgroundTint="?attr/colorSurfaceContainer"
style="@style/Widget.Material3Expressive.Button.TextButton"
app:cornerRadius="16dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:text="@string/book_source"
android:padding="8dp"
app:icon="@drawable/ic_change_source"
android:text="@string/book_source"
android:textSize="13sp"
app:icon="@drawable/ic_change_source"
app:iconGravity="top"
app:iconPadding="4dp"
app:iconSize="20dp"
app:layout_constraintStart_toEndOf="@id/tv_change_group"
app:layout_constraintTop_toTopOf="@id/tv_change_group"
tools:contentDescription="@string/change_origin" />
</com.google.android.material.button.MaterialButtonGroup>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
<TextView
android:id="@+id/tv_toc"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:paddingVertical="16dp"
android:paddingHorizontal="2dp"
android:orientation="vertical">
android:layout_marginTop="8dp"
android:textSize="16sp"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/cl_buttons"
tools:text="@string/toc_s" />
<TextView
android:id="@+id/tv_toc"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="6dp"
android:textSize="16sp"
android:textStyle="bold"
tools:text="@string/toc_s" />
<TextView
android:id="@+id/tv_lasted"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:singleLine="true"
android:textSize="14sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_toc"
tools:text="@string/read_dur_progress" />
<TextView
android:id="@+id/tv_lasted"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingBottom="6dp"
android:singleLine="true"
android:textSize="14sp"
tools:ignore="NestedWeights,RtlHardcoded,RtlSymmetry"
tools:text="@string/read_dur_progress" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:id="@+id/tv_chapter"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:singleLine="true"
android:textColor="?attr/colorPrimary"
android:textSize="14sp"
android:textStyle="bold"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_lasted"
tools:text="@string/read_chapter_total" />
<TextView
android:id="@+id/tv_chapter"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="?attr/colorPrimary"
android:singleLine="true"
android:paddingEnd="8dp"
android:textSize="14sp"
android:textStyle="bold"
tools:ignore="NestedWeights,RtlHardcoded,RtlSymmetry"
tools:text="@string/read_chapter_total" />
<TextView
android:id="@+id/div"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:text="|"
android:textColor="?attr/colorSecondary"
app:layout_constraintBaseline_toBaselineOf="@id/tv_chapter"
app:layout_constraintStart_toEndOf="@id/tv_chapter" />
<TextView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:text="|"
android:textColor="?attr/colorSecondary"
android:layout_marginEnd="8dp"/>
<TextView
android:id="@+id/tv_chapter_index"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:singleLine="true"
android:textColor="?attr/colorSecondary"
android:textSize="14sp"
app:layout_constraintBaseline_toBaselineOf="@id/tv_chapter"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/div"
tools:text="@string/read_chapter_index" />
<TextView
android:id="@+id/tv_chapter_index"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="?attr/colorSecondary"
android:singleLine="true"
android:textSize="14sp"
tools:ignore="NestedWeights,RtlHardcoded,RtlSymmetry"
tools:text="@string/read_chapter_index"/>
</LinearLayout>
<TextView
android:id="@+id/tv_detail"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:clickable="true"
android:focusable="true"
android:lineSpacingExtra="4dp"
android:minHeight="48dp"
android:paddingBottom="80dp"
android:text="@string/book_intro"
android:textIsSelectable="true"
android:textSize="14sp"
android:visibility="visible"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_chapter"
tools:text="@string/book_intro" />
</LinearLayout>
</LinearLayout>
<TextView
android:id="@+id/tv_detail"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginHorizontal="8dp"
android:clickable="true"
android:focusable="true"
android:minHeight="48dp"
android:paddingBottom="80dp"
android:lineSpacingExtra="4dp"
android:text="@string/book_intro"
android:textIsSelectable="true"
android:textSize="14sp"
android:visibility="visible"
tools:ignore="RtlHardcoded,RtlSymmetry" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</com.google.android.material.card.MaterialCardView>
</LinearLayout>
</androidx.core.widget.NestedScrollView>
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
@@ -363,15 +370,13 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_marginEnd="40dp"
android:minWidth="0dp"
android:layout_marginBottom="24dp"
android:layout_margin="48dp"
android:contentDescription="@string/reading"
android:minWidth="56dp"
android:text="@string/reading"
android:textColor="?attr/colorOnPrimary"
android:contentDescription="@string/reading"
app:iconGravity="textStart"
app:backgroundTint="?attr/colorTertiary"
app:icon="@drawable/ic_bottom_books"
app:backgroundTint="?attr/colorSecondary"
app:iconTint="?attr/colorOnPrimary" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
@@ -14,16 +14,11 @@
android:scaleType="centerCrop"
tools:ignore="ContentDescription,ImageContrastCheck" />
<View
android:id="@+id/vw_bg"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<View
android:id="@+id/vw_bg2"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:alpha="0.80"
android:alpha="0.70"
android:background="?attr/colorSurfaceContainer" />
<io.legato.kazusa.ui.widget.TitleBar
@@ -194,8 +189,6 @@
android:paddingHorizontal="6dp"
app:layout_constraintBottom_toTopOf="@id/ll_sub_menu">
<View
android:layout_width="0dp"
android:layout_height="1dp"
+197 -162
View File
@@ -10,9 +10,16 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:contentDescription="@string/bg_image"
android:foreground="@drawable/bg_book_mask"
android:scaleType="centerCrop" />
<ImageView
android:id="@+id/bg_book_mask"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:alpha="0.6"
android:background="?attr/colorSecondaryContainer"
android:contentDescription="@string/bg_image" />
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/app_bar"
android:layout_width="match_parent"
@@ -21,6 +28,7 @@
android:fitsSystemWindows="true">
<com.google.android.material.appbar.CollapsingToolbarLayout
android:id="@+id/coll_top_bar"
android:layout_width="match_parent"
android:layout_height="?attr/collapsingToolbarLayoutMediumSize"
style="?attr/collapsingToolbarLayoutMediumStyle"
@@ -56,32 +64,52 @@
android:scrollbarStyle="outsideOverlay"
android:scrollbars="vertical">
<LinearLayout
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/ll_cover"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/bg_gradient_mask"
android:paddingHorizontal="16dp"
android:paddingVertical="8dp">
android:paddingVertical="8dp"
app:layout_constraintTop_toTopOf="parent">
<com.google.android.material.card.MaterialCardView
android:id="@+id/cd_cov"
android:layout_width="0dp"
android:layout_height="0dp"
app:cardCornerRadius="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintDimensionRatio="5:7"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintWidth_percent="0.32"
app:strokeWidth="0dp">
<io.legato.kazusa.ui.widget.image.CoverImageView
android:id="@+id/iv_cover"
android:layout_width="100dp"
android:layout_height="140dp"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:contentDescription="@string/img_cover"
android:scaleType="centerCrop"
android:src="@drawable/image_cover_default" />
</com.google.android.material.card.MaterialCardView>
<LinearLayout
android:layout_width="match_parent"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:orientation="vertical"
android:padding="12dp">
android:padding="12dp"
app:layout_constraintBottom_toBottomOf="@id/cd_cov"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/cd_cov"
app:layout_constraintTop_toTopOf="@id/cd_cov">
<TextView
android:id="@+id/tv_name"
@@ -102,6 +130,7 @@
tools:ignore="UseCompoundDrawables">
<ImageView
android:id="@+id/iv_name"
android:layout_width="18sp"
android:layout_height="18sp"
android:contentDescription="@string/author"
@@ -152,189 +181,195 @@
</LinearLayout>
</LinearLayout>
</LinearLayout>
<LinearLayout
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/ll_info"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_height="0dp"
android:background="?attr/colorSurface"
android:orientation="vertical">
android:orientation="vertical"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="@id/ll_cover">
<HorizontalScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:scrollbars="none">
<io.legato.kazusa.ui.widget.LabelsBar
android:id="@+id/lb_kind"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone" />
</HorizontalScrollView>
<LinearLayout
<HorizontalScrollView
android:id="@+id/scroll_labels"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingBottom="8dp">
android:paddingStart="16dp"
android:scrollbars="none"
app:layout_constraintTop_toTopOf="parent">
<LinearLayout
android:layout_width="match_parent"
<io.legato.kazusa.ui.widget.LabelsBar
android:id="@+id/lb_kind"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingHorizontal="8dp"
android:weightSum="4"
android:gravity="center_vertical">
android:visibility="gone" />
<com.google.android.material.button.MaterialButton
android:id="@+id/btn_shelf"
style="@style/Widget.Material3Expressive.Button.TextButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:padding="8dp"
android:text="@string/remove_from_bookshelf"
android:textSize="12sp"
app:icon="@drawable/ic_clear_all"
app:iconGravity="top"
app:iconPadding="4dp"
app:iconSize="20dp" />
</HorizontalScrollView>
<!-- 查看目录按钮 -->
<com.google.android.material.button.MaterialButton
android:id="@+id/tv_toc_view"
style="@style/Widget.Material3Expressive.Button.TextButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginHorizontal="4dp"
android:padding="8dp"
android:text="@string/view_toc"
android:textSize="12sp"
app:icon="@drawable/ic_toc"
app:iconGravity="top"
app:iconPadding="4dp"
app:iconSize="20dp" />
<LinearLayout
android:id="@+id/cl_buttons"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="8dp"
android:gravity="center_vertical"
android:orientation="horizontal"
android:weightSum="4"
app:layout_constraintTop_toBottomOf="@id/scroll_labels">
<!-- 更换分组按钮 -->
<com.google.android.material.button.MaterialButton
android:id="@+id/tv_change_group"
style="@style/Widget.Material3Expressive.Button.TextButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="4dp"
android:layout_weight="1"
android:padding="8dp"
android:text="@string/change_group"
android:textSize="12sp"
app:icon="@drawable/ic_groups"
app:iconGravity="top"
app:iconPadding="4dp"
app:iconSize="20dp" />
<!-- 更换来源按钮(保持原样) -->
<com.google.android.material.button.MaterialButton
android:id="@+id/btn_change_source"
style="@style/Widget.Material3Expressive.Button.TextButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/book_source"
android:padding="8dp"
app:icon="@drawable/ic_change_source"
android:textSize="12sp"
app:iconGravity="top"
app:iconPadding="4dp"
app:iconSize="20dp"
tools:contentDescription="@string/change_origin" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
<com.google.android.material.button.MaterialButton
android:id="@+id/btn_shelf"
style="@style/Widget.Material3Expressive.Button.TextButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:paddingHorizontal="16dp"
android:paddingVertical="8dp"
android:orientation="vertical">
android:layout_weight="1"
android:padding="8dp"
android:text="@string/remove_from_bookshelf"
android:textSize="12sp"
app:icon="@drawable/ic_clear_all"
app:iconGravity="top"
app:iconPadding="4dp"
app:iconSize="20dp" />
<TextView
android:id="@+id/tv_toc"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="6dp"
android:textSize="16sp"
android:textStyle="bold"
tools:text="@string/toc_s" />
<!-- 查看目录按钮 -->
<com.google.android.material.button.MaterialButton
android:id="@+id/tv_toc_view"
style="@style/Widget.Material3Expressive.Button.TextButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginHorizontal="4dp"
android:layout_weight="1"
android:padding="8dp"
android:text="@string/view_toc"
android:textSize="12sp"
app:icon="@drawable/ic_toc"
app:iconGravity="top"
app:iconPadding="4dp"
app:iconSize="20dp" />
<TextView
android:id="@+id/tv_lasted"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingBottom="6dp"
android:textSize="14sp"
tools:ignore="NestedWeights,RtlHardcoded,RtlSymmetry"
tools:text="@string/read_dur_progress" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:id="@+id/tv_chapter"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="?attr/colorPrimaryVariant"
android:singleLine="true"
android:paddingEnd="8dp"
android:textSize="14sp"
android:textStyle="bold"
tools:ignore="NestedWeights,RtlHardcoded,RtlSymmetry"
tools:text="@string/read_chapter_total" />
<TextView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:text="|"
android:textColor="?attr/colorSecondary"
android:layout_marginEnd="8dp"/>
<TextView
android:id="@+id/tv_chapter_index"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="?attr/colorSecondary"
android:singleLine="true"
android:textSize="14sp"
tools:ignore="NestedWeights,RtlHardcoded,RtlSymmetry"
tools:text="@string/read_chapter_index"/>
</LinearLayout>
</LinearLayout>
<!-- 更换分组按钮 -->
<com.google.android.material.button.MaterialButton
android:id="@+id/tv_change_group"
style="@style/Widget.Material3Expressive.Button.TextButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="4dp"
android:layout_weight="1"
android:padding="8dp"
android:text="@string/change_group"
android:textSize="12sp"
app:icon="@drawable/ic_groups"
app:iconGravity="top"
app:iconPadding="4dp"
app:iconSize="20dp" />
<!-- 更换来源按钮(保持原样) -->
<com.google.android.material.button.MaterialButton
android:id="@+id/btn_change_source"
style="@style/Widget.Material3Expressive.Button.TextButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:padding="8dp"
android:text="@string/book_source"
android:textSize="12sp"
app:icon="@drawable/ic_change_source"
app:iconGravity="top"
app:iconPadding="4dp"
app:iconSize="20dp"
tools:contentDescription="@string/change_origin" />
</LinearLayout>
<TextView
android:id="@+id/tv_detail"
android:layout_width="match_parent"
android:id="@+id/tv_toc"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginHorizontal="16dp"
android:layout_marginTop="8dp"
android:textSize="16sp"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/cl_buttons"
tools:text="@string/toc_s" />
<TextView
android:id="@+id/tv_lasted"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginHorizontal="16dp"
android:layout_marginTop="8dp"
android:singleLine="true"
android:textSize="14sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_toc"
tools:text="@string/read_dur_progress" />
<TextView
android:id="@+id/tv_chapter"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="8dp"
android:singleLine="true"
android:textColor="?attr/colorPrimary"
android:textSize="14sp"
android:textStyle="bold"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_lasted"
tools:text="@string/read_chapter_total" />
<TextView
android:id="@+id/div"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:text="|"
android:textColor="?attr/colorSecondary"
app:layout_constraintBaseline_toBaselineOf="@id/tv_chapter"
app:layout_constraintStart_toEndOf="@id/tv_chapter" />
<TextView
android:id="@+id/tv_chapter_index"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginEnd="16dp"
android:singleLine="true"
android:textColor="?attr/colorSecondary"
android:textSize="14sp"
app:layout_constraintBaseline_toBaselineOf="@id/tv_chapter"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/div"
tools:text="@string/read_chapter_index" />
<TextView
android:id="@+id/tv_detail"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginHorizontal="16dp"
android:layout_marginTop="8dp"
android:clickable="true"
android:focusable="true"
android:minHeight="48dp"
android:paddingHorizontal="16dp"
android:paddingBottom="80dp"
android:lineSpacingExtra="4dp"
android:minHeight="48dp"
android:paddingBottom="120dp"
android:text="@string/book_intro"
android:textIsSelectable="true"
android:textSize="14sp"
android:visibility="visible"
tools:ignore="RtlHardcoded,RtlSymmetry" />
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_chapter"
tools:text="@string/book_intro" />
</LinearLayout>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.core.widget.NestedScrollView>
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>