Horis 2 лет назад
Родитель
Сommit
097265dab1

+ 1 - 0
app/src/main/java/io/legado/app/constant/PageAnim.kt

@@ -2,6 +2,7 @@ package io.legado.app.constant
 
 import androidx.annotation.IntDef
 
+@Suppress("ConstPropertyName")
 object PageAnim {
 
     const val coverPageAnim = 0

+ 73 - 28
app/src/main/java/io/legado/app/model/ReadBook.kt

@@ -1,6 +1,7 @@
 package io.legado.app.model
 
 import io.legado.app.constant.AppLog
+import io.legado.app.constant.PageAnim.scrollPageAnim
 import io.legado.app.data.appDb
 import io.legado.app.data.entities.Book
 import io.legado.app.data.entities.BookChapter
@@ -32,6 +33,7 @@ import kotlinx.coroutines.cancelChildren
 import kotlinx.coroutines.delay
 import kotlinx.coroutines.launch
 import splitties.init.appCtx
+import kotlin.math.max
 import kotlin.math.min
 
 
@@ -241,7 +243,6 @@ object ReadBook : CoroutineScope by MainScope() {
             callBack?.upMenuView()
             AppLog.putDebug("moveToNextChapter-curPageChanged()")
             curPageChanged()
-            curTextChapter?.let { callBack?.onCurrentTextChapterChanged(it, upContent) }
             return true
         } else {
             AppLog.putDebug("跳转下一章失败,没有下一章")
@@ -271,7 +272,6 @@ object ReadBook : CoroutineScope by MainScope() {
             saveRead()
             callBack?.upMenuView()
             curPageChanged()
-            curTextChapter?.let { callBack?.onCurrentTextChapterChanged(it, upContent) }
             return true
         } else {
             return false
@@ -321,7 +321,7 @@ object ReadBook : CoroutineScope by MainScope() {
      */
     private fun curPageChanged(pageChanged: Boolean = false) {
         callBack?.pageChanged()
-        if (BaseReadAloudService.isRun) {
+        if (BaseReadAloudService.isRun && isLayoutAvailable) {
             val scrollPageAnim = pageAnim() == 3
             if (scrollPageAnim && pageChanged) {
                 ReadAloud.pause(appCtx)
@@ -350,6 +350,13 @@ object ReadBook : CoroutineScope by MainScope() {
             return curTextChapter?.getPageIndexByCharIndex(durChapterPos) ?: durChapterPos
         }
 
+    /**
+     * 是否排版到了当前阅读位置
+     */
+    val isLayoutAvailable inline get() = durPageIndex >= 0
+
+    val isScroll inline get() = pageAnim() == scrollPageAnim
+
     /**
      * chapterOnDur: 0为当前页,1为下一页,-1为上一页
      */
@@ -522,7 +529,8 @@ object ReadBook : CoroutineScope by MainScope() {
                 .getContent(book, chapter, content, includeTitle = false)
             val textChapter =
                 ChapterProvider.getTextChapterAsync(chapter, displayTitle, contents, chapterSize)
-            when (val offset = chapter.index - durChapterIndex) {
+            val offset = chapter.index - durChapterIndex
+            when (offset) {
                 0 -> {
                     curTextChapter?.cancelLayout()
                     curTextChapter = textChapter
@@ -530,44 +538,83 @@ object ReadBook : CoroutineScope by MainScope() {
                         callBack?.resetPageOffset()
                     }
                     callBack?.upMenuView()
-                    curPageChanged()
-                    callBack?.contentLoadFinish()
-                    callBack?.onCurrentTextChapterChanged(textChapter, upContent)
+                    textChapter.setProgressListener(object : LayoutProgressListener {
+                        var available = false
+
+                        override fun onLayoutPageCompleted(index: Int, page: TextPage) {
+                            if (!available && page.containPos(durChapterPos)) {
+                                curPageChanged()
+                                callBack?.contentLoadFinish()
+                                if (upContent) {
+                                    callBack?.upContent(offset, resetPageOffset)
+                                }
+                                available = true
+                            }
+                            if (upContent && isScroll) {
+                                val pageIndex = durPageIndex
+                                if (max(index - 3, 0) < pageIndex) {
+                                    callBack?.upContent(offset, resetPageOffset)
+                                }
+                            }
+                            callBack?.onLayoutPageCompleted(index, page)
+                        }
+
+                        override fun onLayoutCompleted() {
+                            if (upContent) callBack?.upContent(offset, resetPageOffset)
+                            success?.invoke()
+                            callBack?.onLayoutCompleted()
+                        }
+
+                        override fun onLayoutException(e: Throwable) {
+                            AppLog.put("ChapterProvider ERROR", e)
+                            appCtx.toastOnUi("ChapterProvider ERROR:\n${e.stackTraceStr}")
+                            callBack?.onLayoutException(e)
+                        }
+                    })
                 }
 
                 -1 -> {
                     prevTextChapter?.cancelLayout()
                     prevTextChapter = textChapter
-                    if (upContent) {
-                        textChapter.setProgressListener(object : LayoutProgressListener {
-                            override fun onLayoutCompleted() {
-                                callBack?.upContent(offset, resetPageOffset)
-                            }
-                        })
-                    }
+                    textChapter.setProgressListener(object : LayoutProgressListener {
+                        override fun onLayoutCompleted() {
+                            if (upContent) callBack?.upContent(offset, resetPageOffset)
+                            success?.invoke()
+                        }
+
+                        override fun onLayoutException(e: Throwable) {
+                            AppLog.put("ChapterProvider ERROR", e)
+                            appCtx.toastOnUi("ChapterProvider ERROR:\n${e.stackTraceStr}")
+                        }
+                    })
                 }
 
                 1 -> {
                     nextTextChapter?.cancelLayout()
                     nextTextChapter = textChapter
-                    if (upContent) {
-                        textChapter.setProgressListener(object : LayoutProgressListener {
-                            override fun onLayoutPageCompleted(index: Int, page: TextPage) {
-                                if (index > 1) {
-                                    return
-                                }
-                                callBack?.upContent(offset, resetPageOffset)
+                    textChapter.setProgressListener(object : LayoutProgressListener {
+                        override fun onLayoutPageCompleted(index: Int, page: TextPage) {
+                            if (index > 1) {
+                                return
                             }
-                        })
-                    }
+                            if (upContent) callBack?.upContent(offset, resetPageOffset)
+                        }
+
+                        override fun onLayoutCompleted() {
+                            success?.invoke()
+                        }
+
+                        override fun onLayoutException(e: Throwable) {
+                            AppLog.put("ChapterProvider ERROR", e)
+                            appCtx.toastOnUi("ChapterProvider ERROR:\n${e.stackTraceStr}")
+                        }
+                    })
                 }
             }
             textChapter.createLayout(this@ReadBook, book, contents)
         }.onError {
             AppLog.put("ChapterProvider ERROR", it)
             appCtx.toastOnUi("ChapterProvider ERROR:\n${it.stackTraceStr}")
-        }.onSuccess {
-            success?.invoke()
         }
     }
 
@@ -679,7 +726,7 @@ object ReadBook : CoroutineScope by MainScope() {
         curTextChapter?.cancelLayout()
     }
 
-    interface CallBack {
+    interface CallBack : LayoutProgressListener {
         fun upMenuView()
 
         fun loadChapterList(book: Book)
@@ -698,8 +745,6 @@ object ReadBook : CoroutineScope by MainScope() {
 
         fun notifyBookChanged()
 
-        fun onCurrentTextChapterChanged(textChapter: TextChapter, upContent: Boolean = true)
-
         fun resetPageOffset()
     }
 

+ 0 - 39
app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt

@@ -77,7 +77,6 @@ import io.legado.app.ui.book.read.config.TipConfigDialog.Companion.TIP_DIVIDER_C
 import io.legado.app.ui.book.read.page.ContentTextView
 import io.legado.app.ui.book.read.page.ReadView
 import io.legado.app.ui.book.read.page.entities.PageDirection
-import io.legado.app.ui.book.read.page.entities.TextChapter
 import io.legado.app.ui.book.read.page.entities.TextPage
 import io.legado.app.ui.book.read.page.provider.ChapterProvider
 import io.legado.app.ui.book.read.page.provider.LayoutProgressListener
@@ -115,7 +114,6 @@ import io.legado.app.utils.observeEvent
 import io.legado.app.utils.observeEventSticky
 import io.legado.app.utils.postEvent
 import io.legado.app.utils.showDialogFragment
-import io.legado.app.utils.stackTraceStr
 import io.legado.app.utils.startActivity
 import io.legado.app.utils.sysScreenOffTime
 import io.legado.app.utils.throttle
@@ -126,7 +124,6 @@ import kotlinx.coroutines.Job
 import kotlinx.coroutines.delay
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.withContext
-import kotlin.math.max
 
 /**
  * 阅读界面
@@ -1373,47 +1370,11 @@ class ReadBookActivity : BaseReadBookActivity(),
         binding.readView.autoPager.resume()
     }
 
-    override fun onCurrentTextChapterChanged(textChapter: TextChapter, upContent: Boolean) {
-        this.upContent = upContent
-        textChapter.setProgressListener(this)
-    }
-
     override fun onLayoutPageCompleted(index: Int, page: TextPage) {
         upSeekBarThrottle.invoke()
-        if (upContent) {
-            val durChapterPos = ReadBook.durChapterPos
-            if (page.containPos(durChapterPos)) {
-                runOnUiThread {
-                    binding.readView.upContent(0, resetPageOffset = false)
-                }
-            }
-            if (isScroll) {
-                val pageIndex = ReadBook.durPageIndex
-                if (max(index - 3, 0) < pageIndex) {
-                    runOnUiThread {
-                        binding.readView.upContent(0, resetPageOffset = false)
-                    }
-                }
-            }
-        }
         binding.readView.onLayoutPageCompleted(index, page)
     }
 
-    override fun onLayoutCompleted() {
-        if (upContent) {
-            runOnUiThread {
-                binding.readView.upContent(0, resetPageOffset = false)
-            }
-        }
-        binding.readView.onLayoutCompleted()
-    }
-
-    override fun onLayoutException(e: Throwable) {
-        AppLog.put("ChapterProvider ERROR", e)
-        toastOnUi("ChapterProvider ERROR:\n${e.stackTraceStr}")
-        binding.readView.onLayoutException(e)
-    }
-
     override fun resetPageOffset() {
         binding.readView.resetPageOffset()
     }

+ 3 - 8
app/src/main/java/io/legado/app/ui/book/read/page/entities/TextChapter.kt

@@ -96,8 +96,7 @@ data class TextChapter(
     }
 
     fun isLastIndexCurrent(index: Int): Boolean {
-        // 未完成排版时,最后一页是正在排版中的,需要去掉
-        return index >= if (isCompleted) pages.size - 1 else pages.size - 2
+        return index >= pages.size - 1
     }
 
     /**
@@ -212,16 +211,12 @@ data class TextChapter(
         if (pageSize == 0) {
             return -1
         }
-        val size = if (isCompleted) pageSize else pageSize - 1
-        val bIndex = pages.fastBinarySearchBy(charIndex, 0, size) {
+        val bIndex = pages.fastBinarySearchBy(charIndex, 0, pageSize) {
             it.lines.first().chapterPosition
         }
         val index = abs(bIndex + 1) - 1
-        if (index == -1) {
-            return -1
-        }
         // 判断是否已经排版到 charIndex ,没有则返回 -1
-        if (!isCompleted && index == size - 1) {
+        if (!isCompleted && index == pageSize - 1) {
             val page = pages[index]
             val line = page.lines.first()
             val pageEndPos = line.chapterPosition + page.charSize

+ 23 - 20
app/src/main/java/io/legado/app/ui/book/read/page/provider/TextChapterLayout.kt

@@ -67,6 +67,8 @@ class TextChapterLayout(
     private val indentCharWidth = ChapterProvider.indentCharWidth
     private val stringBuilder = StringBuilder()
 
+    private var pendingTextPage = TextPage()
+
     private var isCompleted = false
     private val job: Coroutine<*>
     private val bookChapter inline get() = textChapter.chapter
@@ -167,7 +169,6 @@ class TextChapterLayout(
         val contents = bookContent.textList
         var absStartX = paddingLeft
         var durY = 0f
-        textPages.add(TextPage())
         if (ReadBookConfig.titleMode != 2 || bookChapter.isVolume) {
             //标题非隐藏
             displayTitle.splitNotBlank("\n").forEach { text ->
@@ -185,7 +186,7 @@ class TextChapterLayout(
                     durY = it.second
                 }
             }
-            textPages.last().lines.last().isParagraphEnd = true
+            pendingTextPage.lines.last().isParagraphEnd = true
             stringBuilder.append("\n")
             durY += titleBottomSpacing
         }
@@ -244,7 +245,6 @@ class TextChapterLayout(
                         matcher.group(1)!!,
                         absStartX,
                         durY,
-                        textPages,
                         contentPaintTextHeight,
                         stringBuilder,
                         book.getImageStyle()
@@ -270,10 +270,11 @@ class TextChapterLayout(
                     }
                 }
             }
-            textPages.last().lines.last().isParagraphEnd = true
+            pendingTextPage.lines.last().isParagraphEnd = true
             stringBuilder.append("\n")
         }
-        val textPage = textPages.last()
+        textPages.add(pendingTextPage)
+        val textPage = pendingTextPage
         val endPadding = 20.dpToPx()
         val durYPadding = durY + endPadding
         if (textPage.height < durYPadding) {
@@ -295,7 +296,6 @@ class TextChapterLayout(
         src: String,
         x: Int,
         y: Float,
-        textPages: ArrayList<TextPage>,
         textHeight: Float,
         stringBuilder: StringBuilder,
         imageStyle: String?,
@@ -305,15 +305,16 @@ class TextChapterLayout(
         val size = ImageProvider.getImageSize(book, src, ReadBook.bookSource)
         if (size.width > 0 && size.height > 0) {
             if (durY > visibleHeight) {
-                val textPage = textPages.last()
+                val textPage = pendingTextPage
                 if (textPage.height < durY) {
                     textPage.height = durY
                 }
                 textPage.text = stringBuilder.toString().ifEmpty { "本页无文字内容" }
                 stringBuilder.clear()
+                textPages.add(textPage)
                 coroutineContext.ensureActive()
                 onPageCompleted()
-                textPages.add(TextPage())
+                pendingTextPage = TextPage()
                 durY = 0f
             }
             var height = size.height
@@ -334,7 +335,7 @@ class TextChapterLayout(
                         height = visibleHeight
                     }
                     if (durY + height > visibleHeight) {
-                        val textPage = textPages.last()
+                        val textPage = pendingTextPage
                         if (doublePage && absStartX < viewWidth / 2) {
                             //当前页面左列结束
                             textPage.leftLineSize = textPage.lineSize
@@ -346,9 +347,10 @@ class TextChapterLayout(
                             }
                             textPage.text = stringBuilder.toString().ifEmpty { "本页无文字内容" }
                             stringBuilder.clear()
+                            textPages.add(textPage)
                             coroutineContext.ensureActive()
                             onPageCompleted()
-                            textPages.add(TextPage())
+                            pendingTextPage = TextPage()
                         }
                         // 双页的 durY 不正确,可能会小于实际高度
                         if (textPage.height < durY) {
@@ -373,7 +375,7 @@ class TextChapterLayout(
             )
             calcTextLinePosition(textPages, textLine, stringBuilder.length)
             stringBuilder.append(" ") // 确保翻页时索引计算正确
-            textPages.last().addLine(textLine)
+            pendingTextPage.addLine(textLine)
         }
         return absStartX to durY + textHeight * paragraphSpacing / 10f
     }
@@ -403,8 +405,8 @@ class TextChapterLayout(
         }
         var durY = when {
             //标题y轴居中
-            emptyContent && textPages.size == 1 -> {
-                val textPage = textPages.last()
+            emptyContent && textPages.isEmpty() -> {
+                val textPage = pendingTextPage
                 if (textPage.lineSize == 0) {
                     val ty = (visibleHeight - layout.lineCount * textHeight) / 2
                     if (ty > titleTopSpacing) ty else titleTopSpacing.toFloat()
@@ -423,7 +425,7 @@ class TextChapterLayout(
                 }
             }
 
-            isTitle && textPages.size == 1 && textPages.last().lines.isEmpty() ->
+            isTitle && textPages.isEmpty() && pendingTextPage.lines.isEmpty() ->
                 y + titleTopSpacing
 
             else -> y
@@ -431,7 +433,7 @@ class TextChapterLayout(
         for (lineIndex in 0 until layout.lineCount) {
             val textLine = TextLine(isTitle = isTitle)
             if (durY + textHeight > visibleHeight) {
-                val textPage = textPages.last()
+                val textPage = pendingTextPage
                 if (doublePage && absStartX < viewWidth / 2) {
                     //当前页面左列结束
                     textPage.leftLineSize = textPage.lineSize
@@ -442,10 +444,11 @@ class TextChapterLayout(
                         textPage.leftLineSize = textPage.lineSize
                     }
                     textPage.text = stringBuilder.toString()
+                    textPages.add(textPage)
                     coroutineContext.ensureActive()
                     onPageCompleted()
                     //新建页面
-                    textPages.add(TextPage())
+                    pendingTextPage = TextPage()
                     stringBuilder.clear()
                     absStartX = paddingLeft
                 }
@@ -514,7 +517,7 @@ class TextChapterLayout(
             calcTextLinePosition(textPages, textLine, stringBuilder.length)
             stringBuilder.append(lineText)
             textLine.upTopBottom(durY, textHeight, fontMetrics)
-            val textPage = textPages.last()
+            val textPage = pendingTextPage
             textPage.addLine(textLine)
             durY += textHeight * lineSpacingExtra
             if (textPage.height < durY) {
@@ -530,8 +533,8 @@ class TextChapterLayout(
         textLine: TextLine,
         sbLength: Int
     ) {
-        val lastLine = textPages.last().lines.lastOrNull { it.paragraphNum > 0 }
-            ?: textPages.getOrNull(textPages.lastIndex - 1)?.lines?.lastOrNull { it.paragraphNum > 0 }
+        val lastLine = pendingTextPage.lines.lastOrNull { it.paragraphNum > 0 }
+            ?: textPages.lastOrNull()?.lines?.lastOrNull { it.paragraphNum > 0 }
         val paragraphNum = when {
             lastLine == null -> 1
             lastLine.isParagraphEnd -> lastLine.paragraphNum + 1
@@ -539,7 +542,7 @@ class TextChapterLayout(
         }
         textLine.paragraphNum = paragraphNum
         textLine.chapterPosition =
-            (textPages.getOrNull(textPages.lastIndex - 1)?.lines?.lastOrNull()?.run {
+            (textPages.lastOrNull()?.lines?.lastOrNull()?.run {
                 chapterPosition + charSize + if (isParagraphEnd) 1 else 0
             } ?: 0) + sbLength
         textLine.pagePosition = sbLength