mirror of
https://github.com/gedoor/legado.git
synced 2025-08-10 00:52:30 +00:00
优化
Some checks failed
Test Build / prepare (push) Waiting to run
Test Build / build (app, release) (push) Blocked by required conditions
Test Build / build (app, releaseA) (push) Blocked by required conditions
Test Build / prerelease (push) Blocked by required conditions
Test Build / lanzou (push) Blocked by required conditions
Test Build / test_Branch (push) Blocked by required conditions
Test Build / telegram (push) Blocked by required conditions
closeStaleIssue / stale (push) Has been cancelled
Some checks failed
Test Build / prepare (push) Waiting to run
Test Build / build (app, release) (push) Blocked by required conditions
Test Build / build (app, releaseA) (push) Blocked by required conditions
Test Build / prerelease (push) Blocked by required conditions
Test Build / lanzou (push) Blocked by required conditions
Test Build / test_Branch (push) Blocked by required conditions
Test Build / telegram (push) Blocked by required conditions
closeStaleIssue / stale (push) Has been cancelled
This commit is contained in:
@@ -35,7 +35,8 @@ data class TextPage(
|
||||
var chapterSize: Int = 0,
|
||||
var chapterIndex: Int = 0,
|
||||
var height: Float = 0f,
|
||||
var leftLineSize: Int = 0
|
||||
var leftLineSize: Int = 0,
|
||||
var renderHeight: Int = 0
|
||||
) {
|
||||
|
||||
companion object {
|
||||
@@ -332,8 +333,7 @@ data class TextPage(
|
||||
|
||||
fun render(view: ContentTextView): Boolean {
|
||||
if (!isCompleted) return false
|
||||
val height = lines.lastOrNull()?.lineBottom?.toInt() ?: 0
|
||||
return canvasRecorder.recordIfNeeded(view.width, height) {
|
||||
return canvasRecorder.recordIfNeeded(view.width, renderHeight) {
|
||||
drawPage(view, this)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,6 +86,9 @@ class TextChapterLayout(
|
||||
|
||||
var channel = Channel<TextPage>(Int.MAX_VALUE)
|
||||
|
||||
var durY = 0f
|
||||
var absStartX = paddingLeft
|
||||
|
||||
init {
|
||||
job = Coroutine.async(
|
||||
scope,
|
||||
@@ -127,7 +130,7 @@ class TextChapterLayout(
|
||||
}
|
||||
|
||||
private fun onPageCompleted() {
|
||||
val textPage = textPages.last()
|
||||
val textPage = pendingTextPage
|
||||
textPage.index = textPages.lastIndex
|
||||
textPage.chapterIndex = bookChapter.index
|
||||
textPage.chapterSize = chaptersSize
|
||||
@@ -137,6 +140,8 @@ class TextChapterLayout(
|
||||
textPage.isCompleted = true
|
||||
textPage.textChapter = textChapter
|
||||
textPage.upLinesPosition()
|
||||
textPage.renderHeight = textPage.lines.maxOf { it.lineBottom.toInt() }
|
||||
textPages.add(textPage)
|
||||
channel.trySend(textPage)
|
||||
try {
|
||||
listener?.onLayoutPageCompleted(textPages.lastIndex, textPage)
|
||||
@@ -184,16 +189,15 @@ class TextChapterLayout(
|
||||
bookContent: BookContent,
|
||||
) {
|
||||
val contents = bookContent.textList
|
||||
var absStartX = paddingLeft
|
||||
var durY = 0f
|
||||
val imageStyle = book.getImageStyle()
|
||||
val isSingleImageStyle = imageStyle.equals(Book.imgStyleSingle, true)
|
||||
val isTextImageStyle = imageStyle.equals(Book.imgStyleText, true)
|
||||
|
||||
if (ReadBookConfig.titleMode != 2 || bookChapter.isVolume || contents.isEmpty()) {
|
||||
//标题非隐藏
|
||||
displayTitle.splitNotBlank("\n").forEach { text ->
|
||||
setTypeText(
|
||||
book, absStartX, durY,
|
||||
book,
|
||||
if (AppConfig.enableReview) text + ChapterProvider.reviewChar else text,
|
||||
titlePaint,
|
||||
titlePaintTextHeight,
|
||||
@@ -202,10 +206,7 @@ class TextChapterLayout(
|
||||
isTitle = true,
|
||||
emptyContent = contents.isEmpty(),
|
||||
isVolumeTitle = bookChapter.isVolume
|
||||
).let {
|
||||
absStartX = it.first
|
||||
durY = it.second
|
||||
}
|
||||
)
|
||||
}
|
||||
pendingTextPage.lines.last().isParagraphEnd = true
|
||||
stringBuilder.append("\n")
|
||||
@@ -213,19 +214,15 @@ class TextChapterLayout(
|
||||
|
||||
// 如果是单图模式且当前页有内容,强制分页
|
||||
if (isSingleImageStyle && pendingTextPage.lines.isNotEmpty()) {
|
||||
pendingTextPage.height = durY
|
||||
textPages.add(pendingTextPage)
|
||||
onPageCompleted()
|
||||
pendingTextPage = TextPage()
|
||||
stringBuilder.clear()
|
||||
absStartX = paddingLeft
|
||||
durY = 0f
|
||||
prepareNextPageIfNeed()
|
||||
}
|
||||
}
|
||||
|
||||
val sb = StringBuffer()
|
||||
var isSetTypedImage = false
|
||||
contents.forEach { content ->
|
||||
coroutineContext.ensureActive()
|
||||
if (book.getImageStyle().equals(Book.imgStyleText, true)) {
|
||||
if (isTextImageStyle) {
|
||||
//图片样式为文字嵌入类型
|
||||
var text = content.replace(ChapterProvider.srcReplaceChar, "▣")
|
||||
val srcList = LinkedList<String>()
|
||||
@@ -241,19 +238,18 @@ class TextChapterLayout(
|
||||
text = sb.toString()
|
||||
setTypeText(
|
||||
book,
|
||||
absStartX,
|
||||
durY,
|
||||
text,
|
||||
contentPaint,
|
||||
contentPaintTextHeight,
|
||||
contentPaintFontMetrics,
|
||||
book.getImageStyle(),
|
||||
srcList = srcList
|
||||
).let {
|
||||
absStartX = it.first
|
||||
durY = it.second
|
||||
}
|
||||
)
|
||||
} else {
|
||||
if (isSingleImageStyle && isSetTypedImage) {
|
||||
isSetTypedImage = false
|
||||
prepareNextPageIfNeed()
|
||||
}
|
||||
val matcher = AppPattern.imgPattern.matcher(content)
|
||||
var start = 0
|
||||
while (matcher.find()) {
|
||||
@@ -262,57 +258,45 @@ class TextChapterLayout(
|
||||
if (text.isNotBlank()) {
|
||||
setTypeText(
|
||||
book,
|
||||
absStartX,
|
||||
durY,
|
||||
text,
|
||||
contentPaint,
|
||||
contentPaintTextHeight,
|
||||
contentPaintFontMetrics,
|
||||
book.getImageStyle(),
|
||||
isFirstLine = start == 0
|
||||
).let {
|
||||
absStartX = it.first
|
||||
durY = it.second
|
||||
}
|
||||
)
|
||||
}
|
||||
setTypeImage(
|
||||
book,
|
||||
matcher.group(1)!!,
|
||||
absStartX,
|
||||
durY,
|
||||
contentPaintTextHeight,
|
||||
stringBuilder,
|
||||
book.getImageStyle()
|
||||
).let {
|
||||
absStartX = it.first
|
||||
durY = it.second
|
||||
}
|
||||
)
|
||||
isSetTypedImage = true
|
||||
start = matcher.end()
|
||||
}
|
||||
if (start < content.length) {
|
||||
if (isSingleImageStyle && isSetTypedImage) {
|
||||
isSetTypedImage = false
|
||||
prepareNextPageIfNeed()
|
||||
}
|
||||
val text = content.substring(start, content.length)
|
||||
if (text.isNotBlank()) {
|
||||
setTypeText(
|
||||
book, absStartX, durY,
|
||||
book,
|
||||
if (AppConfig.enableReview) text + ChapterProvider.reviewChar else text,
|
||||
contentPaint,
|
||||
contentPaintTextHeight,
|
||||
contentPaintFontMetrics,
|
||||
book.getImageStyle(),
|
||||
isFirstLine = start == 0
|
||||
).let {
|
||||
absStartX = it.first
|
||||
durY = it.second
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (pendingTextPage.lines.isNotEmpty()) {
|
||||
pendingTextPage.lines.last().isParagraphEnd = true
|
||||
}
|
||||
pendingTextPage.lines.last().isParagraphEnd = true
|
||||
stringBuilder.append("\n")
|
||||
}
|
||||
textPages.add(pendingTextPage)
|
||||
val textPage = pendingTextPage
|
||||
val endPadding = 20.dpToPx()
|
||||
val durYPadding = durY + endPadding
|
||||
@@ -333,29 +317,12 @@ class TextChapterLayout(
|
||||
private suspend fun setTypeImage(
|
||||
book: Book,
|
||||
src: String,
|
||||
x: Int,
|
||||
y: Float,
|
||||
textHeight: Float,
|
||||
stringBuilder: StringBuilder,
|
||||
imageStyle: String?,
|
||||
): Pair<Int, Float> {
|
||||
var absStartX = x
|
||||
var durY = y
|
||||
) {
|
||||
val size = ImageProvider.getImageSize(book, src, ReadBook.bookSource)
|
||||
if (size.width > 0 && size.height > 0) {
|
||||
if (durY > visibleHeight) {
|
||||
val textPage = pendingTextPage
|
||||
if (textPage.height < durY) {
|
||||
textPage.height = durY
|
||||
}
|
||||
textPage.text = stringBuilder.toString().ifEmpty { "本页无文字内容" }
|
||||
stringBuilder.clear()
|
||||
textPages.add(textPage)
|
||||
coroutineContext.ensureActive()
|
||||
onPageCompleted()
|
||||
pendingTextPage = TextPage()
|
||||
durY = 0f
|
||||
}
|
||||
prepareNextPageIfNeed(durY)
|
||||
var height = size.height
|
||||
var width = size.width
|
||||
when (imageStyle?.uppercase()) {
|
||||
@@ -372,17 +339,7 @@ class TextChapterLayout(
|
||||
height = visibleHeight
|
||||
}
|
||||
if (durY > 0f) {
|
||||
val textPage = pendingTextPage
|
||||
if (textPage.height < durY) {
|
||||
textPage.height = durY
|
||||
}
|
||||
textPage.text = stringBuilder.toString().ifEmpty { "本页无文字内容" }
|
||||
stringBuilder.clear()
|
||||
textPages.add(textPage)
|
||||
coroutineContext.ensureActive()
|
||||
onPageCompleted()
|
||||
pendingTextPage = TextPage()
|
||||
durY = 0f
|
||||
prepareNextPageIfNeed()
|
||||
}
|
||||
|
||||
// 图片竖直方向居中:调整 Y 坐标
|
||||
@@ -401,30 +358,7 @@ class TextChapterLayout(
|
||||
width = width * visibleHeight / height
|
||||
height = visibleHeight
|
||||
}
|
||||
if (durY + height > visibleHeight) {
|
||||
val textPage = pendingTextPage
|
||||
// 双页的 durY 不正确,可能会小于实际高度
|
||||
if (textPage.height < durY) {
|
||||
textPage.height = durY
|
||||
}
|
||||
if (doublePage && absStartX < viewWidth / 2) {
|
||||
//当前页面左列结束
|
||||
textPage.leftLineSize = textPage.lineSize
|
||||
absStartX = viewWidth / 2 + paddingLeft
|
||||
} else {
|
||||
//当前页面结束
|
||||
if (textPage.leftLineSize == 0) {
|
||||
textPage.leftLineSize = textPage.lineSize
|
||||
}
|
||||
textPage.text = stringBuilder.toString().ifEmpty { "本页无文字内容" }
|
||||
stringBuilder.clear()
|
||||
textPages.add(textPage)
|
||||
coroutineContext.ensureActive()
|
||||
onPageCompleted()
|
||||
pendingTextPage = TextPage()
|
||||
}
|
||||
durY = 0f
|
||||
}
|
||||
prepareNextPageIfNeed(durY + height)
|
||||
}
|
||||
}
|
||||
val textLine = TextLine(isImage = true)
|
||||
@@ -439,13 +373,13 @@ class TextChapterLayout(
|
||||
Pair(0f, width.toFloat())
|
||||
}
|
||||
textLine.addColumn(
|
||||
ImageColumn(start = x + start, end = x + end, src = src)
|
||||
ImageColumn(start = absStartX + start, end = absStartX + end, src = src)
|
||||
)
|
||||
calcTextLinePosition(textPages, textLine, stringBuilder.length)
|
||||
stringBuilder.append(" ") // 确保翻页时索引计算正确
|
||||
pendingTextPage.addLine(textLine)
|
||||
}
|
||||
return absStartX to durY + textHeight * paragraphSpacing / 10f
|
||||
durY += textHeight * paragraphSpacing / 10f
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -454,8 +388,6 @@ class TextChapterLayout(
|
||||
@Suppress("DEPRECATION")
|
||||
private suspend fun setTypeText(
|
||||
book: Book,
|
||||
x: Int,
|
||||
y: Float,
|
||||
text: String,
|
||||
textPaint: TextPaint,
|
||||
textHeight: Float,
|
||||
@@ -466,8 +398,7 @@ class TextChapterLayout(
|
||||
emptyContent: Boolean = false,
|
||||
isVolumeTitle: Boolean = false,
|
||||
srcList: LinkedList<String>? = null
|
||||
): Pair<Int, Float> {
|
||||
var absStartX = x
|
||||
) {
|
||||
val widthsArray = FloatArray(text.length)
|
||||
textPaint.getTextWidths(text, widthsArray)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) {
|
||||
@@ -485,7 +416,7 @@ class TextChapterLayout(
|
||||
} else {
|
||||
StaticLayout(text, textPaint, visibleWidth, Layout.Alignment.ALIGN_NORMAL, 0f, 0f, true)
|
||||
}
|
||||
var durY = when {
|
||||
durY = when {
|
||||
//标题y轴居中
|
||||
emptyContent && textPages.isEmpty() -> {
|
||||
val textPage = pendingTextPage
|
||||
@@ -503,7 +434,7 @@ class TextChapterLayout(
|
||||
it.lineBase -= textLayoutHeight
|
||||
it.lineBottom -= textLayoutHeight
|
||||
}
|
||||
y - textLayoutHeight
|
||||
durY - textLayoutHeight
|
||||
}
|
||||
}
|
||||
|
||||
@@ -514,39 +445,15 @@ class TextChapterLayout(
|
||||
if (ty > titleTopSpacing) ty else titleTopSpacing.toFloat()
|
||||
}
|
||||
|
||||
else -> y + titleTopSpacing
|
||||
else -> durY + titleTopSpacing
|
||||
}
|
||||
}
|
||||
|
||||
else -> y
|
||||
else -> durY
|
||||
}
|
||||
for (lineIndex in 0 until layout.lineCount) {
|
||||
val textLine = TextLine(isTitle = isTitle)
|
||||
if (durY + textHeight > visibleHeight) {
|
||||
val textPage = pendingTextPage
|
||||
if (textPage.height < durY) {
|
||||
textPage.height = durY
|
||||
}
|
||||
if (doublePage && absStartX < viewWidth / 2) {
|
||||
//当前页面左列结束
|
||||
textPage.leftLineSize = textPage.lineSize
|
||||
absStartX = viewWidth / 2 + paddingLeft
|
||||
} else {
|
||||
//当前页面结束,设置各种值
|
||||
if (textPage.leftLineSize == 0) {
|
||||
textPage.leftLineSize = textPage.lineSize
|
||||
}
|
||||
textPage.text = stringBuilder.toString()
|
||||
textPages.add(textPage)
|
||||
coroutineContext.ensureActive()
|
||||
onPageCompleted()
|
||||
//新建页面
|
||||
pendingTextPage = TextPage()
|
||||
stringBuilder.clear()
|
||||
absStartX = paddingLeft
|
||||
}
|
||||
durY = 0f
|
||||
}
|
||||
prepareNextPageIfNeed(durY + textHeight)
|
||||
val lineStart = layout.getLineStart(lineIndex)
|
||||
val lineEnd = layout.getLineEnd(lineIndex)
|
||||
val lineText = text.substring(lineStart, lineEnd)
|
||||
@@ -615,7 +522,6 @@ class TextChapterLayout(
|
||||
}
|
||||
}
|
||||
durY += textHeight * paragraphSpacing / 10f
|
||||
return Pair(absStartX, durY)
|
||||
}
|
||||
|
||||
private fun calcTextLinePosition(
|
||||
@@ -850,6 +756,34 @@ class TextChapterLayout(
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun prepareNextPageIfNeed(requestHeight: Float = -1f) {
|
||||
if (requestHeight > visibleHeight || requestHeight == -1f) {
|
||||
val textPage = pendingTextPage
|
||||
// 双页的 durY 不正确,可能会小于实际高度
|
||||
if (textPage.height < durY) {
|
||||
textPage.height = durY
|
||||
}
|
||||
if (doublePage && absStartX < viewWidth / 2) {
|
||||
//当前页面左列结束
|
||||
textPage.leftLineSize = textPage.lineSize
|
||||
absStartX = viewWidth / 2 + paddingLeft
|
||||
} else {
|
||||
//当前页面结束,设置各种值
|
||||
if (textPage.leftLineSize == 0) {
|
||||
textPage.leftLineSize = textPage.lineSize
|
||||
}
|
||||
textPage.text = stringBuilder.toString()
|
||||
coroutineContext.ensureActive()
|
||||
onPageCompleted()
|
||||
//新建页面
|
||||
pendingTextPage = TextPage()
|
||||
stringBuilder.clear()
|
||||
absStartX = paddingLeft
|
||||
}
|
||||
durY = 0f
|
||||
}
|
||||
}
|
||||
|
||||
private fun measureTextSplit(
|
||||
text: String,
|
||||
widthsArray: FloatArray,
|
||||
|
||||
Reference in New Issue
Block a user