优化
This commit is contained in:
@@ -422,7 +422,7 @@ class ReadBookViewModel(application: Application) : BaseViewModel(application) {
|
|||||||
var curLine = curTextLines[lineIndex]
|
var curLine = curTextLines[lineIndex]
|
||||||
length = length - currentPage.text.length + curLine.text.length
|
length = length - currentPage.text.length + curLine.text.length
|
||||||
if (curLine.isParagraphEnd) length++
|
if (curLine.isParagraphEnd) length++
|
||||||
while (length < contentPosition && lineIndex + 1 < curTextLines.size) {
|
while (length <= contentPosition && lineIndex + 1 < curTextLines.size) {
|
||||||
lineIndex += 1
|
lineIndex += 1
|
||||||
curLine = curTextLines[lineIndex]
|
curLine = curTextLines[lineIndex]
|
||||||
length += curLine.text.length
|
length += curLine.text.length
|
||||||
|
|||||||
@@ -2,13 +2,18 @@ package io.legado.app.ui.book.read.page.entities
|
|||||||
|
|
||||||
import android.graphics.Canvas
|
import android.graphics.Canvas
|
||||||
import android.graphics.Paint.FontMetrics
|
import android.graphics.Paint.FontMetrics
|
||||||
|
import android.os.Build
|
||||||
import androidx.annotation.Keep
|
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.book.isImage
|
||||||
import io.legado.app.help.config.ReadBookConfig
|
import io.legado.app.help.config.ReadBookConfig
|
||||||
|
import io.legado.app.lib.theme.ThemeStore
|
||||||
import io.legado.app.model.ReadBook
|
import io.legado.app.model.ReadBook
|
||||||
import io.legado.app.ui.book.read.page.ContentTextView
|
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.TextPage.Companion.emptyTextPage
|
||||||
import io.legado.app.ui.book.read.page.entities.column.BaseColumn
|
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.ui.book.read.page.provider.ChapterProvider
|
||||||
import io.legado.app.utils.canvasrecorder.CanvasRecorderFactory
|
import io.legado.app.utils.canvasrecorder.CanvasRecorderFactory
|
||||||
import io.legado.app.utils.canvasrecorder.recordIfNeededThenDraw
|
import io.legado.app.utils.canvasrecorder.recordIfNeededThenDraw
|
||||||
@@ -32,6 +37,13 @@ data class TextLine(
|
|||||||
val isTitle: Boolean = false,
|
val isTitle: Boolean = false,
|
||||||
var isParagraphEnd: Boolean = false,
|
var isParagraphEnd: Boolean = false,
|
||||||
var isImage: 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
|
val columns: List<BaseColumn> get() = textColumns
|
||||||
@@ -41,6 +53,7 @@ data class TextLine(
|
|||||||
val chapterIndices: IntRange get() = chapterPosition..chapterPosition + charSize
|
val chapterIndices: IntRange get() = chapterPosition..chapterPosition + charSize
|
||||||
val height: Float inline get() = lineBottom - lineTop
|
val height: Float inline get() = lineBottom - lineTop
|
||||||
val canvasRecorder = CanvasRecorderFactory.create()
|
val canvasRecorder = CanvasRecorderFactory.create()
|
||||||
|
var searchResultColumnCount = 0
|
||||||
var isReadAloud: Boolean = false
|
var isReadAloud: Boolean = false
|
||||||
set(value) {
|
set(value) {
|
||||||
if (field != value) {
|
if (field != value) {
|
||||||
@@ -52,6 +65,9 @@ data class TextLine(
|
|||||||
var isLeftLine = true
|
var isLeftLine = true
|
||||||
|
|
||||||
fun addColumn(column: BaseColumn) {
|
fun addColumn(column: BaseColumn) {
|
||||||
|
if (column !is TextColumn) {
|
||||||
|
onlyTextColumn = false
|
||||||
|
}
|
||||||
column.textLine = this
|
column.textLine = this
|
||||||
textColumns.add(column)
|
textColumns.add(column)
|
||||||
}
|
}
|
||||||
@@ -129,14 +145,56 @@ data class TextLine(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun drawTextLine(view: ContentTextView, canvas: Canvas) {
|
private fun drawTextLine(view: ContentTextView, canvas: Canvas) {
|
||||||
for (i in columns.indices) {
|
if (checkFastDraw()) {
|
||||||
columns[i].draw(view, canvas)
|
fastDrawTextLine(view, canvas)
|
||||||
|
} else {
|
||||||
|
for (i in columns.indices) {
|
||||||
|
columns[i].draw(view, canvas)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (ReadBookConfig.underline && !isImage && ReadBook.book?.isImage != true) {
|
if (ReadBookConfig.underline && !isImage && ReadBook.book?.isImage != true) {
|
||||||
drawUnderline(canvas)
|
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() {
|
fun invalidate() {
|
||||||
invalidateSelf()
|
invalidateSelf()
|
||||||
textPage.invalidate()
|
textPage.invalidate()
|
||||||
@@ -166,6 +234,7 @@ data class TextLine(
|
|||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val emptyTextLine = TextLine()
|
val emptyTextLine = TextLine()
|
||||||
|
private val atLeastApi29 = Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
package io.legado.app.ui.book.read.page.entities
|
package io.legado.app.ui.book.read.page.entities
|
||||||
|
|
||||||
import android.graphics.Canvas
|
import android.graphics.Canvas
|
||||||
|
import android.graphics.Paint
|
||||||
import android.text.Layout
|
import android.text.Layout
|
||||||
import android.text.StaticLayout
|
import android.text.StaticLayout
|
||||||
import androidx.annotation.Keep
|
import androidx.annotation.Keep
|
||||||
import androidx.core.graphics.withTranslation
|
import androidx.core.graphics.withTranslation
|
||||||
import io.legado.app.R
|
import io.legado.app.R
|
||||||
|
import io.legado.app.help.PaintPool
|
||||||
import io.legado.app.help.config.ReadBookConfig
|
import io.legado.app.help.config.ReadBookConfig
|
||||||
import io.legado.app.ui.book.read.page.ContentTextView
|
import io.legado.app.ui.book.read.page.ContentTextView
|
||||||
import io.legado.app.ui.book.read.page.entities.TextChapter.Companion.emptyTextChapter
|
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.ui.book.read.page.provider.ChapterProvider
|
||||||
import io.legado.app.utils.canvasrecorder.CanvasRecorderFactory
|
import io.legado.app.utils.canvasrecorder.CanvasRecorderFactory
|
||||||
import io.legado.app.utils.canvasrecorder.recordIfNeeded
|
import io.legado.app.utils.canvasrecorder.recordIfNeeded
|
||||||
|
import io.legado.app.utils.dpToPx
|
||||||
import splitties.init.appCtx
|
import splitties.init.appCtx
|
||||||
import java.text.DecimalFormat
|
import java.text.DecimalFormat
|
||||||
import kotlin.math.min
|
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) {
|
private fun drawPage(view: ContentTextView, canvas: Canvas) {
|
||||||
for (i in lines.indices) {
|
for (i in lines.indices) {
|
||||||
val line = lines[i]
|
val line = lines[i]
|
||||||
|
|||||||
@@ -33,6 +33,11 @@ data class TextColumn(
|
|||||||
set(value) {
|
set(value) {
|
||||||
if (field != value) {
|
if (field != value) {
|
||||||
textLine.invalidate()
|
textLine.invalidate()
|
||||||
|
if (value) {
|
||||||
|
textLine.searchResultColumnCount++
|
||||||
|
} else {
|
||||||
|
textLine.searchResultColumnCount--
|
||||||
|
}
|
||||||
}
|
}
|
||||||
field = value
|
field = value
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ import kotlinx.coroutines.launch
|
|||||||
import java.util.LinkedList
|
import java.util.LinkedList
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
import kotlin.coroutines.coroutineContext
|
import kotlin.coroutines.coroutineContext
|
||||||
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
class TextChapterLayout(
|
class TextChapterLayout(
|
||||||
scope: CoroutineScope,
|
scope: CoroutineScope,
|
||||||
@@ -467,7 +468,7 @@ class TextChapterLayout(
|
|||||||
//第一行 非标题
|
//第一行 非标题
|
||||||
textLine.text = lineText
|
textLine.text = lineText
|
||||||
addCharsToLineFirst(
|
addCharsToLineFirst(
|
||||||
book, absStartX, textLine, words,
|
book, absStartX, textLine, words, textPaint,
|
||||||
desiredWidth, widths, srcList
|
desiredWidth, widths, srcList
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -505,7 +506,7 @@ class TextChapterLayout(
|
|||||||
//中间行
|
//中间行
|
||||||
textLine.text = lineText
|
textLine.text = lineText
|
||||||
addCharsToLineMiddle(
|
addCharsToLineMiddle(
|
||||||
book, absStartX, textLine, words,
|
book, absStartX, textLine, words, textPaint,
|
||||||
desiredWidth, 0f, widths, srcList
|
desiredWidth, 0f, widths, srcList
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -556,6 +557,7 @@ class TextChapterLayout(
|
|||||||
absStartX: Int,
|
absStartX: Int,
|
||||||
textLine: TextLine,
|
textLine: TextLine,
|
||||||
words: List<String>,
|
words: List<String>,
|
||||||
|
textPaint: TextPaint,
|
||||||
/**自然排版长度**/
|
/**自然排版长度**/
|
||||||
desiredWidth: Float,
|
desiredWidth: Float,
|
||||||
textWidths: List<Float>,
|
textWidths: List<Float>,
|
||||||
@@ -582,11 +584,12 @@ class TextChapterLayout(
|
|||||||
x = x1
|
x = x1
|
||||||
textLine.indentWidth = x
|
textLine.indentWidth = x
|
||||||
}
|
}
|
||||||
|
textLine.indentSize = bodyIndent.length
|
||||||
if (words.size > bodyIndent.length) {
|
if (words.size > bodyIndent.length) {
|
||||||
val text1 = words.subList(bodyIndent.length, words.size)
|
val text1 = words.subList(bodyIndent.length, words.size)
|
||||||
val textWidths1 = textWidths.subList(bodyIndent.length, textWidths.size)
|
val textWidths1 = textWidths.subList(bodyIndent.length, textWidths.size)
|
||||||
addCharsToLineMiddle(
|
addCharsToLineMiddle(
|
||||||
book, absStartX, textLine, text1,
|
book, absStartX, textLine, text1, textPaint,
|
||||||
desiredWidth, x, textWidths1, srcList
|
desiredWidth, x, textWidths1, srcList
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -600,6 +603,7 @@ class TextChapterLayout(
|
|||||||
absStartX: Int,
|
absStartX: Int,
|
||||||
textLine: TextLine,
|
textLine: TextLine,
|
||||||
words: List<String>,
|
words: List<String>,
|
||||||
|
textPaint: TextPaint,
|
||||||
/**自然排版长度**/
|
/**自然排版长度**/
|
||||||
desiredWidth: Float,
|
desiredWidth: Float,
|
||||||
/**起始x坐标**/
|
/**起始x坐标**/
|
||||||
@@ -616,8 +620,10 @@ class TextChapterLayout(
|
|||||||
}
|
}
|
||||||
val residualWidth = visibleWidth - desiredWidth
|
val residualWidth = visibleWidth - desiredWidth
|
||||||
val spaceSize = words.count { it == " " }
|
val spaceSize = words.count { it == " " }
|
||||||
|
textLine.startX = absStartX + startX
|
||||||
if (spaceSize > 1) {
|
if (spaceSize > 1) {
|
||||||
val d = residualWidth / spaceSize
|
val d = residualWidth / (spaceSize - 1)
|
||||||
|
textLine.wordSpacing = d
|
||||||
var x = startX
|
var x = startX
|
||||||
for (index in words.indices) {
|
for (index in words.indices) {
|
||||||
val char = words[index]
|
val char = words[index]
|
||||||
@@ -636,6 +642,8 @@ class TextChapterLayout(
|
|||||||
} else {
|
} else {
|
||||||
val gapCount: Int = words.lastIndex
|
val gapCount: Int = words.lastIndex
|
||||||
val d = residualWidth / gapCount
|
val d = residualWidth / gapCount
|
||||||
|
textLine.extraLetterSpacingOffsetX = -d / 2
|
||||||
|
textLine.extraLetterSpacing = d / textPaint.textSize
|
||||||
var x = startX
|
var x = startX
|
||||||
for (index in words.indices) {
|
for (index in words.indices) {
|
||||||
val char = words[index]
|
val char = words[index]
|
||||||
@@ -666,6 +674,7 @@ class TextChapterLayout(
|
|||||||
) {
|
) {
|
||||||
val indentLength = ReadBookConfig.paragraphIndent.length
|
val indentLength = ReadBookConfig.paragraphIndent.length
|
||||||
var x = startX
|
var x = startX
|
||||||
|
textLine.startX = absStartX + startX
|
||||||
for (index in words.indices) {
|
for (index in words.indices) {
|
||||||
val char = words[index]
|
val char = words[index]
|
||||||
val cw = textWidths[index]
|
val cw = textWidths[index]
|
||||||
@@ -727,8 +736,9 @@ class TextChapterLayout(
|
|||||||
*/
|
*/
|
||||||
private fun exceed(absStartX: Int, textLine: TextLine, words: List<String>) {
|
private fun exceed(absStartX: Int, textLine: TextLine, words: List<String>) {
|
||||||
val visibleEnd = absStartX + visibleWidth
|
val visibleEnd = absStartX + visibleWidth
|
||||||
val endX = textLine.columns.lastOrNull()?.end ?: return
|
val endX = textLine.columns.lastOrNull()?.end?.roundToInt() ?: return
|
||||||
if (endX > visibleEnd) {
|
if (endX > visibleEnd) {
|
||||||
|
textLine.exceed = true
|
||||||
val cc = (endX - visibleEnd) / words.size
|
val cc = (endX - visibleEnd) / words.size
|
||||||
for (i in 0..words.lastIndex) {
|
for (i in 0..words.lastIndex) {
|
||||||
textLine.getColumnReverseAt(i).let {
|
textLine.getColumnReverseAt(i).let {
|
||||||
|
|||||||
Reference in New Issue
Block a user