标题分段 #181
This commit is contained in:
@@ -273,6 +273,12 @@ object ReadBookConfig {
|
||||
config.lineSpacingExtra = value
|
||||
}
|
||||
|
||||
var titleLineSpacingExtra: Int
|
||||
get() = config.titleLineSpacingExtra
|
||||
set(value) {
|
||||
config.titleLineSpacingExtra = value
|
||||
}
|
||||
|
||||
var paragraphSpacing: Int
|
||||
get() = config.paragraphSpacing
|
||||
set(value) {
|
||||
@@ -293,6 +299,30 @@ object ReadBookConfig {
|
||||
config.titleSize = value
|
||||
}
|
||||
|
||||
var titleSegType: Int
|
||||
get() = config.titleSegType
|
||||
set(value) {
|
||||
config.titleSegType = value
|
||||
}
|
||||
|
||||
var titleSegScaling: Float
|
||||
get() = config.titleSegScaling
|
||||
set(value) {
|
||||
config.titleSegScaling = value
|
||||
}
|
||||
|
||||
var titleSegDistance: Int
|
||||
get() = config.titleSegDistance
|
||||
set(value) {
|
||||
config.titleSegDistance = value
|
||||
}
|
||||
|
||||
var titleSegFlag: String
|
||||
get() = config.titleSegFlag
|
||||
set(value) {
|
||||
config.titleSegFlag = value
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否标题居中
|
||||
*/
|
||||
@@ -497,6 +527,11 @@ object ReadBookConfig {
|
||||
var titleTopSpacing: Int = 0,
|
||||
var titleBottomSpacing: Int = 0,
|
||||
var titleBold: Int = 500,
|
||||
var titleLineSpacingExtra: Int = 4,
|
||||
var titleSegType: Int = 0,//分段模式
|
||||
var titleSegScaling: Float = 1.2f,//分段缩放,第二段与第一段的字体大小比例
|
||||
var titleSegDistance: Int = 4,//分段判断,第几个字符开始分段
|
||||
var titleSegFlag: String = "",//分段判断,碰到指定值时分段
|
||||
var paragraphIndent: String = " ",//段落缩进
|
||||
var underline: Boolean = false, //下划线
|
||||
var paddingBottom: Int = 6,
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
package io.legado.app.ui.book.read.config
|
||||
|
||||
import android.os.Bundle
|
||||
import android.text.InputType
|
||||
import android.view.View
|
||||
import android.widget.EditText
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import com.google.android.material.chip.Chip
|
||||
import com.jaredrummler.android.colorpicker.ColorPickerDialog
|
||||
import io.legado.app.R
|
||||
import io.legado.app.base.BaseBottomSheetDialogFragment
|
||||
import io.legado.app.constant.EventBus
|
||||
import io.legado.app.databinding.DialogEditTextBinding
|
||||
import io.legado.app.databinding.DialogTipConfigBinding
|
||||
import io.legado.app.help.config.ReadBookConfig
|
||||
import io.legado.app.help.config.ReadTipConfig
|
||||
@@ -15,6 +19,8 @@ import io.legado.app.lib.dialogs.selector
|
||||
import io.legado.app.utils.hexString
|
||||
import io.legado.app.utils.observeEvent
|
||||
import io.legado.app.utils.postEvent
|
||||
import io.legado.app.utils.requestInputMethod
|
||||
import io.legado.app.utils.toastOnUi
|
||||
import io.legado.app.utils.viewbindingdelegate.viewBinding
|
||||
|
||||
|
||||
@@ -74,10 +80,82 @@ class TipConfigDialog : BaseBottomSheetDialogFragment(R.layout.dialog_tip_config
|
||||
addOnChangeListener { _, newValue, _ ->
|
||||
binding.textFontWeightConverter.text = "自定义"
|
||||
ReadBookConfig.titleBold = newValue.toInt()
|
||||
postEvent(EventBus.UP_CONFIG, arrayListOf(8, 9, 6))
|
||||
postEvent(EventBus.UP_CONFIG, arrayListOf(5))
|
||||
}
|
||||
}
|
||||
|
||||
binding.btnTitleSegType.setOnClickListener {
|
||||
val types = arrayOf("不分段", "按字符数分段", "按标志字符串分段")
|
||||
val current = ReadBookConfig.titleSegType
|
||||
|
||||
alert(title = "选择标题分段模式") {
|
||||
singleChoiceItems(types, current) { _, which ->
|
||||
ReadBookConfig.titleSegType = which
|
||||
}
|
||||
positiveButton("确定") {
|
||||
toastOnUi("分段模式已设置为:${types[ReadBookConfig.titleSegType]}")
|
||||
postEvent(EventBus.UP_CONFIG, arrayListOf(5))
|
||||
}
|
||||
negativeButton("取消")
|
||||
}.show()
|
||||
}
|
||||
|
||||
binding.btnTitleSegConfig.setOnClickListener {
|
||||
when (ReadBookConfig.titleSegType) {
|
||||
1 -> { // 按字符数分段
|
||||
alert(title = "设置分段字符数") {
|
||||
val alertBinding = DialogEditTextBinding.inflate(layoutInflater).apply {
|
||||
editView.inputType = InputType.TYPE_CLASS_NUMBER
|
||||
editView.setText(ReadBookConfig.titleSegDistance.toString())
|
||||
editView.hint = "输入分段字符数"
|
||||
}
|
||||
customView { alertBinding.root }
|
||||
|
||||
okButton {
|
||||
val value = alertBinding.editView.text?.toString()?.toIntOrNull()
|
||||
if (value != null && value > 0) {
|
||||
ReadBookConfig.titleSegDistance = value
|
||||
toastOnUi("分段字符数设置为 $value")
|
||||
postEvent(EventBus.UP_CONFIG, arrayListOf(5))
|
||||
} else {
|
||||
toastOnUi("请输入有效数字")
|
||||
}
|
||||
}
|
||||
cancelButton()
|
||||
}.requestInputMethod()
|
||||
}
|
||||
|
||||
2 -> { // 按标志字符串分段
|
||||
alert(title = "设置分段标志") {
|
||||
val alertBinding = DialogEditTextBinding.inflate(layoutInflater).apply {
|
||||
editView.inputType = InputType.TYPE_CLASS_TEXT
|
||||
editView.setText(ReadBookConfig.titleSegFlag)
|
||||
editLayout.hint = "输入多个标志,用英文逗号分隔,例如:章,回,篇"
|
||||
}
|
||||
customView { alertBinding.root }
|
||||
|
||||
okButton {
|
||||
val value = alertBinding.editView.text?.toString()?.trim()
|
||||
if (!value.isNullOrEmpty()) {
|
||||
ReadBookConfig.titleSegFlag = value
|
||||
toastOnUi("分段标志设置为 \"$value\"")
|
||||
postEvent(EventBus.UP_CONFIG, arrayListOf(5))
|
||||
} else {
|
||||
toastOnUi("标志不能为空")
|
||||
}
|
||||
}
|
||||
cancelButton()
|
||||
}.requestInputMethod()
|
||||
}
|
||||
|
||||
else -> {
|
||||
toastOnUi("当前分段模式无需配置参数")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
binding.dsbTitleSegScaling.progress = ReadBookConfig.titleSegScaling.toInt() * 10
|
||||
binding.dsbTitleLineSpacingExtra.progress = ReadBookConfig.titleLineSpacingExtra
|
||||
binding.dsbTitleSize.progress = ReadBookConfig.titleSize
|
||||
binding.dsbTitleTop.progress = ReadBookConfig.titleTopSpacing
|
||||
binding.dsbTitleBottom.progress = ReadBookConfig.titleBottomSpacing
|
||||
@@ -166,6 +244,14 @@ class TipConfigDialog : BaseBottomSheetDialogFragment(R.layout.dialog_tip_config
|
||||
postEvent(EventBus.UP_CONFIG, arrayListOf(5))
|
||||
}
|
||||
}
|
||||
binding.dsbTitleSegScaling.onChanged = {
|
||||
ReadBookConfig.titleSegScaling = it / 10f
|
||||
postEvent(EventBus.UP_CONFIG, arrayListOf(8, 5))
|
||||
}
|
||||
binding.dsbTitleLineSpacingExtra.onChanged = {
|
||||
ReadBookConfig.titleLineSpacingExtra = it
|
||||
postEvent(EventBus.UP_CONFIG, arrayListOf(8, 5))
|
||||
}
|
||||
dsbTitleSize.onChanged = {
|
||||
ReadBookConfig.titleSize = it
|
||||
postEvent(EventBus.UP_CONFIG, arrayListOf(8, 5))
|
||||
|
||||
@@ -97,6 +97,9 @@ object ChapterProvider {
|
||||
var lineSpacingExtra = 0f
|
||||
private set
|
||||
|
||||
var titleLineSpacingExtra = 0f
|
||||
private set
|
||||
|
||||
@JvmStatic
|
||||
var paragraphSpacing = 0
|
||||
private set
|
||||
@@ -861,6 +864,7 @@ object ChapterProvider {
|
||||
}
|
||||
//间距
|
||||
lineSpacingExtra = ReadBookConfig.lineSpacingExtra / 10f
|
||||
titleLineSpacingExtra = ReadBookConfig.titleLineSpacingExtra / 10f
|
||||
paragraphSpacing = ReadBookConfig.paragraphSpacing
|
||||
titleTopSpacing = ReadBookConfig.titleTopSpacing.dpToPx()
|
||||
titleBottomSpacing = ReadBookConfig.titleBottomSpacing.dpToPx()
|
||||
|
||||
@@ -65,6 +65,8 @@ class TextChapterLayout(
|
||||
private val titleTopSpacing = ChapterProvider.titleTopSpacing
|
||||
private val titleBottomSpacing = ChapterProvider.titleBottomSpacing
|
||||
private val lineSpacingExtra = ChapterProvider.lineSpacingExtra
|
||||
private val titleLineSpacingExtra = ChapterProvider.titleLineSpacingExtra
|
||||
|
||||
private val paragraphSpacing = ChapterProvider.paragraphSpacing
|
||||
|
||||
private val visibleHeight = ChapterProvider.visibleHeight
|
||||
@@ -80,6 +82,11 @@ class TextChapterLayout(
|
||||
private val useZhLayout = ReadBookConfig.useZhLayout
|
||||
private val isMiddleTitle = ReadBookConfig.isMiddleTitle
|
||||
private val textFullJustify = ReadBookConfig.textFullJustify
|
||||
private val titleSegType = ReadBookConfig.titleSegType
|
||||
private val titleSegDistance = ReadBookConfig.titleSegDistance
|
||||
private val titleSegFlag = ReadBookConfig.titleSegFlag
|
||||
private val titleSegScaling = ReadBookConfig.titleSegScaling
|
||||
|
||||
|
||||
private var pendingTextPage = TextPage()
|
||||
|
||||
@@ -189,6 +196,48 @@ class TextChapterLayout(
|
||||
}
|
||||
}
|
||||
|
||||
private fun parseTitleSegments(
|
||||
rawTitle: String,
|
||||
segType: Int,
|
||||
segDistance: Int,
|
||||
segFlag: String
|
||||
): List<String> {
|
||||
if (rawTitle.isBlank()) return emptyList()
|
||||
|
||||
return when (segType) {
|
||||
// 0:不分段
|
||||
0 -> listOf(rawTitle)
|
||||
|
||||
// 1:按字符数分段
|
||||
1 -> {
|
||||
if (segDistance <= 0 || segDistance >= rawTitle.length)
|
||||
listOf(rawTitle)
|
||||
else
|
||||
listOf(
|
||||
rawTitle.substring(0, segDistance),
|
||||
rawTitle.substring(segDistance)
|
||||
)
|
||||
}
|
||||
|
||||
// 2:按标志字符串分段
|
||||
2 -> {
|
||||
val flags = segFlag.split(",").map { it.trim() }.filter { it.isNotEmpty() }
|
||||
if (flags.isEmpty()) return listOf(rawTitle)
|
||||
|
||||
val pattern = flags.joinToString("|") { Regex.escape(it) }
|
||||
val regex = Regex("(?<=$pattern)") // 在分隔符后断开
|
||||
val segments = rawTitle.split(regex)
|
||||
.map { it.trim() }
|
||||
.filter { it.isNotEmpty() }
|
||||
|
||||
segments.ifEmpty { listOf(rawTitle) }
|
||||
}
|
||||
|
||||
else -> listOf(rawTitle)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取拆分完的章节数据
|
||||
*/
|
||||
@@ -204,37 +253,48 @@ class TextChapterLayout(
|
||||
val isTextImageStyle = imageStyle.equals(Book.imgStyleText, true)
|
||||
|
||||
if (titleMode != 2 || bookChapter.isVolume || contents.isEmpty()) {
|
||||
//标题非隐藏
|
||||
displayTitle.splitNotBlank("\n").forEach { text ->
|
||||
val srcList = LinkedList<String>()
|
||||
val reviewImg = bookChapter.reviewImg
|
||||
var reviewTxt = ""
|
||||
if (reviewImg != null) {
|
||||
srcList.add(reviewImg)
|
||||
reviewTxt = if (reviewImg.contains("TEXT")) {
|
||||
ChapterProvider.reviewChar
|
||||
} else {
|
||||
ChapterProvider.srcReplaceChar
|
||||
}
|
||||
}
|
||||
setTypeText(
|
||||
book,
|
||||
text + reviewTxt,
|
||||
titlePaint,
|
||||
titlePaintTextHeight,
|
||||
titlePaintFontMetrics,
|
||||
imageStyle,
|
||||
srcList = srcList.ifEmpty { null },
|
||||
isTitle = true,
|
||||
emptyContent = contents.isEmpty(),
|
||||
isVolumeTitle = bookChapter.isVolume
|
||||
val baseTitles = displayTitle.splitNotBlank("\n")
|
||||
baseTitles.forEach { rawTitle ->
|
||||
val titleSegments = parseTitleSegments(
|
||||
rawTitle,
|
||||
titleSegType,
|
||||
titleSegDistance,
|
||||
titleSegFlag
|
||||
)
|
||||
pendingTextPage.lines.last().isParagraphEnd = true
|
||||
stringBuilder.append("\n")
|
||||
titleSegments.forEachIndexed { index, segment ->
|
||||
val srcList = LinkedList<String>()
|
||||
val reviewImg = bookChapter.reviewImg
|
||||
var reviewTxt = ""
|
||||
if (reviewImg != null) {
|
||||
srcList.add(reviewImg)
|
||||
reviewTxt = if (reviewImg.contains("TEXT")) {
|
||||
ChapterProvider.reviewChar
|
||||
} else {
|
||||
ChapterProvider.srcReplaceChar
|
||||
}
|
||||
}
|
||||
val paint = if (index == 0) titlePaint else {
|
||||
TextPaint(titlePaint).apply {
|
||||
textSize = titlePaint.textSize * titleSegScaling
|
||||
}
|
||||
}
|
||||
setTypeText(
|
||||
book,
|
||||
segment + reviewTxt,
|
||||
paint,
|
||||
titlePaintTextHeight,
|
||||
titlePaintFontMetrics,
|
||||
imageStyle,
|
||||
srcList = srcList.ifEmpty { null },
|
||||
isTitle = true,
|
||||
emptyContent = contents.isEmpty(),
|
||||
isVolumeTitle = bookChapter.isVolume
|
||||
)
|
||||
pendingTextPage.lines.last().isParagraphEnd = true
|
||||
stringBuilder.append("\n")
|
||||
}
|
||||
}
|
||||
durY += titleBottomSpacing
|
||||
|
||||
// 如果是单图模式且当前页有内容,强制分页
|
||||
if (isSingleImageStyle && pendingTextPage.lines.isNotEmpty() && contents.isNotEmpty()) {
|
||||
prepareNextPageIfNeed()
|
||||
}
|
||||
@@ -598,7 +658,7 @@ class TextChapterLayout(
|
||||
textLine.upTopBottom(durY, textHeight, fontMetrics)
|
||||
val textPage = pendingTextPage
|
||||
textPage.addLine(textLine)
|
||||
durY += textHeight * lineSpacingExtra
|
||||
durY += textHeight * if (isTitle) titleLineSpacingExtra else lineSpacingExtra
|
||||
if (textPage.height < durY) {
|
||||
textPage.height = durY
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user