From 92d6e00ab0eeb9e7de2d4ee5c6b0eedec434f875 Mon Sep 17 00:00:00 2001 From: Horis <821938089@qq.com> Date: Sun, 25 Feb 2024 19:46:40 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/ui/book/read/ReadBookViewModel.kt | 2 +- .../ui/book/read/page/entities/TextLine.kt | 73 ++++++++++++++++++- .../ui/book/read/page/entities/TextPage.kt | 18 +++++ .../read/page/entities/column/TextColumn.kt | 5 ++ .../read/page/provider/TextChapterLayout.kt | 20 +++-- 5 files changed, 110 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/io/legado/app/ui/book/read/ReadBookViewModel.kt b/app/src/main/java/io/legado/app/ui/book/read/ReadBookViewModel.kt index d6d78c7cd..4426655e6 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/ReadBookViewModel.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/ReadBookViewModel.kt @@ -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 diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/entities/TextLine.kt b/app/src/main/java/io/legado/app/ui/book/read/page/entities/TextLine.kt index 2adc81ea6..7d2ab8e68 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/page/entities/TextLine.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/page/entities/TextLine.kt @@ -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 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 } } diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/entities/TextPage.kt b/app/src/main/java/io/legado/app/ui/book/read/page/entities/TextPage.kt index a2d374d7a..07877065f 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/page/entities/TextPage.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/page/entities/TextPage.kt @@ -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] diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/entities/column/TextColumn.kt b/app/src/main/java/io/legado/app/ui/book/read/page/entities/column/TextColumn.kt index d96de4ce2..cf010a08c 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/page/entities/column/TextColumn.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/page/entities/column/TextColumn.kt @@ -33,6 +33,11 @@ data class TextColumn( set(value) { if (field != value) { textLine.invalidate() + if (value) { + textLine.searchResultColumnCount++ + } else { + textLine.searchResultColumnCount-- + } } field = value } diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/provider/TextChapterLayout.kt b/app/src/main/java/io/legado/app/ui/book/read/page/provider/TextChapterLayout.kt index af21d4721..4d097077e 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/page/provider/TextChapterLayout.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/page/provider/TextChapterLayout.kt @@ -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, + textPaint: TextPaint, /**自然排版长度**/ desiredWidth: Float, textWidths: List, @@ -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, + 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) { 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 {