更多的电池模式与带箭头的标题 #185

This commit is contained in:
HapeLee
2025-11-02 00:40:25 +08:00
parent de1ae6f9e4
commit 45b454c4c1
10 changed files with 231 additions and 88 deletions
@@ -19,9 +19,12 @@ object ReadTipConfig {
const val timeBattery = 8 const val timeBattery = 8
const val timeBatteryPercentage = 9 const val timeBatteryPercentage = 9
const val totalProgress1 = 11 const val totalProgress1 = 11
const val chapterTitleArrow = 12
const val batteryInside = 13
const val batteryIcon = 14
val tipValues = arrayOf( val tipValues = arrayOf(
none, bookName, chapterTitle, time, battery, batteryPercentage, page, none, bookName, chapterTitle, chapterTitleArrow, time, battery, batteryInside, batteryIcon, batteryPercentage, page,
totalProgress, totalProgress1, pageAndTotal, timeBattery, timeBatteryPercentage totalProgress, totalProgress1, pageAndTotal, timeBattery, timeBatteryPercentage
) )
val tipNames get() = appCtx.resources.getStringArray(R.array.read_tip).toList() val tipNames get() = appCtx.resources.getStringArray(R.array.read_tip).toList()
@@ -31,7 +31,6 @@ import io.legado.app.utils.applyStatusBarPadding
import io.legado.app.utils.dpToPx import io.legado.app.utils.dpToPx
import io.legado.app.utils.gone import io.legado.app.utils.gone
import io.legado.app.utils.setOnApplyWindowInsetsListenerCompat import io.legado.app.utils.setOnApplyWindowInsetsListenerCompat
import io.legado.app.utils.setTextIfNotEqual
import io.legado.app.utils.statusBarHeight import io.legado.app.utils.statusBarHeight
import splitties.views.backgroundColor import splitties.views.backgroundColor
import java.util.Date import java.util.Date
@@ -55,6 +54,9 @@ class PageView(context: Context) : FrameLayout(context) {
private var tvBookName: BatteryView? = null private var tvBookName: BatteryView? = null
private var tvTimeBattery: BatteryView? = null private var tvTimeBattery: BatteryView? = null
private var tvTimeBatteryP: BatteryView? = null private var tvTimeBatteryP: BatteryView? = null
private var tvTitleArrow: BatteryView? = null
private var tvBatteryInside: BatteryView? = null
private var tvBatteryIcon: BatteryView? = null
private var isMainView = false private var isMainView = false
var isScroll = false var isScroll = false
@@ -183,6 +185,13 @@ class PageView(context: Context) : FrameLayout(context) {
typeface = ChapterProvider.typeface typeface = ChapterProvider.typeface
textSize = 12f textSize = 12f
} }
tvTitleArrow = getTipView(ReadTipConfig.chapterTitleArrow)?.apply {
tag = ReadTipConfig.chapterTitleArrow
isBattery = false
typeface = ChapterProvider.typeface
textSize = 12f
batteryMode = BatteryView.BatteryMode.ARROW
}
tvTime = getTipView(ReadTipConfig.time)?.apply { tvTime = getTipView(ReadTipConfig.time)?.apply {
tag = ReadTipConfig.time tag = ReadTipConfig.time
isBattery = false isBattery = false
@@ -193,6 +202,19 @@ class PageView(context: Context) : FrameLayout(context) {
tag = ReadTipConfig.battery tag = ReadTipConfig.battery
isBattery = true isBattery = true
textSize = 11f textSize = 11f
batteryMode = BatteryView.BatteryMode.OUTER
}
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 { tvPage = getTipView(ReadTipConfig.page)?.apply {
tag = ReadTipConfig.page tag = ReadTipConfig.page
@@ -296,6 +318,8 @@ class PageView(context: Context) : FrameLayout(context) {
fun upBattery(battery: Int) { fun upBattery(battery: Int) {
this.battery = battery this.battery = battery
tvBattery?.setBattery(battery) tvBattery?.setBattery(battery)
tvBatteryInside?.setBattery(battery)
tvBatteryIcon?.setBattery(battery)
tvBatteryP?.text = "$battery%" tvBatteryP?.text = "$battery%"
upTimeBattery() upTimeBattery()
} }
@@ -307,6 +331,8 @@ class PageView(context: Context) : FrameLayout(context) {
private fun upTimeBattery() { private fun upTimeBattery() {
val time = timeFormat.format(Date(System.currentTimeMillis())) val time = timeFormat.format(Date(System.currentTimeMillis()))
tvTimeBattery?.setBattery(battery, time) tvTimeBattery?.setBattery(battery, time)
tvTimeBatteryP?.setBattery(battery, time)
tvTimeBattery?.text = "$time $battery%"
tvTimeBatteryP?.text = "$time $battery%" tvTimeBatteryP?.text = "$time $battery%"
} }
@@ -352,6 +378,7 @@ class PageView(context: Context) : FrameLayout(context) {
fun setProgress(textPage: TextPage) = textPage.apply { fun setProgress(textPage: TextPage) = textPage.apply {
tvBookName?.setTextIfNotEqual(ReadBook.book?.name) tvBookName?.setTextIfNotEqual(ReadBook.book?.name)
tvTitle?.setTextIfNotEqual(textPage.title) tvTitle?.setTextIfNotEqual(textPage.title)
tvTitleArrow?.setTextIfNotEqual(textPage.title)
val readProgress = readProgress val readProgress = readProgress
tvTotalProgress?.setTextIfNotEqual(readProgress) tvTotalProgress?.setTextIfNotEqual(readProgress)
tvTotalProgress1?.setTextIfNotEqual("${chapterIndex.plus(1)}/${chapterSize}") tvTotalProgress1?.setTextIfNotEqual("${chapterIndex.plus(1)}/${chapterSize}")
@@ -1,122 +1,140 @@
package io.legado.app.ui.widget package io.legado.app.ui.widget
import android.annotation.SuppressLint
import android.content.Context import android.content.Context
import android.graphics.Canvas import android.graphics.PorterDuff
import android.graphics.Paint
import android.graphics.Rect
import android.graphics.Typeface import android.graphics.Typeface
import android.os.Build
import android.text.StaticLayout
import android.util.AttributeSet import android.util.AttributeSet
import android.util.TypedValue
import android.view.LayoutInflater
import androidx.annotation.ColorInt import androidx.annotation.ColorInt
import androidx.appcompat.widget.AppCompatTextView import androidx.constraintlayout.widget.ConstraintLayout
import io.legado.app.help.config.AppConfig import io.legado.app.databinding.ViewBatteryBinding
import io.legado.app.utils.canvasrecorder.CanvasRecorderFactory
import io.legado.app.utils.canvasrecorder.recordIfNeededThenDraw
import io.legado.app.utils.dpToPx import io.legado.app.utils.dpToPx
class BatteryView @JvmOverloads constructor( class BatteryView @JvmOverloads constructor(
context: Context, context: Context,
attrs: AttributeSet? = null attrs: AttributeSet? = null
) : AppCompatTextView(context, attrs) { ) : ConstraintLayout(context, attrs) {
private val binding: ViewBatteryBinding =
ViewBatteryBinding.inflate(LayoutInflater.from(context), this, true)
enum class BatteryMode { OUTER, INNER, ICON, ARROW }
private val batteryTypeface by lazy { private val batteryTypeface by lazy {
Typeface.createFromAsset(context.assets, "font/number.ttf") 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 private var battery: Int = 0
init { init {
setPadding(4.dpToPx(), 3.dpToPx(), 6.dpToPx(), 3.dpToPx()) setPadding(4.dpToPx(), 3.dpToPx(), 6.dpToPx(), 3.dpToPx())
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { binding.batteryText.typeface = batteryTypeface
isFallbackLineSpacing = false binding.batteryTextInner.typeface = batteryTypeface
}
batteryPaint.strokeWidth = 1f.dpToPx()
batteryPaint.isAntiAlias = true
batteryPaint.color = paint.color
} }
override fun setTypeface(tf: Typeface?) { var isBattery = false
if (!isBattery) { set(value) {
super.setTypeface(tf) 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) {
binding.batteryText.text = value
binding.batteryTextInner.text = value
}
var batteryMode: BatteryMode = BatteryMode.OUTER
set(value) {
field = value
updateMode()
}
var typeface: Typeface?
get() = binding.batteryText.typeface
set(value) {
binding.batteryText.typeface = value ?: Typeface.DEFAULT
binding.batteryTextInner.typeface = value ?: Typeface.DEFAULT
}
var textSize: Float
get() = binding.batteryText.textSize / binding.batteryText.resources.displayMetrics.density
set(value) {
binding.batteryText.setTextSize(TypedValue.COMPLEX_UNIT_SP, value)
}
fun setTextColor(@ColorInt color: Int) {
binding.batteryText.setTextColor(color)
binding.batteryTextInner.setTextColor(color)
}
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()
updateFill()
} }
fun setColor(@ColorInt color: Int) { fun setColor(@ColorInt color: Int) {
setTextColor(color) binding.batteryText.setTextColor(color)
batteryPaint.color = color binding.batteryFill.setCardBackgroundColor(color)
invalidate() binding.arrowIcon.setColorFilter(color, PorterDuff.Mode.SRC_IN)
binding.batteryIcon.setColorFilter(color, PorterDuff.Mode.SRC_IN)
binding.batteryIcon.alpha = 0.76f
binding.arrowIcon.alpha = 0.76f
} }
@SuppressLint("SetTextI18n") fun setTextIfNotEqual(newText: String?) {
fun setBattery(battery: Int, text: String? = null) { if (binding.batteryText.text?.toString() != newText) {
this.battery = battery binding.batteryText.text = newText
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) { private fun updateMode() {
if (AppConfig.optimizeRender) { when (batteryMode) {
canvasRecorder.recordIfNeededThenDraw(canvas, width, height) { BatteryMode.OUTER -> {
super.onDraw(this) binding.arrowIcon.visibility = GONE
drawBattery(this) binding.batteryText.visibility = VISIBLE
binding.batteryTextInner.visibility = GONE
binding.batteryFill.visibility = VISIBLE
}
BatteryMode.INNER -> {
binding.arrowIcon.visibility = GONE
binding.batteryText.visibility = GONE
binding.batteryTextInner.visibility = VISIBLE
binding.batteryFill.visibility = GONE
}
BatteryMode.ICON -> {
binding.arrowIcon.visibility = GONE
binding.batteryText.visibility = GONE
binding.batteryTextInner.visibility = GONE
binding.batteryFill.visibility = VISIBLE
}
BatteryMode.ARROW -> {
binding.batteryText.visibility = VISIBLE
binding.batteryTextInner.visibility = GONE
binding.batteryFill.visibility = GONE
binding.arrowIcon.visibility = VISIBLE
} }
} else {
super.onDraw(canvas)
drawBattery(canvas)
} }
updateFill()
} }
private fun drawBattery(canvas: Canvas) { private fun updateFill() {
if (!isBattery) return post {
layout.getLineBounds(0, outFrame) val maxWidth = 14.dpToPx()
val batteryStart = layout val params = binding.batteryFill.layoutParams
.getPrimaryHorizontal(text.length - battery.toString().length) params.width = (maxWidth * battery / 100f).toInt()
.toInt() + 2.dpToPx() binding.batteryFill.layoutParams = params
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()
} }
} }
+12
View File
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M175.38,680Q135.64,680 107.82,652.18Q80,624.36 80,584.62L80,375.38Q80,335.64 107.82,307.82Q135.64,280 175.38,280L700,280Q739.74,280 767.56,307.82Q795.38,335.64 795.38,375.38L795.38,584.62Q795.38,624.36 767.56,652.18Q739.74,680 700,680L175.38,680ZM175.38,640L700,640Q723.54,640 739.46,624.08Q755.38,608.15 755.38,584.62L755.38,375.38Q755.38,351.85 739.46,335.92Q723.54,320 700,320L175.38,320Q151.85,320 135.92,335.92Q120,351.85 120,375.38L120,584.62Q120,608.15 135.92,624.08Q151.85,640 175.38,640ZM835.38,564.62L835.38,396.15L847.69,396.15Q861.42,396.15 870.71,405.44Q880,414.73 880,428.46L880,532.31Q880,546.04 870.71,555.33Q861.42,564.62 847.69,564.62L835.38,564.62ZM120,640Q120,640 120,624.08Q120,608.15 120,584.62L120,375.38Q120,351.85 120,335.92Q120,320 120,320L120,320Q120,320 120,335.92Q120,351.85 120,375.38L120,584.62Q120,608.15 120,624.08Q120,640 120,640Z"/>
</vector>
+71
View File
@@ -0,0 +1,71 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto">
<ImageView
android:id="@+id/arrow_icon"
android:layout_width="24dp"
android:layout_height="12dp"
android:scaleType="centerCrop"
android:visibility="gone"
android:src="@drawable/ic_arrow_right"
android:rotation="180"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
<TextView
android:id="@+id/battery_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:includeFontPadding="false"
android:textSize="11sp"
android:text="@string/text"
android:textStyle="bold"
app:layout_constraintStart_toEndOf="@id/arrow_icon"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
<TextView
android:id="@+id/battery_text_inner"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="2.4dp"
android:layout_marginBottom="0.8dp"
android:textSize="7sp"
android:text="@string/text"
android:textStyle="bold"
app:layout_constraintStart_toStartOf="@id/battery_icon"
app:layout_constraintEnd_toEndOf="@id/battery_icon"
app:layout_constraintTop_toTopOf="@id/battery_icon"
app:layout_constraintBottom_toBottomOf="@id/battery_icon"
tools:ignore="SmallSp" />
<ImageView
android:id="@+id/battery_icon"
android:layout_width="28dp"
android:layout_height="12dp"
android:layout_marginStart="4dp"
android:scaleType="centerCrop"
android:src="@drawable/ic_battery"
app:layout_constraintStart_toEndOf="@id/battery_text"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
<com.google.android.material.card.MaterialCardView
android:id="@+id/battery_fill"
android:layout_width="14dp"
android:layout_height="8dp"
android:visibility="gone"
android:layout_marginStart="4.2dp"
app:strokeWidth="0dp"
app:cardCornerRadius="1dp"
app:cardBackgroundColor="?attr/colorControlNormal"
app:layout_constraintStart_toStartOf="@id/battery_icon"
app:layout_constraintTop_toTopOf="@id/battery_icon"
app:layout_constraintBottom_toBottomOf="@id/battery_icon"/>
</androidx.constraintlayout.widget.ConstraintLayout>
@@ -51,8 +51,11 @@
<item></item> <item></item>
<item>書名</item> <item>書名</item>
<item>標題</item> <item>標題</item>
<item>標題+箭頭</item>
<item>時間</item> <item>時間</item>
<item>電量</item> <item>電量</item>
<item>電量內顯</item>
<item>僅電量圖標</item>
<item>電量%</item> <item>電量%</item>
<item>頁數</item> <item>頁數</item>
<item>進度(%)</item> <item>進度(%)</item>
@@ -88,8 +88,11 @@
<item></item> <item></item>
<item>書名</item> <item>書名</item>
<item>標題</item> <item>標題</item>
<item>標題+箭頭</item>
<item>時間</item> <item>時間</item>
<item>電量</item> <item>電量</item>
<item>電量內顯</item>
<item>僅電量圖標</item>
<item>電量%</item> <item>電量%</item>
<item>頁數</item> <item>頁數</item>
<item>進度(%)</item> <item>進度(%)</item>
+3
View File
@@ -75,8 +75,11 @@
<item></item> <item></item>
<item>书名</item> <item>书名</item>
<item>标题</item> <item>标题</item>
<item>带箭头的标题</item>
<item>时间</item> <item>时间</item>
<item>电量</item> <item>电量</item>
<item>电量内显</item>
<item>仅电量图标</item>
<item>电量%</item> <item>电量%</item>
<item>页数</item> <item>页数</item>
<item>进度(%)</item> <item>进度(%)</item>
+3
View File
@@ -142,8 +142,11 @@
<item>Blank</item> <item>Blank</item>
<item>Book name</item> <item>Book name</item>
<item>Title</item> <item>Title</item>
<item>Title With Arrow</item>
<item>Time</item> <item>Time</item>
<item>Battery</item> <item>Battery</item>
<item>Battery Inside</item>
<item>Battery Only Icon</item>
<item>Battery%</item> <item>Battery%</item>
<item>Pages</item> <item>Pages</item>
<item>Progress(%)</item> <item>Progress(%)</item>
+1 -1
View File
@@ -1,4 +1,4 @@
VERSION_MAJOR=3 VERSION_MAJOR=3
VERSION_MINOR=26 VERSION_MINOR=26
VERSION_PATCH=2 VERSION_PATCH=2
VERSION_SUFFIX=-beta.3 VERSION_SUFFIX=-beta.4