增加无极字重切换

This commit is contained in:
HapeLee committed 2025-09-05 03:38:27 +08:00
1 parent cb60443323
commit 158812fd65
3 files changed
+362 -30

No files matched your search

@@ -1,25 +1,32 @@
package io.legato.kazusa.ui.font package io.legato.kazusa.ui.book.read.config
import android.net.Uri //import io.legado.app.lib.theme.primaryColor
import android.content.Context
import android.graphics.Typeface
import android.os.Bundle import android.os.Bundle
import android.view.MenuItem import android.view.MenuItem
import android.view.View import android.view.View
import android.view.ViewGroup
import androidx.appcompat.widget.Toolbar import androidx.appcompat.widget.Toolbar
import androidx.core.net.toUri
import androidx.documentfile.provider.DocumentFile import androidx.documentfile.provider.DocumentFile
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.GridLayoutManager
import io.legato.kazusa.R import io.legato.kazusa.R
import io.legato.kazusa.base.BaseBottomSheetDialogFragment import io.legato.kazusa.base.BaseBottomSheetDialogFragment
import io.legato.kazusa.base.BaseDialogFragment import io.legato.kazusa.base.adapter.ItemViewHolder
import io.legato.kazusa.base.adapter.RecyclerAdapter
import io.legato.kazusa.constant.AppLog import io.legato.kazusa.constant.AppLog
import io.legato.kazusa.constant.EventBus
import io.legato.kazusa.constant.PreferKey import io.legato.kazusa.constant.PreferKey
import io.legato.kazusa.databinding.DialogFontSelectBinding import io.legato.kazusa.databinding.DialogFontSelectBinding
import io.legato.kazusa.databinding.ItemFontBinding
import io.legato.kazusa.help.config.AppConfig import io.legato.kazusa.help.config.AppConfig
import io.legato.kazusa.help.config.ReadBookConfig
import io.legato.kazusa.lib.dialogs.SelectItem import io.legato.kazusa.lib.dialogs.SelectItem
import io.legato.kazusa.lib.dialogs.alert import io.legato.kazusa.lib.dialogs.alert
import io.legato.kazusa.lib.permission.Permissions import io.legato.kazusa.lib.permission.Permissions
import io.legato.kazusa.lib.permission.PermissionsCompat import io.legato.kazusa.lib.permission.PermissionsCompat
//import io.legado.app.lib.theme.primaryColor
import io.legato.kazusa.ui.file.HandleFileContract import io.legato.kazusa.ui.file.HandleFileContract
import io.legato.kazusa.utils.FileDoc import io.legato.kazusa.utils.FileDoc
import io.legato.kazusa.utils.FileUtils import io.legato.kazusa.utils.FileUtils
@@ -28,28 +35,30 @@ import io.legato.kazusa.utils.applyTint
import io.legato.kazusa.utils.cnCompare import io.legato.kazusa.utils.cnCompare
import io.legato.kazusa.utils.externalFiles import io.legato.kazusa.utils.externalFiles
import io.legato.kazusa.utils.getPrefString import io.legato.kazusa.utils.getPrefString
import io.legato.kazusa.utils.invisible
import io.legato.kazusa.utils.isContentScheme import io.legato.kazusa.utils.isContentScheme
import io.legato.kazusa.utils.list import io.legato.kazusa.utils.list
import io.legato.kazusa.utils.listFileDocs import io.legato.kazusa.utils.listFileDocs
import io.legato.kazusa.utils.postEvent
import io.legato.kazusa.utils.printOnDebug
import io.legato.kazusa.utils.putPrefString import io.legato.kazusa.utils.putPrefString
import io.legato.kazusa.utils.setLayout
import io.legato.kazusa.utils.toastOnUi import io.legato.kazusa.utils.toastOnUi
import io.legato.kazusa.utils.viewbindingdelegate.viewBinding import io.legato.kazusa.utils.viewbindingdelegate.viewBinding
import io.legato.kazusa.utils.visible
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import java.io.File import java.io.File
import androidx.core.net.toUri import java.net.URLDecoder
/** /**
* 字体选择对话框 * 字体选择对话框
*/ */
class FontSelectDialog : BaseBottomSheetDialogFragment(R.layout.dialog_font_select), class FontSelectDialog : BaseBottomSheetDialogFragment(R.layout.dialog_font_select),
Toolbar.OnMenuItemClickListener, Toolbar.OnMenuItemClickListener {
FontAdapter.CallBack {
private val fontRegex = Regex("(?i).*\\.[ot]tf") private val fontRegex = Regex("(?i).*\\.[ot]tf")
private val binding by viewBinding(DialogFontSelectBinding::bind) private val binding by viewBinding(DialogFontSelectBinding::bind)
private val adapter by lazy { private val adapter by lazy {
val curFontPath = callBack?.curFontPath ?: "" val curFontPath = callBack?.curFontPath ?: ""
FontAdapter(requireContext(), curFontPath, this) FontAdapter(requireContext(), curFontPath)
} }
private val selectFontDir = registerForActivityResult(HandleFileContract()) { private val selectFontDir = registerForActivityResult(HandleFileContract()) {
it.uri?.let { uri -> it.uri?.let { uri ->
@@ -78,9 +87,17 @@ class FontSelectDialog : BaseBottomSheetDialogFragment(R.layout.dialog_font_sele
binding.toolBar.inflateMenu(R.menu.font_select) binding.toolBar.inflateMenu(R.menu.font_select)
binding.toolBar.menu.applyTint(requireContext()) binding.toolBar.menu.applyTint(requireContext())
binding.toolBar.setOnMenuItemClickListener(this) binding.toolBar.setOnMenuItemClickListener(this)
binding.recyclerView.layoutManager = LinearLayoutManager(context) binding.recyclerView.layoutManager = GridLayoutManager(context, 2)
binding.recyclerView.adapter = adapter binding.recyclerView.adapter = adapter
initView()
upView()
initViewEvent()
}
private fun initView() = binding.run {
val fontPath = getPrefString(PreferKey.fontFolder) val fontPath = getPrefString(PreferKey.fontFolder)
if (fontPath.isNullOrEmpty()) { if (fontPath.isNullOrEmpty()) {
openFolder() openFolder()
@@ -96,6 +113,93 @@ class FontSelectDialog : BaseBottomSheetDialogFragment(R.layout.dialog_font_sele
loadFontFilesByPermission(fontPath) loadFontFilesByPermission(fontPath)
} }
} }
dsbTextLetterSpacing.valueFormat = {
((it - 50) / 100f).toString()
}
dsbLineSize.valueFormat = { ((it - 10) / 10f).toString() }
binding.bgTextIndent.apply {
val currentIndex = ReadBookConfig.paragraphIndent.length.coerceAtMost(childCount - 1)
val defaultButtonId = getChildAt(currentIndex).id
check(defaultButtonId)
addOnButtonCheckedListener { group, checkedId, isChecked ->
if (isChecked) {
val checkedIndex = (0 until group.childCount)
.firstOrNull { group.getChildAt(it).id == checkedId }
?: return@addOnButtonCheckedListener
ReadBookConfig.paragraphIndent = " ".repeat(checkedIndex)
postEvent(EventBus.UP_CONFIG, arrayListOf(8, 5))
}
}
}
val weightOptions = context?.resources?.getStringArray(R.array.text_font_weight)
val weightValues = listOf(500, 700, 300)
val initialIndex = weightValues.indexOf(ReadBookConfig.textBold)
binding.textFontWeightConverter.text = weightOptions?.getOrNull(initialIndex) ?: "自定义"
binding.textFontWeightConverter.setOnClickListener {
context?.alert(titleResource = R.string.text_font_weight_converter) {
weightOptions?.let { options ->
items(options.toList()) { _, i ->
ReadBookConfig.textBold = weightValues[i]
binding.sliderFontWeight.value =
ReadBookConfig.textBold.toFloat().coerceAtLeast(100f)
binding.textFontWeightConverter.text = options.getOrNull(i) ?: ""
postEvent(EventBus.UP_CONFIG, arrayListOf(8, 9, 6))
}
}
}
}
binding.sliderFontWeight.apply {
valueFrom = 100f
valueTo = 900f
stepSize = 10f
value = ReadBookConfig.textBold.toFloat().coerceAtLeast(100f)
addOnChangeListener { _, newValue, _ ->
binding.textFontWeightConverter.text = "自定义"
ReadBookConfig.textBold = newValue.toInt()
postEvent(EventBus.UP_CONFIG, arrayListOf(8, 9, 6))
}
}
}
private fun initViewEvent() = binding.run {
dsbTextLetterSpacing.onChanged = {
ReadBookConfig.letterSpacing = (it - 50) / 100f
postEvent(EventBus.UP_CONFIG, arrayListOf(8, 5))
}
dsbLineSize.onChanged = {
ReadBookConfig.lineSpacingExtra = it
postEvent(EventBus.UP_CONFIG, arrayListOf(8, 5))
}
binding.dsbParagraphSpacing.apply {
valueFrom = 0f
valueTo = 20f
stepSize = 1f
value = ReadBookConfig.paragraphSpacing.toFloat()
textParagraphSpacing.text = (ReadBookConfig.paragraphSpacing.toFloat() / 10f).toString()
addOnChangeListener { slider, newValue, fromUser ->
binding.textParagraphSpacing.text = (newValue / 10f).toString()
if (fromUser) {
ReadBookConfig.paragraphSpacing = newValue.toInt()
postEvent(EventBus.UP_CONFIG, arrayListOf(8, 5))
}
}
}
}
private fun upView() = binding.run {
ReadBookConfig.let {
dsbTextLetterSpacing.progress = (it.letterSpacing * 100).toInt() + 50
dsbLineSize.progress = it.lineSpacingExtra
dsbParagraphSpacing.value = it.paragraphSpacing.toFloat()
}
} }
override fun onMenuItemClick(item: MenuItem?): Boolean { override fun onMenuItemClick(item: MenuItem?): Boolean {
@@ -183,7 +287,7 @@ class FontSelectDialog : BaseBottomSheetDialogFragment(R.layout.dialog_font_sele
} }
} }
override fun onFontSelect(docItem: FileDoc) { fun onFontSelect(docItem: FileDoc) {
execute { execute {
callBack?.selectFont(docItem.toString()) callBack?.selectFont(docItem.toString())
}.onSuccess { }.onSuccess {
@@ -198,6 +302,57 @@ class FontSelectDialog : BaseBottomSheetDialogFragment(R.layout.dialog_font_sele
private val callBack: CallBack? private val callBack: CallBack?
get() = (parentFragment as? CallBack) ?: (activity as? CallBack) get() = (parentFragment as? CallBack) ?: (activity as? CallBack)
inner class FontAdapter(context: Context, curFilePath: String) :
RecyclerAdapter<FileDoc, ItemFontBinding>(context) {
private val curName = runCatching {
URLDecoder.decode(curFilePath, "utf-8")
}.getOrNull()?.substringAfterLast(File.separator)
override fun getViewBinding(parent: ViewGroup): ItemFontBinding {
return ItemFontBinding.inflate(inflater, parent, false)
}
override fun convert(
holder: ItemViewHolder,
binding: ItemFontBinding,
item: FileDoc,
payloads: MutableList<Any>
) {
binding.run {
runCatching {
val typeface: Typeface? = if (item.isContentScheme) {
context.contentResolver
.openFileDescriptor(item.uri, "r")?.use {
Typeface.Builder(it.fileDescriptor).build()
}
} else {
Typeface.createFromFile(item.uri.path!!)
}
tvFont.typeface = typeface
}.onFailure {
it.printOnDebug()
AppLog.put("读取字体 ${item.name} 出错\n${it.localizedMessage}", it, true)
}
tvFont.text = item.name
root.setOnClickListener { onFontSelect(item) }
if (item.name == curName) {
ivChecked.visible()
} else {
ivChecked.invisible()
}
}
}
override fun registerListener(holder: ItemViewHolder, binding: ItemFontBinding) {
holder.itemView.setOnClickListener {
getItem(holder.layoutPosition)?.let {
onFontSelect(it)
}
}
}
}
interface CallBack { interface CallBack {
fun selectFont(path: String) fun selectFont(path: String)
val curFontPath: String val curFontPath: String
@@ -912,24 +912,18 @@ object ChapterProvider {
private fun getPaints(typeface: Typeface?): Pair<TextPaint, TextPaint> { private fun getPaints(typeface: Typeface?): Pair<TextPaint, TextPaint> {
// 字体统一处理 // 字体统一处理
val bold = Typeface.create(typeface, Typeface.BOLD) val (titleFont, textFont) = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
val normal = Typeface.create(typeface, Typeface.NORMAL) Pair(
val (titleFont, textFont) = when (ReadBookConfig.textBold) { Typeface.create(typeface, ReadBookConfig.textBold, false),
1 -> { Typeface.create(typeface, ReadBookConfig.textBold, false)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) )
Pair(Typeface.create(typeface, 900, false), bold) } else {
else val tf = when (ReadBookConfig.textBold) {
Pair(bold, bold) in 700..900 -> Typeface.DEFAULT_BOLD
in 300..499 -> Typeface.DEFAULT
else -> Typeface.DEFAULT
} }
Pair(tf, tf)
2 -> {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P)
Pair(normal, Typeface.create(typeface, 300, false))
else
Pair(normal, normal)
}
else -> Pair(bold, normal)
} }
//标题 //标题
+184 -1
View File
@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical" android:orientation="vertical"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"> android:layout_height="wrap_content">
@@ -10,9 +11,191 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:theme="?attr/actionBarStyle" /> android:theme="?attr/actionBarStyle" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingStart="16dp"
android:paddingEnd="2dp">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/text_font_weight_converter"
android:textSize="12sp"
android:textStyle="bold" />
<com.google.android.material.button.MaterialButton
android:id="@+id/textFontWeightConverter"
style="@style/Widget.Material3Expressive.Button.TonalButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:minWidth="0dp"
android:textSize="12sp" />
<com.google.android.material.slider.Slider
android:id="@+id/sliderFontWeight"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="16dp"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginBottom="4dp"
android:text="@string/text_indent"
android:textSize="12sp"
android:textStyle="bold" />
<com.google.android.material.button.MaterialButtonToggleGroup
android:id="@+id/bgTextIndent"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:spacing="2dp"
app:innerCornerSize="8dp"
app:selectionRequired="true"
app:singleSelection="true">
<com.google.android.material.button.MaterialButton
android:id="@+id/btnIndent0"
style="@style/Widget.Material3Expressive.Button.OutlinedButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="无"
android:textStyle="bold" />
<com.google.android.material.button.MaterialButton
android:id="@+id/btnIndent1"
style="@style/Widget.Material3Expressive.Button.OutlinedButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="1"
android:textStyle="bold" />
<com.google.android.material.button.MaterialButton
android:id="@+id/btnIndent2"
style="@style/Widget.Material3Expressive.Button.OutlinedButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="2"
android:textStyle="bold" />
<com.google.android.material.button.MaterialButton
android:id="@+id/btnIndent3"
style="@style/Widget.Material3Expressive.Button.OutlinedButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="3"
android:textStyle="bold" />
<com.google.android.material.button.MaterialButton
android:id="@+id/btnIndent4"
style="@style/Widget.Material3Expressive.Button.OutlinedButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="4"
android:textStyle="bold" />
</com.google.android.material.button.MaterialButtonToggleGroup>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginVertical="8dp"
android:gravity="center_vertical"
android:orientation="horizontal">
<io.legato.kazusa.ui.widget.SimpleCounterView
android:id="@+id/dsb_line_size"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="32dp"
android:layout_weight="1"
app:isBottomBackground="true"
app:max="20"
app:title="@string/line_size" />
<io.legato.kazusa.ui.widget.SimpleCounterView
android:id="@+id/dsb_text_letter_spacing"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
app:isBottomBackground="true"
app:max="100"
app:title="@string/text_letter_spacing" />
</LinearLayout>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="16dp"
android:layout_marginTop="8dp"
android:orientation="horizontal">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_weight="1"
android:text="@string/text_indent"
android:textSize="12sp"
android:textStyle="bold" />
<com.google.android.material.card.MaterialCardView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:cardBackgroundColor="?attr/colorSurfaceContainer"
app:cardCornerRadius="4dp"
app:strokeWidth="0dp">
<TextView
android:id="@+id/textParagraphSpacing"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:paddingHorizontal="4dp"
android:paddingVertical="2dp"
android:text="@string/text"
android:textSize="12sp"
android:textStyle="bold" />
</com.google.android.material.card.MaterialCardView>
</LinearLayout>
<com.google.android.material.slider.Slider
android:id="@+id/dsb_paragraph_spacing"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="4dp"
android:stepSize="1"
android:valueFrom="0"
android:valueTo="20"
app:isBottomBackground="true"
app:thumbHeight="28dp"
app:title="@string/paragraph_size"
app:trackHeight="28dp" />
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler_view" android:id="@+id/recycler_view"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" /> android:layout_height="match_parent"
android:layout_margin="2dp" />
</LinearLayout> </LinearLayout>