This commit is contained in:
Horis 2024-02-25 19:46:40 +08:00
parent 822db28d88
commit 92d6e00ab0
5 changed files with 110 additions and 8 deletions

View File

@ -422,7 +422,7 @@ class ReadBookViewModel(application: Application) : BaseViewModel(application) {
var curLine = curTextLines[lineIndex]
length = length - currentPage.text.length + curLine.text.length
if (curLine.isParagraphEnd) length++
while (length < contentPosition && lineIndex + 1 < curTextLines.size) {
while (length <= contentPosition && lineIndex + 1 < curTextLines.size) {
lineIndex += 1
curLine = curTextLines[lineIndex]
length += curLine.text.length

View File

@ -2,13 +2,18 @@ package io.legado.app.ui.book.read.page.entities
import android.graphics.Canvas
import android.graphics.Paint.FontMetrics
import android.os.Build
import androidx.annotation.Keep
import androidx.core.graphics.withTranslation
import io.legado.app.help.PaintPool
import io.legado.app.help.book.isImage
import io.legado.app.help.config.ReadBookConfig
import io.legado.app.lib.theme.ThemeStore
import io.legado.app.model.ReadBook
import io.legado.app.ui.book.read.page.ContentTextView
import io.legado.app.ui.book.read.page.entities.TextPage.Companion.emptyTextPage
import io.legado.app.ui.book.read.page.entities.column.BaseColumn
import io.legado.app.ui.book.read.page.entities.column.TextColumn
import io.legado.app.ui.book.read.page.provider.ChapterProvider
import io.legado.app.utils.canvasrecorder.CanvasRecorderFactory
import io.legado.app.utils.canvasrecorder.recordIfNeededThenDraw
@ -32,6 +37,13 @@ data class TextLine(
val isTitle: Boolean = false,
var isParagraphEnd: Boolean = false,
var isImage: Boolean = false,
var startX: Float = 0f,
var indentSize: Int = 0,
var extraLetterSpacing: Float = 0f,
var extraLetterSpacingOffsetX: Float = 0f,
var wordSpacing: Float = 0f,
var exceed: Boolean = false,
var onlyTextColumn: Boolean = true,
) {
val columns: List<BaseColumn> get() = textColumns
@ -41,6 +53,7 @@ data class TextLine(
val chapterIndices: IntRange get() = chapterPosition..chapterPosition + charSize
val height: Float inline get() = lineBottom - lineTop
val canvasRecorder = CanvasRecorderFactory.create()
var searchResultColumnCount = 0
var isReadAloud: Boolean = false
set(value) {
if (field != value) {
@ -52,6 +65,9 @@ data class TextLine(
var isLeftLine = true
fun addColumn(column: BaseColumn) {
if (column !is TextColumn) {
onlyTextColumn = false
}
column.textLine = this
textColumns.add(column)
}
@ -129,14 +145,56 @@ data class TextLine(
}
private fun drawTextLine(view: ContentTextView, canvas: Canvas) {
for (i in columns.indices) {
columns[i].draw(view, canvas)
if (checkFastDraw()) {
fastDrawTextLine(view, canvas)
} else {
for (i in columns.indices) {
columns[i].draw(view, canvas)
}
}
if (ReadBookConfig.underline && !isImage && ReadBook.book?.isImage != true) {
drawUnderline(canvas)
}
}
private fun fastDrawTextLine(view: ContentTextView, canvas: Canvas) {
val textPaint = if (isTitle) {
ChapterProvider.titlePaint
} else {
ChapterProvider.contentPaint
}
val textColor = if (isReadAloud) {
ThemeStore.accentColor
} else {
ReadBookConfig.textColor
}
val paint = PaintPool.obtain()
paint.set(textPaint)
if (extraLetterSpacing != 0f) {
paint.letterSpacing += extraLetterSpacing
}
if (wordSpacing != 0f) {
paint.wordSpacing += wordSpacing
}
if (paint.color != textColor) {
paint.color = textColor
}
if (extraLetterSpacingOffsetX != 0f) {
canvas.withTranslation(extraLetterSpacingOffsetX) {
canvas.drawText(text, indentSize, text.length, startX, lineBase - lineTop, paint)
}
} else {
canvas.drawText(text, indentSize, text.length, startX, lineBase - lineTop, paint)
}
PaintPool.recycle(paint)
for (i in columns.indices) {
val column = columns[i] as TextColumn
if (column.selected) {
canvas.drawRect(column.start, 0f, column.end, height, view.selectedPaint)
}
}
}
/**
* 绘制下划线
*/
@ -151,6 +209,16 @@ data class TextLine(
)
}
fun checkFastDraw(): Boolean {
if (exceed || !onlyTextColumn || textPage.isMsgPage) {
return false
}
if (!atLeastApi29 && wordSpacing != 0f) {
return false
}
return searchResultColumnCount == 0
}
fun invalidate() {
invalidateSelf()
textPage.invalidate()
@ -166,6 +234,7 @@ data class TextLine(
companion object {
val emptyTextLine = TextLine()
private val atLeastApi29 = Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q
}
}

View File

@ -1,11 +1,13 @@
package io.legado.app.ui.book.read.page.entities
import android.graphics.Canvas
import android.graphics.Paint
import android.text.Layout
import android.text.StaticLayout
import androidx.annotation.Keep
import androidx.core.graphics.withTranslation
import io.legado.app.R
import io.legado.app.help.PaintPool
import io.legado.app.help.config.ReadBookConfig
import io.legado.app.ui.book.read.page.ContentTextView
import io.legado.app.ui.book.read.page.entities.TextChapter.Companion.emptyTextChapter
@ -13,6 +15,7 @@ import io.legado.app.ui.book.read.page.entities.column.TextColumn
import io.legado.app.ui.book.read.page.provider.ChapterProvider
import io.legado.app.utils.canvasrecorder.CanvasRecorderFactory
import io.legado.app.utils.canvasrecorder.recordIfNeeded
import io.legado.app.utils.dpToPx
import splitties.init.appCtx
import java.text.DecimalFormat
import kotlin.math.min
@ -277,6 +280,21 @@ data class TextPage(
}
}
private fun drawDebugInfo(canvas: Canvas) {
ChapterProvider.run {
val paint = PaintPool.obtain()
paint.style = Paint.Style.STROKE
canvas.drawRect(
paddingLeft.toFloat(),
0f,
(paddingLeft + visibleWidth).toFloat(),
height - 1.dpToPx(),
paint
)
PaintPool.recycle(paint)
}
}
private fun drawPage(view: ContentTextView, canvas: Canvas) {
for (i in lines.indices) {
val line = lines[i]

View File

@ -33,6 +33,11 @@ data class TextColumn(
set(value) {
if (field != value) {
textLine.invalidate()
if (value) {
textLine.searchResultColumnCount++
} else {
textLine.searchResultColumnCount--
}
}
field = value
}

View File

@ -31,6 +31,7 @@ import kotlinx.coroutines.launch
import java.util.LinkedList
import java.util.Locale
import kotlin.coroutines.coroutineContext
import kotlin.math.roundToInt
class TextChapterLayout(
scope: CoroutineScope,
@ -467,7 +468,7 @@ class TextChapterLayout(
//第一行 非标题
textLine.text = lineText
addCharsToLineFirst(
book, absStartX, textLine, words,
book, absStartX, textLine, words, textPaint,
desiredWidth, widths, srcList
)
}
@ -505,7 +506,7 @@ class TextChapterLayout(
//中间行
textLine.text = lineText
addCharsToLineMiddle(
book, absStartX, textLine, words,
book, absStartX, textLine, words, textPaint,
desiredWidth, 0f, widths, srcList
)
}
@ -556,6 +557,7 @@ class TextChapterLayout(
absStartX: Int,
textLine: TextLine,
words: List<String>,
textPaint: TextPaint,
/**自然排版长度**/
desiredWidth: Float,
textWidths: List<Float>,
@ -582,11 +584,12 @@ class TextChapterLayout(
x = x1
textLine.indentWidth = x
}
textLine.indentSize = bodyIndent.length
if (words.size > bodyIndent.length) {
val text1 = words.subList(bodyIndent.length, words.size)
val textWidths1 = textWidths.subList(bodyIndent.length, textWidths.size)
addCharsToLineMiddle(
book, absStartX, textLine, text1,
book, absStartX, textLine, text1, textPaint,
desiredWidth, x, textWidths1, srcList
)
}
@ -600,6 +603,7 @@ class TextChapterLayout(
absStartX: Int,
textLine: TextLine,
words: List<String>,
textPaint: TextPaint,
/**自然排版长度**/
desiredWidth: Float,
/**起始x坐标**/
@ -616,8 +620,10 @@ class TextChapterLayout(
}
val residualWidth = visibleWidth - desiredWidth
val spaceSize = words.count { it == " " }
textLine.startX = absStartX + startX
if (spaceSize > 1) {
val d = residualWidth / spaceSize
val d = residualWidth / (spaceSize - 1)
textLine.wordSpacing = d
var x = startX
for (index in words.indices) {
val char = words[index]
@ -636,6 +642,8 @@ class TextChapterLayout(
} else {
val gapCount: Int = words.lastIndex
val d = residualWidth / gapCount
textLine.extraLetterSpacingOffsetX = -d / 2
textLine.extraLetterSpacing = d / textPaint.textSize
var x = startX
for (index in words.indices) {
val char = words[index]
@ -666,6 +674,7 @@ class TextChapterLayout(
) {
val indentLength = ReadBookConfig.paragraphIndent.length
var x = startX
textLine.startX = absStartX + startX
for (index in words.indices) {
val char = words[index]
val cw = textWidths[index]
@ -727,8 +736,9 @@ class TextChapterLayout(
*/
private fun exceed(absStartX: Int, textLine: TextLine, words: List<String>) {
val visibleEnd = absStartX + visibleWidth
val endX = textLine.columns.lastOrNull()?.end ?: return
val endX = textLine.columns.lastOrNull()?.end?.roundToInt() ?: return
if (endX > visibleEnd) {
textLine.exceed = true
val cc = (endX - visibleEnd) / words.size
for (i in 0..words.lastIndex) {
textLine.getColumnReverseAt(i).let {