mirror of
https://github.com/gedoor/legado.git
synced 2024-07-06 23:47:49 +08:00
优化
This commit is contained in:
parent
d115910e9b
commit
7fbc23ab13
@ -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)
|
||||
|
||||
|
@ -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<T>(
|
||||
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 <T> async(
|
||||
scope: CoroutineScope = DEFAULT,
|
||||
@ -46,7 +51,7 @@ class Coroutine<T>(
|
||||
|
||||
}
|
||||
|
||||
private val job: Job
|
||||
private val job: Job by lazy { executeInternal(context, block) }
|
||||
|
||||
private var start: VoidCallback? = null
|
||||
private var success: Callback<T>? = null
|
||||
@ -67,7 +72,13 @@ class Coroutine<T>(
|
||||
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<T> {
|
||||
|
@ -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
|
||||
|
@ -58,6 +58,12 @@ abstract class BaseReadBookActivity :
|
||||
override val binding by viewBinding(ActivityBookReadBinding::inflate)
|
||||
override val viewModel by viewModels<ReadBookViewModel>()
|
||||
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<PaddingConfigDialog>()
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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()
|
||||
}
|
||||
|
||||
}
|
||||
|
146
app/src/main/java/io/legado/app/ui/book/read/page/AutoPager.kt
Normal file
146
app/src/main/java/io/legado/app/ui/book/read/page/AutoPager.kt
Normal file
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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()
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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()
|
||||
|
@ -20,4 +20,6 @@ interface DataSource {
|
||||
fun hasPrevChapter(): Boolean
|
||||
|
||||
fun upContent(relativePosition: Int = 0, resetPageOffset: Boolean = true)
|
||||
|
||||
fun onPageChange()
|
||||
}
|
@ -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) {
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -44,6 +44,7 @@ class TextPageFactory(dataSource: DataSource) : PageFactory<TextPage>(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<TextPage>(dataSource
|
||||
ReadBook.setPageIndex(pageIndex.minus(1))
|
||||
}
|
||||
if (upContent) upContent(resetPageOffset = false)
|
||||
dataSource.onPageChange()
|
||||
true
|
||||
} else
|
||||
false
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user