页眉页脚 #193

This commit is contained in:
HapeLee
2025-11-02 13:19:27 +08:00
parent b1701ad49f
commit 767c1d1a81
9 changed files with 230 additions and 38 deletions
@@ -22,10 +22,12 @@ object ReadTipConfig {
const val chapterTitleArrow = 12
const val batteryInside = 13
const val batteryIcon = 14
const val batteryClassic = 15
const val timeBatteryClassic = 16
val tipValues = arrayOf(
none, bookName, chapterTitle, chapterTitleArrow, time, battery, batteryInside, batteryIcon, batteryPercentage, page,
totalProgress, totalProgress1, pageAndTotal, timeBattery, timeBatteryPercentage
none, bookName, chapterTitle, chapterTitleArrow, time, battery, batteryClassic, batteryInside, batteryIcon, batteryPercentage, page,
totalProgress, totalProgress1, pageAndTotal, timeBattery, timeBatteryClassic, timeBatteryPercentage
)
val tipNames get() = appCtx.resources.getStringArray(R.array.read_tip).toList()
@@ -57,6 +57,9 @@ class PageView(context: Context) : FrameLayout(context) {
private var tvTitleArrow: BatteryView? = null
private var tvBatteryInside: BatteryView? = null
private var tvBatteryIcon: BatteryView? = null
private var tvBatteryClassic: BatteryView? = null
private var tvTimeBatteryClassic: BatteryView? = null
private var isMainView = false
var isScroll = false
@@ -181,86 +184,92 @@ class PageView(context: Context) : FrameLayout(context) {
}
tvTitle = getTipView(ReadTipConfig.chapterTitle)?.apply {
tag = ReadTipConfig.chapterTitle
isBattery = false
typeface = ChapterProvider.typeface
textSize = 12f
batteryMode = BatteryView.BatteryMode.NO_BATTERY
}
tvTitleArrow = getTipView(ReadTipConfig.chapterTitleArrow)?.apply {
tag = ReadTipConfig.chapterTitleArrow
isBattery = false
typeface = ChapterProvider.typeface
textSize = 12f
batteryMode = BatteryView.BatteryMode.ARROW
}
tvTime = getTipView(ReadTipConfig.time)?.apply {
tag = ReadTipConfig.time
isBattery = false
typeface = ChapterProvider.typeface
textSize = 12f
batteryMode = BatteryView.BatteryMode.NO_BATTERY
}
tvBattery = getTipView(ReadTipConfig.battery)?.apply {
tag = ReadTipConfig.battery
isBattery = true
textSize = 11f
batteryMode = BatteryView.BatteryMode.OUTER
}
tvBatteryClassic = getTipView(ReadTipConfig.batteryClassic)?.apply {
tag = ReadTipConfig.batteryClassic
textSize = 11f
batteryMode = BatteryView.BatteryMode.CLASSIC
}
tvBatteryInside = getTipView(ReadTipConfig.batteryInside)?.apply {
tag = ReadTipConfig.batteryInside
isBattery = true
textSize = 11f
batteryMode = BatteryView.BatteryMode.INNER
}
tvBatteryIcon = getTipView(ReadTipConfig.batteryIcon)?.apply {
tag = ReadTipConfig.batteryIcon
isBattery = true
textSize = 11f
batteryMode = BatteryView.BatteryMode.ICON
}
tvPage = getTipView(ReadTipConfig.page)?.apply {
tag = ReadTipConfig.page
isBattery = false
typeface = ChapterProvider.typeface
textSize = 12f
batteryMode = BatteryView.BatteryMode.NO_BATTERY
}
tvTotalProgress = getTipView(ReadTipConfig.totalProgress)?.apply {
tag = ReadTipConfig.totalProgress
isBattery = false
batteryMode = BatteryView.BatteryMode.NO_BATTERY
typeface = ChapterProvider.typeface
textSize = 12f
}
tvTotalProgress1 = getTipView(ReadTipConfig.totalProgress1)?.apply {
tag = ReadTipConfig.totalProgress1
isBattery = false
batteryMode = BatteryView.BatteryMode.NO_BATTERY
typeface = ChapterProvider.typeface
textSize = 12f
}
tvPageAndTotal = getTipView(ReadTipConfig.pageAndTotal)?.apply {
tag = ReadTipConfig.pageAndTotal
isBattery = false
batteryMode = BatteryView.BatteryMode.NO_BATTERY
typeface = ChapterProvider.typeface
textSize = 12f
}
tvBookName = getTipView(ReadTipConfig.bookName)?.apply {
tag = ReadTipConfig.bookName
isBattery = false
batteryMode = BatteryView.BatteryMode.NO_BATTERY
typeface = ChapterProvider.typeface
textSize = 12f
}
tvTimeBattery = getTipView(ReadTipConfig.timeBattery)?.apply {
tag = ReadTipConfig.timeBattery
isBattery = true
textSize = 11f
batteryMode = BatteryView.BatteryMode.TIME
}
tvTimeBatteryClassic = getTipView(ReadTipConfig.timeBatteryClassic)?.apply {
tag = ReadTipConfig.timeBatteryClassic
typeface = ChapterProvider.typeface
textSize = 11f
batteryMode = BatteryView.BatteryMode.CLASSIC
}
tvBatteryP = getTipView(ReadTipConfig.batteryPercentage)?.apply {
tag = ReadTipConfig.batteryPercentage
isBattery = false
batteryMode = BatteryView.BatteryMode.NO_BATTERY
typeface = ChapterProvider.typeface
textSize = 12f
}
tvTimeBatteryP = getTipView(ReadTipConfig.timeBatteryPercentage)?.apply {
tag = ReadTipConfig.timeBatteryPercentage
isBattery = false
batteryMode = BatteryView.BatteryMode.NO_BATTERY
typeface = ChapterProvider.typeface
textSize = 12f
}
@@ -318,6 +327,7 @@ class PageView(context: Context) : FrameLayout(context) {
fun upBattery(battery: Int) {
this.battery = battery
tvBattery?.setBattery(battery)
tvBatteryClassic?.setBattery(battery)
tvBatteryInside?.setBattery(battery)
tvBatteryIcon?.setBattery(battery)
tvBatteryP?.text = "$battery%"
@@ -332,7 +342,8 @@ class PageView(context: Context) : FrameLayout(context) {
val time = timeFormat.format(Date(System.currentTimeMillis()))
tvTimeBattery?.setBattery(battery, time)
tvTimeBatteryP?.setBattery(battery, time)
tvTimeBattery?.text = "$time $battery%"
tvTimeBatteryClassic?.setBattery(battery, time)
tvTimeBatteryClassic?.text = "$time $battery%"
tvTimeBatteryP?.text = "$time $battery%"
}
@@ -19,7 +19,7 @@ class BatteryView @JvmOverloads constructor(
private val binding: ViewBatteryBinding =
ViewBatteryBinding.inflate(LayoutInflater.from(context), this, true)
enum class BatteryMode { OUTER, INNER, ICON, ARROW }
enum class BatteryMode { OUTER, INNER, ICON, ARROW, TIME, CLASSIC, NO_BATTERY }
private val batteryTypeface by lazy {
Typeface.createFromAsset(context.assets, "font/number.ttf")
@@ -28,22 +28,10 @@ class BatteryView @JvmOverloads constructor(
private var battery: Int = 0
init {
setPadding(4.dpToPx(), 3.dpToPx(), 6.dpToPx(), 3.dpToPx())
binding.batteryText.typeface = batteryTypeface
binding.batteryTextInner.typeface = batteryTypeface
//binding.batteryText.typeface = batteryTypeface
//binding.batteryTextInner.typeface = batteryTypeface
}
var isBattery = false
set(value) {
field = value
binding.arrowIcon.visibility = if (value) VISIBLE else GONE
binding.batteryTextInner.visibility = if (value) VISIBLE else GONE
binding.batteryTextInner.typeface = if (value) Typeface.DEFAULT else batteryTypeface
binding.batteryText.typeface = if (value) Typeface.DEFAULT else batteryTypeface
binding.batteryIcon.visibility = if (value) VISIBLE else GONE
binding.batteryFill.visibility = if (value) VISIBLE else GONE
}
var text: CharSequence?
get() = binding.batteryText.text
set(value) {
@@ -77,14 +65,35 @@ class BatteryView @JvmOverloads constructor(
fun setBattery(battery: Int, text: String? = null) {
this.battery = battery.coerceIn(0, 100)
binding.batteryText.text = text?.let { "$it $battery" } ?: battery.toString()
binding.batteryTextInner.text = text?.let { "$it $battery" } ?: battery.toString()
when (batteryMode) {
BatteryMode.TIME -> {
val timePart = text ?: ""
val batteryPart = "$battery"
binding.batteryText.text = timePart
binding.batteryTextInner.text = batteryPart
}
BatteryMode.CLASSIC -> {
binding.batteryClassic.isBattery = true
binding.batteryClassic.setBattery(battery, text)
}
else -> {
val displayText = text?.let { "$it $battery" } ?: battery.toString()
binding.batteryText.text = displayText
binding.batteryTextInner.text = displayText
}
}
updateFill()
}
fun setColor(@ColorInt color: Int) {
binding.batteryText.setTextColor(color)
binding.batteryTextInner.setTextColor(color)
binding.batteryFill.setCardBackgroundColor(color)
binding.batteryClassic.setColor(color)
binding.arrowIcon.setColorFilter(color, PorterDuff.Mode.SRC_IN)
binding.batteryIcon.setColorFilter(color, PorterDuff.Mode.SRC_IN)
binding.batteryIcon.alpha = 0.76f
@@ -101,28 +110,58 @@ class BatteryView @JvmOverloads constructor(
private fun updateMode() {
when (batteryMode) {
BatteryMode.OUTER -> {
binding.arrowIcon.visibility = GONE
binding.batteryText.visibility = VISIBLE
binding.batteryTextInner.visibility = GONE
binding.batteryFill.visibility = VISIBLE
binding.batteryIcon.visibility = VISIBLE
binding.arrowIcon.visibility = GONE
binding.batteryClassic.visibility = GONE
}
BatteryMode.INNER -> {
binding.arrowIcon.visibility = GONE
binding.batteryText.visibility = GONE
binding.batteryTextInner.visibility = VISIBLE
binding.batteryFill.visibility = GONE
binding.batteryIcon.visibility = VISIBLE
binding.arrowIcon.visibility = GONE
binding.batteryClassic.visibility = GONE
}
BatteryMode.ICON -> {
binding.arrowIcon.visibility = GONE
binding.batteryText.visibility = GONE
binding.batteryTextInner.visibility = GONE
binding.batteryFill.visibility = VISIBLE
binding.batteryIcon.visibility = VISIBLE
binding.arrowIcon.visibility = GONE
binding.batteryClassic.visibility = GONE
}
BatteryMode.ARROW -> {
binding.batteryText.visibility = VISIBLE
binding.batteryTextInner.visibility = GONE
binding.batteryFill.visibility = GONE
binding.batteryIcon.visibility = GONE
binding.arrowIcon.visibility = VISIBLE
binding.batteryClassic.visibility = GONE
}
BatteryMode.TIME -> {
binding.batteryText.visibility = VISIBLE
binding.batteryTextInner.visibility = VISIBLE
binding.batteryFill.visibility = GONE
binding.batteryIcon.visibility = VISIBLE
binding.arrowIcon.visibility = GONE
binding.batteryClassic.visibility = GONE
}
BatteryMode.CLASSIC -> {
binding.batteryText.visibility = GONE
binding.batteryTextInner.visibility = GONE
binding.batteryFill.visibility = GONE
binding.batteryIcon.visibility = GONE
binding.arrowIcon.visibility = GONE
binding.batteryClassic.visibility = VISIBLE
}
BatteryMode.NO_BATTERY -> {
binding.batteryFill.visibility = GONE
binding.batteryIcon.visibility = GONE
binding.arrowIcon.visibility = GONE
binding.batteryClassic.visibility = GONE
}
}
updateFill()
@@ -0,0 +1,122 @@
package io.legado.app.ui.widget
import android.annotation.SuppressLint
import android.content.Context
import android.graphics.Canvas
import android.graphics.Paint
import android.graphics.Rect
import android.graphics.Typeface
import android.os.Build
import android.text.StaticLayout
import android.util.AttributeSet
import androidx.annotation.ColorInt
import androidx.appcompat.widget.AppCompatTextView
import io.legado.app.help.config.AppConfig
import io.legado.app.utils.canvasrecorder.CanvasRecorderFactory
import io.legado.app.utils.canvasrecorder.recordIfNeededThenDraw
import io.legado.app.utils.dpToPx
class BatteryViewOrgin @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null
) : AppCompatTextView(context, attrs) {
private val batteryTypeface by lazy {
Typeface.createFromAsset(context.assets, "font/number.ttf")
}
private val batteryPaint = Paint()
private val outFrame = Rect()
private val polar = Rect()
private val canvasRecorder = CanvasRecorderFactory.create()
var isBattery = false
set(value) {
field = value
if (value && !isInEditMode) {
super.setTypeface(batteryTypeface)
postInvalidate()
}
}
private var battery: Int = 0
init {
setPadding(4.dpToPx(), 3.dpToPx(), 6.dpToPx(), 3.dpToPx())
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
isFallbackLineSpacing = false
}
batteryPaint.strokeWidth = 1f.dpToPx()
batteryPaint.isAntiAlias = true
batteryPaint.color = paint.color
}
override fun setTypeface(tf: Typeface?) {
if (!isBattery) {
super.setTypeface(tf)
}
}
fun setColor(@ColorInt color: Int) {
setTextColor(color)
batteryPaint.color = color
invalidate()
}
@SuppressLint("SetTextI18n")
fun setBattery(battery: Int, text: String? = null) {
this.battery = battery
if (text.isNullOrEmpty()) {
setText(battery.toString())
} else {
setText("$text $battery")
}
}
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
super.onLayout(changed, left, top, right, bottom)
canvasRecorder.invalidate()
}
override fun onDraw(canvas: Canvas) {
if (AppConfig.optimizeRender) {
canvasRecorder.recordIfNeededThenDraw(canvas, width, height) {
super.onDraw(this)
drawBattery(this)
}
} else {
super.onDraw(canvas)
drawBattery(canvas)
}
}
private fun drawBattery(canvas: Canvas) {
if (!isBattery) return
layout.getLineBounds(0, outFrame)
val batteryStart = layout
.getPrimaryHorizontal(text.length - battery.toString().length)
.toInt() + 2.dpToPx()
val batteryEnd = batteryStart +
StaticLayout.getDesiredWidth(battery.toString(), paint).toInt() + 4.dpToPx()
outFrame.set(
batteryStart,
2.dpToPx(),
batteryEnd,
height - 2.dpToPx()
)
val dj = (outFrame.bottom - outFrame.top) / 3
polar.set(
batteryEnd,
outFrame.top + dj,
batteryEnd + 2.dpToPx(),
outFrame.bottom - dj
)
batteryPaint.style = Paint.Style.STROKE
canvas.drawRect(outFrame, batteryPaint)
batteryPaint.style = Paint.Style.FILL
canvas.drawRect(polar, batteryPaint)
}
@Suppress("UNNECESSARY_SAFE_CALL")
override fun invalidate() {
super.invalidate()
canvasRecorder?.invalidate()
}
}