diff --git a/app/src/main/java/io/legado/app/help/config/AppConfig.kt b/app/src/main/java/io/legado/app/help/config/AppConfig.kt index 1ce8d8a80..9563699b8 100644 --- a/app/src/main/java/io/legado/app/help/config/AppConfig.kt +++ b/app/src/main/java/io/legado/app/help/config/AppConfig.kt @@ -137,6 +137,9 @@ object AppConfig : SharedPreferences.OnSharedPreferenceChangeListener { } } + val textSelectAble: Boolean + get() = appCtx.getPrefBoolean(PreferKey.textSelectAble, true) + val isTransparentStatusBar: Boolean get() = appCtx.getPrefBoolean(PreferKey.transparentStatusBar, true) diff --git a/app/src/main/java/io/legado/app/help/coroutine/Coroutine.kt b/app/src/main/java/io/legado/app/help/coroutine/Coroutine.kt index 250ac5b51..78fbd70cc 100644 --- a/app/src/main/java/io/legado/app/help/coroutine/Coroutine.kt +++ b/app/src/main/java/io/legado/app/help/coroutine/Coroutine.kt @@ -1,5 +1,6 @@ package io.legado.app.help.coroutine +import android.os.Looper import io.legado.app.utils.printOnDebug import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CompletionHandler @@ -15,6 +16,7 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.plus import kotlinx.coroutines.withContext import kotlinx.coroutines.withTimeout +import java.util.concurrent.Executors import kotlin.coroutines.CoroutineContext /** @@ -33,6 +35,9 @@ class Coroutine( companion object { private val DEFAULT = MainScope() + private val launchExecutor = Executors.newSingleThreadExecutor() + private val mainThread = Looper.getMainLooper().thread + private val isMainThread inline get() = mainThread === Thread.currentThread() fun async( scope: CoroutineScope = DEFAULT, @@ -46,7 +51,7 @@ class Coroutine( } - private val job: Job + private val job: Job by lazy { executeInternal(context, block) } private var start: VoidCallback? = null private var success: Callback? = null @@ -67,7 +72,13 @@ class Coroutine( get() = job.isCompleted init { - this.job = executeInternal(context, block) + if (context == Dispatchers.Main.immediate && isMainThread) { + job + } else { + launchExecutor.execute { + job + } + } } fun timeout(timeMillis: () -> Long): Coroutine { diff --git a/app/src/main/java/io/legado/app/model/ReadBook.kt b/app/src/main/java/io/legado/app/model/ReadBook.kt index 20bb7f48a..ada00455d 100644 --- a/app/src/main/java/io/legado/app/model/ReadBook.kt +++ b/app/src/main/java/io/legado/app/model/ReadBook.kt @@ -275,7 +275,7 @@ object ReadBook : CoroutineScope by MainScope() { textChapter.getPage(index - 2)?.recyclePictures() } if (index < pageIndex) { - textChapter.getPage(index + 2)?.recyclePictures() + textChapter.getPage(index + 3)?.recyclePictures() } } durChapterPos = curTextChapter?.getReadLength(index) ?: index diff --git a/app/src/main/java/io/legado/app/ui/book/read/BaseReadBookActivity.kt b/app/src/main/java/io/legado/app/ui/book/read/BaseReadBookActivity.kt index a7735129b..8496ae091 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/BaseReadBookActivity.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/BaseReadBookActivity.kt @@ -58,6 +58,12 @@ abstract class BaseReadBookActivity : override val binding by viewBinding(ActivityBookReadBinding::inflate) override val viewModel by viewModels() var bottomDialog = 0 + set(value) { + if (field != value) { + field = value + onBottomDialogChange() + } + } private val selectBookFolderResult = registerForActivityResult(HandleFileContract()) { it.uri?.let { uri -> ReadBook.book?.let { book -> @@ -94,6 +100,21 @@ abstract class BaseReadBookActivity : } } + private fun onBottomDialogChange() { + when (bottomDialog) { + 0 -> onMenuHide() + 1 -> onMenuShow() + } + } + + open fun onMenuShow() { + + } + + open fun onMenuHide() { + + } + fun showPaddingConfig() { showDialogFragment() } diff --git a/app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt b/app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt index 83cd8fbb1..ac05cbb3b 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt @@ -94,7 +94,6 @@ import io.legado.app.utils.ACache import io.legado.app.utils.Debounce import io.legado.app.utils.LogUtils import io.legado.app.utils.StartActivityContract -import io.legado.app.utils.SyncedRenderer import io.legado.app.utils.applyOpenTint import io.legado.app.utils.buildMainHandler import io.legado.app.utils.getPrefBoolean @@ -201,8 +200,7 @@ class ReadBookActivity : BaseReadBookActivity(), } override val isInitFinish: Boolean get() = viewModel.isInitFinish override val isScroll: Boolean get() = binding.readView.isScroll - override var autoPageProgress = 0 - override var isAutoPage = false + private val isAutoPage get() = binding.readView.isAutoPage override var isShowingSearchResult = false override var isSelectingSearchResult = false set(value) { @@ -220,8 +218,6 @@ class ReadBookActivity : BaseReadBookActivity(), private var bookChanged = false private var pageChanged = false private var reloadContent = false - private val autoPageRenderer by lazy { SyncedRenderer { doAutoPage(it) } } - private var autoPageScrollOffset = 0.0 private val handler by lazy { buildMainHandler() } private val screenOffRunnable by lazy { Runnable { keepScreenOn(false) } } @@ -934,6 +930,7 @@ class ReadBookActivity : BaseReadBookActivity(), ReadAloud.upTtsProgress(this) } loadStates = true + binding.readView.onContentLoadFinish() } /** @@ -945,9 +942,6 @@ class ReadBookActivity : BaseReadBookActivity(), success: (() -> Unit)? ) { lifecycleScope.launch { - if (relativePosition == 0) { - autoPageProgress = 0 - } binding.readView.upContent(relativePosition, resetPageOffset) upSeekBarProgress() loadStates = false @@ -970,8 +964,7 @@ class ReadBookActivity : BaseReadBookActivity(), */ override fun pageChanged() { pageChanged = true - lifecycleScope.launch { - autoPageProgress = 0 + handler.post { upSeekBarProgress() startBackupJob() } @@ -1053,8 +1046,7 @@ class ReadBookActivity : BaseReadBookActivity(), if (isAutoPage) { autoPageStop() } else { - isAutoPage = true - autoPagePlus() + binding.readView.autoPager.start() binding.readMenu.setAutoPage(true) screenTimeOut = -1L screenOffTimerStart() @@ -1063,53 +1055,12 @@ class ReadBookActivity : BaseReadBookActivity(), override fun autoPageStop() { if (isAutoPage) { - isAutoPage = false - autoPageRenderer.stop() - binding.readView.invalidate() - binding.readView.clearNextPageBitmap() + binding.readView.autoPager.stop() binding.readMenu.setAutoPage(false) upScreenTimeOut() } } - private fun autoPagePlus() { - autoPageProgress = 0 - autoPageScrollOffset = 0.0 - autoPageRenderer.start() - } - - private fun doAutoPage(frameTime: Double) { - if (menuLayoutIsVisible) { - return - } - if (binding.readView.run { isScroll && pageDelegate?.isRunning == true }) { - return - } - val readTime = ReadBookConfig.autoReadSpeed * 1000.0 - val height = binding.readView.height - autoPageScrollOffset += height / readTime * frameTime - if (autoPageScrollOffset < 1) { - return - } - val scrollOffset = autoPageScrollOffset.toInt() - autoPageScrollOffset -= scrollOffset - if (binding.readView.isScroll) { - binding.readView.curPage.scroll(-scrollOffset) - } else { - autoPageProgress += scrollOffset - if (autoPageProgress >= height) { - autoPageProgress = 0 - if (!binding.readView.fillPage(PageDirection.NEXT)) { - autoPageStop() - } else { - binding.readView.clearNextPageBitmap() - } - } else { - binding.readView.invalidate() - } - } - } - override fun openSourceEditActivity() { ReadBook.bookSource?.let { sourceEditActivity.launch { @@ -1414,6 +1365,14 @@ class ReadBookActivity : BaseReadBookActivity(), skipToSearch(searchResult) } + override fun onMenuShow() { + binding.readView.autoPager.pause() + } + + override fun onMenuHide() { + binding.readView.autoPager.resume() + } + /* 全文搜索跳转 */ private fun skipToSearch(searchResult: SearchResult) { val previousResult = binding.searchMenu.previousSearchResult diff --git a/app/src/main/java/io/legado/app/ui/book/read/ReadMenu.kt b/app/src/main/java/io/legado/app/ui/book/read/ReadMenu.kt index bca34bf8b..5e163456c 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/ReadMenu.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/ReadMenu.kt @@ -27,13 +27,38 @@ import io.legado.app.help.config.LocalConfig import io.legado.app.help.config.ReadBookConfig import io.legado.app.help.config.ThemeConfig import io.legado.app.lib.dialogs.alert -import io.legado.app.lib.theme.* +import io.legado.app.lib.theme.Selector +import io.legado.app.lib.theme.accentColor +import io.legado.app.lib.theme.bottomBackground +import io.legado.app.lib.theme.buttonDisabledColor +import io.legado.app.lib.theme.getPrimaryTextColor +import io.legado.app.lib.theme.primaryColor +import io.legado.app.lib.theme.primaryTextColor import io.legado.app.model.ReadBook import io.legado.app.ui.book.info.BookInfoActivity import io.legado.app.ui.browser.WebViewActivity import io.legado.app.ui.widget.seekbar.SeekBarChangeListener -import io.legado.app.utils.* -import splitties.views.* +import io.legado.app.utils.ColorUtils +import io.legado.app.utils.ConstraintModify +import io.legado.app.utils.activity +import io.legado.app.utils.dpToPx +import io.legado.app.utils.getPrefBoolean +import io.legado.app.utils.gone +import io.legado.app.utils.invisible +import io.legado.app.utils.loadAnimation +import io.legado.app.utils.modifyBegin +import io.legado.app.utils.navigationBarGravity +import io.legado.app.utils.navigationBarHeight +import io.legado.app.utils.openUrl +import io.legado.app.utils.putPrefBoolean +import io.legado.app.utils.startActivity +import io.legado.app.utils.visible +import splitties.views.bottomPadding +import splitties.views.leftPadding +import splitties.views.onClick +import splitties.views.onLongClick +import splitties.views.padding +import splitties.views.rightPadding /** * 阅读界面菜单 @@ -273,6 +298,7 @@ class ReadMenu @JvmOverloads constructor( } fun runMenuIn(anim: Boolean = !AppConfig.isEInkMode) { + callBack.onMenuShow() this.visible() binding.titleBar.visible() binding.bottomMenu.visible() @@ -286,6 +312,7 @@ class ReadMenu @JvmOverloads constructor( } fun runMenuOut(anim: Boolean = !AppConfig.isEInkMode, onMenuOutEnd: (() -> Unit)? = null) { + callBack.onMenuHide() this.onMenuOutEnd = onMenuOutEnd if (this.isVisible) { if (anim) { @@ -558,6 +585,8 @@ class ReadMenu @JvmOverloads constructor( fun payAction() fun disableSource() fun skipToChapter(index: Int) + fun onMenuShow() + fun onMenuHide() } } diff --git a/app/src/main/java/io/legado/app/ui/book/read/SearchMenu.kt b/app/src/main/java/io/legado/app/ui/book/read/SearchMenu.kt index e3f0eeade..6edf40f80 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/SearchMenu.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/SearchMenu.kt @@ -18,7 +18,13 @@ import io.legado.app.lib.theme.bottomBackground import io.legado.app.lib.theme.getPrimaryTextColor import io.legado.app.model.ReadBook import io.legado.app.ui.book.searchContent.SearchResult -import io.legado.app.utils.* +import io.legado.app.utils.ColorUtils +import io.legado.app.utils.activity +import io.legado.app.utils.invisible +import io.legado.app.utils.loadAnimation +import io.legado.app.utils.navigationBarGravity +import io.legado.app.utils.navigationBarHeight +import io.legado.app.utils.visible import splitties.views.bottomPadding import splitties.views.leftPadding import splitties.views.padding @@ -235,6 +241,8 @@ class SearchMenu @JvmOverloads constructor( fun exitSearchMenu() fun showMenuBar() fun navigateToSearch(searchResult: SearchResult, index: Int) + fun onMenuShow() + fun onMenuHide() } } diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/AutoPager.kt b/app/src/main/java/io/legado/app/ui/book/read/page/AutoPager.kt new file mode 100644 index 000000000..494abec34 --- /dev/null +++ b/app/src/main/java/io/legado/app/ui/book/read/page/AutoPager.kt @@ -0,0 +1,146 @@ +package io.legado.app.ui.book.read.page + +import android.graphics.Bitmap +import android.graphics.Canvas +import android.graphics.Paint +import android.graphics.Picture +import android.graphics.Rect +import android.os.Build +import android.os.SystemClock +import androidx.core.graphics.withClip +import io.legado.app.help.config.AppConfig +import io.legado.app.help.config.ReadBookConfig +import io.legado.app.lib.theme.ThemeStore +import io.legado.app.ui.book.read.page.entities.PageDirection +import io.legado.app.utils.screenshot + +/** + * 自动翻页 + */ +class AutoPager(private val readView: ReadView) { + private var progress = 0 + var isRunning = false + private var isPausing = false + private var scrollOffsetRemain = 0.0 + private var scrollOffset = 0 + private var lastTimeMillis = 0L + private var bitmap: Bitmap? = null + private var picture: Picture? = null + private var pictureIsDirty = true + private val atLeastApi23 = Build.VERSION.SDK_INT >= Build.VERSION_CODES.M + private val rect = Rect() + private val paint by lazy { Paint() } + + fun start() { + isRunning = true + paint.color = ThemeStore.accentColor + lastTimeMillis = SystemClock.uptimeMillis() + readView.curPage.upSelectAble(false) + readView.invalidate() + } + + fun stop() { + if (!isRunning) { + return + } + isRunning = false + isPausing = false + readView.curPage.upSelectAble(AppConfig.textSelectAble) + readView.invalidate() + reset() + } + + fun pause() { + if (!isRunning) { + return + } + isPausing = true + } + + fun resume() { + if (!isRunning) { + return + } + isPausing = false + lastTimeMillis = SystemClock.uptimeMillis() + readView.invalidate() + } + + fun reset() { + progress = 0 + scrollOffsetRemain = 0.0 + scrollOffset = 0 + bitmap?.recycle() + bitmap = null + pictureIsDirty = true + } + + fun onDraw(canvas: Canvas) { + if (!isRunning) { + return + } + + if (readView.isScroll) { + computeOffset() + if (!isPausing) readView.curPage.scroll(-scrollOffset) + } else { + val bottom = progress + val width = readView.width + if (atLeastApi23) { + if (picture == null) { + picture = Picture() + } + if (pictureIsDirty) { + pictureIsDirty = false + readView.nextPage.screenshot(picture!!) + } + canvas.withClip(0, 0, width, bottom) { + drawPicture(picture!!) + } + } else { + if (bitmap == null) { + bitmap = readView.nextPage.screenshot() + } + rect.set(0, 0, width, bottom) + canvas.drawBitmap(bitmap!!, rect, rect, null) + } + canvas.drawRect( + 0f, + bottom.toFloat() - 1, + width.toFloat(), + bottom.toFloat(), + paint + ) + if (!isPausing) readView.invalidate() + computeOffset() + } + + } + + private fun computeOffset() { + + val currentTime = SystemClock.uptimeMillis() + val elapsedTime = currentTime - lastTimeMillis + lastTimeMillis = currentTime + + val readTime = ReadBookConfig.autoReadSpeed * 1000.0 + val height = readView.height + scrollOffsetRemain += height / readTime * elapsedTime + if (scrollOffsetRemain < 1) { + return + } + scrollOffset = scrollOffsetRemain.toInt() + this.scrollOffsetRemain -= scrollOffset + if (!readView.isScroll) { + progress += scrollOffset + if (progress >= height) { + if (!readView.fillPage(PageDirection.NEXT)) { + stop() + } else { + reset() + } + } + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/ContentTextView.kt b/app/src/main/java/io/legado/app/ui/book/read/page/ContentTextView.kt index e100476f0..d69774d0d 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/page/ContentTextView.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/page/ContentTextView.kt @@ -9,7 +9,6 @@ import android.view.View import androidx.core.graphics.withTranslation import io.legado.app.R import io.legado.app.constant.PageAnim -import io.legado.app.constant.PreferKey import io.legado.app.data.entities.Bookmark import io.legado.app.help.config.AppConfig import io.legado.app.model.ReadBook @@ -28,16 +27,16 @@ import io.legado.app.ui.widget.dialog.PhotoDialog import io.legado.app.utils.PictureMirror import io.legado.app.utils.activity import io.legado.app.utils.getCompatColor -import io.legado.app.utils.getPrefBoolean import io.legado.app.utils.showDialogFragment import io.legado.app.utils.toastOnUi +import java.util.concurrent.Executors import kotlin.math.min /** * 阅读内容视图 */ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, attrs) { - var selectAble = context.getPrefBoolean(PreferKey.textSelectAble, true) + var selectAble = AppConfig.textSelectAble val selectedPaint by lazy { Paint().apply { color = context.getCompatColor(R.color.btn_bg_press_2) @@ -61,6 +60,9 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at private var pageOffset = 0 private val pictureMirror = PictureMirror() private val isNoAnim get() = ReadBook.pageAnim() == PageAnim.noAnim + private var autoPager: AutoPager? = null + private val renderThread by lazy { Executors.newSingleThreadExecutor() } + private val renderRunnable by lazy { Runnable { preRenderPage() } } //绘制图片的paint val imagePaint by lazy { @@ -91,6 +93,7 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at override fun onDraw(canvas: Canvas) { super.onDraw(canvas) + autoPager?.onDraw(canvas) if (longScreenshot) { canvas.translate(0f, scrollY.toFloat()) } @@ -179,6 +182,20 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at pictureMirror.invalidate() } + fun submitPreRenderTask() { + renderThread.submit(renderRunnable) + } + + private fun preRenderPage() { + val view = this + pageFactory.run { + prevPage.preRender(view) + prevPage.preRender(view) + nextPage.preRender(view) + nextPlusPage.preRender(view) + } + } + /** * 重置滚动位置 */ @@ -669,6 +686,10 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at } } + fun setAutoPager(autoPager: AutoPager?) { + this.autoPager = autoPager + } + override fun canScrollVertically(direction: Int): Boolean { return callBack.isScroll && pageFactory.hasNext() } diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/PageView.kt b/app/src/main/java/io/legado/app/ui/book/read/page/PageView.kt index 7632ba4a1..0e2de7eee 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/page/PageView.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/page/PageView.kt @@ -45,6 +45,8 @@ class PageView(context: Context) : FrameLayout(context) { private var tvBookName: BatteryView? = null private var tvTimeBattery: BatteryView? = null private var tvTimeBatteryP: BatteryView? = null + private var isMainView = false + var isScroll = false val headerHeight: Int get() { @@ -272,7 +274,13 @@ class PageView(context: Context) : FrameLayout(context) { * 设置内容 */ fun setContent(textPage: TextPage, resetPageOffset: Boolean = true) { - setProgress(textPage) + if (isMainView && !isScroll) { + setProgress(textPage) + } else { + post { + setProgress(textPage) + } + } if (resetPageOffset) { resetPageOffset() } @@ -324,6 +332,14 @@ class PageView(context: Context) : FrameLayout(context) { tvPageAndTotal?.text = "${index.plus(1)}/$pageSize $readProgress" } + fun setAutoPager(autoPager: AutoPager?) { + binding.contentTextView.setAutoPager(autoPager) + } + + fun submitPreRenderTask() { + binding.contentTextView.submitPreRenderTask() + } + /** * 滚动事件 */ @@ -375,6 +391,7 @@ class PageView(context: Context) : FrameLayout(context) { } fun markAsMainView() { + isMainView = true binding.contentTextView.isMainView = true } diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/ReadView.kt b/app/src/main/java/io/legado/app/ui/book/read/page/ReadView.kt index c1d5f66e0..25df757ea 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/page/ReadView.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/page/ReadView.kt @@ -35,7 +35,6 @@ import io.legado.app.ui.book.read.page.provider.ChapterProvider import io.legado.app.ui.book.read.page.provider.TextPageFactory import io.legado.app.utils.activity import io.legado.app.utils.invisible -import io.legado.app.utils.screenshot import io.legado.app.utils.showDialogFragment import io.legado.app.utils.visible import java.text.BreakIterator @@ -108,6 +107,8 @@ class ReadView(context: Context, attrs: AttributeSet) : private val autoPagePint by lazy { Paint().apply { color = context.accentColor } } private val boundary by lazy { BreakIterator.getWordInstance(Locale.getDefault()) } private var nextPageBitmap: Bitmap? = null + val autoPager = AutoPager(this) + val isAutoPage get() = autoPager.isRunning init { addView(nextPage) @@ -149,22 +150,7 @@ class ReadView(context: Context, attrs: AttributeSet) : override fun dispatchDraw(canvas: Canvas) { super.dispatchDraw(canvas) pageDelegate?.onDraw(canvas) - if (!isInEditMode && callBack.isAutoPage && !isScroll) { - // 自动翻页 - val bitmap = nextPageBitmap ?: nextPage.screenshot()?.also { nextPageBitmap = it } - bitmap?.let { - val bottom = callBack.autoPageProgress - autoPageRect.set(0, 0, width, bottom) - canvas.drawBitmap(it, autoPageRect, autoPageRect, null) - canvas.drawRect( - 0f, - bottom.toFloat() - 1, - width.toFloat(), - bottom.toFloat(), - autoPagePint - ) - } - } + autoPager.onDraw(canvas) } override fun computeScroll() { @@ -262,6 +248,7 @@ class ReadView(context: Context, attrs: AttributeSet) : pageDelegate?.onTouch(event) } pressOnTextSelected = false + autoPager.resume() } } return true @@ -530,6 +517,12 @@ class ReadView(context: Context, attrs: AttributeSet) : } else { nextPage.visible() } + if (isScroll) { + curPage.setAutoPager(autoPager) + } else { + curPage.setAutoPager(null) + } + curPage.isScroll = isScroll } /** @@ -541,12 +534,13 @@ class ReadView(context: Context, attrs: AttributeSet) : post { curPage.setContentDescription(pageFactory.curPage.text) } - if (isScroll && !callBack.isAutoPage) { + if (isScroll && !isAutoPage) { curPage.setContent(pageFactory.curPage, resetPageOffset) } else { - if (callBack.isAutoPage && relativePosition >= 0) { - clearNextPageBitmap() - } +// if (isAutoPage && relativePosition >= 0) { +//// clearNextPageBitmap() +// autoPager.clear() +// } when (relativePosition) { -1 -> prevPage.setContent(pageFactory.prevPage) 1 -> nextPage.setContent(pageFactory.nextPage) @@ -663,6 +657,24 @@ class ReadView(context: Context, attrs: AttributeSet) : } } + fun onScrollAnimStart() { + autoPager.pause() + } + + fun onScrollAnimStop() { + autoPager.resume() + } + + override fun onPageChange() { + autoPager.reset() + curPage.submitPreRenderTask() + } + + fun onContentLoadFinish() { + autoPager.reset() + curPage.submitPreRenderTask() + } + override val currentChapter: TextChapter? get() { return if (callBack.isInitFinish) ReadBook.textChapter(0) else null @@ -688,8 +700,6 @@ class ReadView(context: Context, attrs: AttributeSet) : interface CallBack { val isInitFinish: Boolean - val isAutoPage: Boolean - val autoPageProgress: Int fun showActionMenu() fun screenOffTimerStart() fun showTextActionMenu() diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/api/DataSource.kt b/app/src/main/java/io/legado/app/ui/book/read/page/api/DataSource.kt index eb28942d6..e06238554 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/page/api/DataSource.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/page/api/DataSource.kt @@ -20,4 +20,6 @@ interface DataSource { fun hasPrevChapter(): Boolean fun upContent(relativePosition: Int = 0, resetPageOffset: Boolean = true) + + fun onPageChange() } \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/delegate/ScrollPageDelegate.kt b/app/src/main/java/io/legado/app/ui/book/read/page/delegate/ScrollPageDelegate.kt index e7d58f935..232aaa925 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/page/delegate/ScrollPageDelegate.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/page/delegate/ScrollPageDelegate.kt @@ -21,6 +21,7 @@ class ScrollPageDelegate(readView: ReadView) : PageDelegate(readView) { var noAnim: Boolean = false override fun onAnimStart(animationSpeed: Int) { + readView.onScrollAnimStart() //惯性滚动 fling( 0, touchY.toInt(), 0, mVelocity.yVelocity.toInt(), @@ -29,7 +30,7 @@ class ScrollPageDelegate(readView: ReadView) : PageDelegate(readView) { } override fun onAnimStop() { - // nothing + readView.onScrollAnimStop() } override fun onTouch(event: MotionEvent) { 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 0f89a6e32..2683ed97a 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 @@ -121,7 +121,7 @@ data class TextLine( return visible } - fun draw(view: ContentTextView, canvas: Canvas) { + fun draw(view: ContentTextView, canvas: Canvas?) { pictureMirror.draw(canvas, view.width, height.toInt()) { drawTextLine(view, this) } 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 1a9d366f7..079519be1 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 @@ -263,7 +263,7 @@ data class TextPage( return null } - fun draw(view: ContentTextView, canvas: Canvas) { + fun draw(view: ContentTextView, canvas: Canvas?) { pictureMirror.draw(canvas, view.width, height.toInt()) { drawPage(view, this) } @@ -278,6 +278,15 @@ data class TextPage( } } + fun preRender(view: ContentTextView) { + if (!pictureMirror.isDirty) return + draw(view, null) + } + + fun isDirty(): Boolean { + return pictureMirror.isDirty + } + fun invalidate() { pictureMirror.invalidate() } diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/provider/TextPageFactory.kt b/app/src/main/java/io/legado/app/ui/book/read/page/provider/TextPageFactory.kt index 9ff5f77ee..386d5d2ed 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/page/provider/TextPageFactory.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/page/provider/TextPageFactory.kt @@ -44,6 +44,7 @@ class TextPageFactory(dataSource: DataSource) : PageFactory(dataSource ReadBook.setPageIndex(pageIndex.plus(1)) } if (upContent) upContent(resetPageOffset = false) + dataSource.onPageChange() true } else false @@ -63,6 +64,7 @@ class TextPageFactory(dataSource: DataSource) : PageFactory(dataSource ReadBook.setPageIndex(pageIndex.minus(1)) } if (upContent) upContent(resetPageOffset = false) + dataSource.onPageChange() true } else false diff --git a/app/src/main/java/io/legado/app/utils/PictureMirror.kt b/app/src/main/java/io/legado/app/utils/PictureMirror.kt index 9a725500d..71bd6ccbf 100644 --- a/app/src/main/java/io/legado/app/utils/PictureMirror.kt +++ b/app/src/main/java/io/legado/app/utils/PictureMirror.kt @@ -4,23 +4,31 @@ import android.graphics.Canvas import android.graphics.Picture import android.os.Build import androidx.core.graphics.record +import java.util.concurrent.locks.ReentrantLock class PictureMirror { var picture: Picture? = null + @Volatile var isDirty = true + val lock = ReentrantLock() - inline fun draw(canvas: Canvas, width: Int, height: Int, block: Canvas.() -> Unit) { + inline fun draw(canvas: Canvas?, width: Int, height: Int, block: Canvas.() -> Unit) { if (atLeastApi23) { if (picture == null) picture = Picture() val picture = picture!! if (isDirty) { - isDirty = false - picture.record(width, height, block) + if (!lock.tryLock()) return + try { + picture.record(width, height, block) + isDirty = false + } finally { + lock.unlock() + } } - canvas.drawPicture(picture) + canvas?.drawPicture(picture) } else { - canvas.block() + canvas?.block() } }