pull/517/head
gedoor 5 years ago
parent d20ec0a97c
commit f14ba7c859
  1. 88
      app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt
  2. 1
      app/src/main/java/io/legado/app/ui/book/read/page/ContentTextView.kt
  3. 279
      app/src/main/java/io/legado/app/ui/book/read/page/ContentView.kt
  4. 611
      app/src/main/java/io/legado/app/ui/book/read/page/PageView.kt
  5. 460
      app/src/main/java/io/legado/app/ui/book/read/page/ReadView.kt
  6. 2
      app/src/main/java/io/legado/app/ui/book/read/page/api/DataSource.kt
  7. 2
      app/src/main/java/io/legado/app/ui/book/read/page/api/PageFactory.kt
  8. 6
      app/src/main/java/io/legado/app/ui/book/read/page/delegate/CoverPageDelegate.kt
  9. 22
      app/src/main/java/io/legado/app/ui/book/read/page/delegate/HorizontalPageDelegate.kt
  10. 6
      app/src/main/java/io/legado/app/ui/book/read/page/delegate/NoAnimPageDelegate.kt
  11. 46
      app/src/main/java/io/legado/app/ui/book/read/page/delegate/PageDelegate.kt
  12. 23
      app/src/main/java/io/legado/app/ui/book/read/page/delegate/ScrollPageDelegate.kt
  13. 12
      app/src/main/java/io/legado/app/ui/book/read/page/delegate/SimulationPageDelegate.kt
  14. 6
      app/src/main/java/io/legado/app/ui/book/read/page/delegate/SlidePageDelegate.kt
  15. 4
      app/src/main/java/io/legado/app/ui/book/read/page/provider/TextPageFactory.kt
  16. 4
      app/src/main/res/layout/activity_book_read.xml

@ -37,9 +37,9 @@ import io.legado.app.ui.book.read.config.BgTextConfigDialog.Companion.BG_COLOR
import io.legado.app.ui.book.read.config.BgTextConfigDialog.Companion.TEXT_COLOR import io.legado.app.ui.book.read.config.BgTextConfigDialog.Companion.TEXT_COLOR
import io.legado.app.ui.book.read.config.TipConfigDialog.Companion.TIP_COLOR import io.legado.app.ui.book.read.config.TipConfigDialog.Companion.TIP_COLOR
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.PageView import io.legado.app.ui.book.read.page.ReadView
import io.legado.app.ui.book.read.page.TextPageFactory
import io.legado.app.ui.book.read.page.entities.PageDirection import io.legado.app.ui.book.read.page.entities.PageDirection
import io.legado.app.ui.book.read.page.provider.TextPageFactory
import io.legado.app.ui.book.searchContent.SearchContentActivity import io.legado.app.ui.book.searchContent.SearchContentActivity
import io.legado.app.ui.book.source.edit.BookSourceEditActivity import io.legado.app.ui.book.source.edit.BookSourceEditActivity
import io.legado.app.ui.book.toc.ChapterListActivity import io.legado.app.ui.book.toc.ChapterListActivity
@ -60,7 +60,7 @@ import org.jetbrains.anko.toast
class ReadBookActivity : ReadBookBaseActivity(), class ReadBookActivity : ReadBookBaseActivity(),
View.OnTouchListener, View.OnTouchListener,
PageView.CallBack, ReadView.CallBack,
TextActionMenu.CallBack, TextActionMenu.CallBack,
ContentTextView.CallBack, ContentTextView.CallBack,
ReadMenu.CallBack, ReadMenu.CallBack,
@ -79,7 +79,7 @@ class ReadBookActivity : ReadBookBaseActivity(),
override val scope: CoroutineScope get() = this override val scope: CoroutineScope get() = this
override val isInitFinish: Boolean get() = viewModel.isInitFinish override val isInitFinish: Boolean get() = viewModel.isInitFinish
override val isScroll: Boolean get() = binding.pageView.isScroll override val isScroll: Boolean get() = binding.readView.isScroll
private val mHandler = Handler(Looper.getMainLooper()) private val mHandler = Handler(Looper.getMainLooper())
private val keepScreenRunnable: Runnable = private val keepScreenRunnable: Runnable =
Runnable { keepScreenOn(window, false) } Runnable { keepScreenOn(window, false) }
@ -89,8 +89,8 @@ class ReadBookActivity : ReadBookBaseActivity(),
private var screenTimeOut: Long = 0 private var screenTimeOut: Long = 0
private var timeBatteryReceiver: TimeBatteryReceiver? = null private var timeBatteryReceiver: TimeBatteryReceiver? = null
private var loadStates: Boolean = false private var loadStates: Boolean = false
override val pageFactory: TextPageFactory get() = binding.pageView.pageFactory override val pageFactory: TextPageFactory get() = binding.readView.pageFactory
override val headerHeight: Int get() = binding.pageView.curPage.headerHeight override val headerHeight: Int get() = binding.readView.curPage.headerHeight
@SuppressLint("ClickableViewAccessibility") @SuppressLint("ClickableViewAccessibility")
override fun onActivityCreated(savedInstanceState: Bundle?) { override fun onActivityCreated(savedInstanceState: Bundle?) {
@ -116,7 +116,7 @@ class ReadBookActivity : ReadBookBaseActivity(),
override fun onConfigurationChanged(newConfig: Configuration) { override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig) super.onConfigurationChanged(newConfig)
binding.pageView.upStatusBar() binding.readView.upStatusBar()
ReadBook.loadContent(resetPageOffset = false) ReadBook.loadContent(resetPageOffset = false)
} }
@ -125,7 +125,7 @@ class ReadBookActivity : ReadBookBaseActivity(),
ReadBook.readStartTime = System.currentTimeMillis() ReadBook.readStartTime = System.currentTimeMillis()
upSystemUiVisibility() upSystemUiVisibility()
timeBatteryReceiver = TimeBatteryReceiver.register(this) timeBatteryReceiver = TimeBatteryReceiver.register(this)
binding.pageView.upTime() binding.readView.upTime()
} }
override fun onPause() { override fun onPause() {
@ -190,7 +190,7 @@ class ReadBookActivity : ReadBookBaseActivity(),
R.id.menu_refresh -> { R.id.menu_refresh -> {
ReadBook.book?.let { ReadBook.book?.let {
ReadBook.curTextChapter = null ReadBook.curTextChapter = null
binding.pageView.upContent() binding.readView.upContent()
viewModel.refreshContent(it) viewModel.refreshContent(it)
} }
} }
@ -212,7 +212,7 @@ class ReadBookActivity : ReadBookBaseActivity(),
ReadBook.loadContent(false) ReadBook.loadContent(false)
} }
R.id.menu_page_anim -> showPageAnimConfig { R.id.menu_page_anim -> showPageAnimConfig {
binding.pageView.upPageAnim() binding.readView.upPageAnim()
} }
R.id.menu_book_info -> ReadBook.book?.let { R.id.menu_book_info -> ReadBook.book?.let {
startActivity<BookInfoActivity>( startActivity<BookInfoActivity>(
@ -264,13 +264,13 @@ class ReadBookActivity : ReadBookBaseActivity(),
when { when {
isPrevKey(keyCode) -> { isPrevKey(keyCode) -> {
if (keyCode != KeyEvent.KEYCODE_UNKNOWN) { if (keyCode != KeyEvent.KEYCODE_UNKNOWN) {
binding.pageView.pageDelegate?.keyTurnPage(PageDirection.PREV) binding.readView.pageDelegate?.keyTurnPage(PageDirection.PREV)
return true return true
} }
} }
isNextKey(keyCode) -> { isNextKey(keyCode) -> {
if (keyCode != KeyEvent.KEYCODE_UNKNOWN) { if (keyCode != KeyEvent.KEYCODE_UNKNOWN) {
binding.pageView.pageDelegate?.keyTurnPage(PageDirection.NEXT) binding.readView.pageDelegate?.keyTurnPage(PageDirection.NEXT)
return true return true
} }
} }
@ -285,7 +285,7 @@ class ReadBookActivity : ReadBookBaseActivity(),
} }
} }
keyCode == KeyEvent.KEYCODE_SPACE -> { keyCode == KeyEvent.KEYCODE_SPACE -> {
binding.pageView.pageDelegate?.keyTurnPage(PageDirection.NEXT) binding.readView.pageDelegate?.keyTurnPage(PageDirection.NEXT)
return true return true
} }
} }
@ -346,11 +346,11 @@ class ReadBookActivity : ReadBookBaseActivity(),
MotionEvent.ACTION_DOWN -> textActionMenu?.dismiss() MotionEvent.ACTION_DOWN -> textActionMenu?.dismiss()
MotionEvent.ACTION_MOVE -> { MotionEvent.ACTION_MOVE -> {
when (v.id) { when (v.id) {
R.id.cursor_left -> binding.pageView.curPage.selectStartMove( R.id.cursor_left -> binding.readView.curPage.selectStartMove(
event.rawX + binding.cursorLeft.width, event.rawX + binding.cursorLeft.width,
event.rawY - binding.cursorLeft.height event.rawY - binding.cursorLeft.height
) )
R.id.cursor_right -> binding.pageView.curPage.selectEndMove( R.id.cursor_right -> binding.readView.curPage.selectEndMove(
event.rawX - binding.cursorRight.width, event.rawX - binding.cursorRight.width,
event.rawY - binding.cursorRight.height event.rawY - binding.cursorRight.height
) )
@ -419,7 +419,7 @@ class ReadBookActivity : ReadBookBaseActivity(),
/** /**
* 当前选择的文本 * 当前选择的文本
*/ */
override val selectedText: String get() = binding.pageView.curPage.selectedText override val selectedText: String get() = binding.readView.curPage.selectedText
/** /**
* 文本选择菜单操作 * 文本选择菜单操作
@ -455,8 +455,8 @@ class ReadBookActivity : ReadBookBaseActivity(),
*/ */
override fun onMenuActionFinally() { override fun onMenuActionFinally() {
textActionMenu?.dismiss() textActionMenu?.dismiss()
binding.pageView.curPage.cancelSelect() binding.readView.curPage.cancelSelect()
binding.pageView.isTextSelected = false binding.readView.isTextSelected = false
} }
/** /**
@ -468,8 +468,8 @@ class ReadBookActivity : ReadBookBaseActivity(),
if (getPrefBoolean("volumeKeyPageOnPlay") if (getPrefBoolean("volumeKeyPageOnPlay")
|| BaseReadAloudService.pause || BaseReadAloudService.pause
) { ) {
binding.pageView.pageDelegate?.isCancel = false binding.readView.pageDelegate?.isCancel = false
binding.pageView.pageDelegate?.keyTurnPage(direction) binding.readView.pageDelegate?.keyTurnPage(direction)
return true return true
} }
} }
@ -499,7 +499,7 @@ class ReadBookActivity : ReadBookBaseActivity(),
override fun upContent(relativePosition: Int, resetPageOffset: Boolean) { override fun upContent(relativePosition: Int, resetPageOffset: Boolean) {
autoPageProgress = 0 autoPageProgress = 0
launch { launch {
binding.pageView.upContent(relativePosition, resetPageOffset) binding.readView.upContent(relativePosition, resetPageOffset)
binding.readMenu.setSeekPage(ReadBook.durPageIndex()) binding.readMenu.setSeekPage(ReadBook.durPageIndex())
} }
loadStates = false loadStates = false
@ -516,7 +516,7 @@ class ReadBookActivity : ReadBookBaseActivity(),
override fun upPageAnim() { override fun upPageAnim() {
launch { launch {
binding.pageView.upPageAnim() binding.readView.upPageAnim()
} }
} }
@ -579,8 +579,8 @@ class ReadBookActivity : ReadBookBaseActivity(),
autoPageStop() autoPageStop()
} else { } else {
isAutoPage = true isAutoPage = true
binding.pageView.upContent() binding.readView.upContent()
binding.pageView.upContent(1) binding.readView.upContent(1)
autoPagePlus() autoPagePlus()
} }
binding.readMenu.setAutoPage(isAutoPage) binding.readMenu.setAutoPage(isAutoPage)
@ -589,20 +589,20 @@ class ReadBookActivity : ReadBookBaseActivity(),
override fun autoPageStop() { override fun autoPageStop() {
isAutoPage = false isAutoPage = false
mHandler.removeCallbacks(autoPageRunnable) mHandler.removeCallbacks(autoPageRunnable)
binding.pageView.upContent() binding.readView.upContent()
} }
private fun autoPagePlus() { private fun autoPagePlus() {
mHandler.removeCallbacks(autoPageRunnable) mHandler.removeCallbacks(autoPageRunnable)
if (binding.pageView.isScroll) { if (binding.readView.isScroll) {
binding.pageView.curPage.scroll(-binding.pageView.height / ReadBookConfig.autoReadSpeed / 50) binding.readView.curPage.scroll(-binding.readView.height / ReadBookConfig.autoReadSpeed / 50)
} else { } else {
autoPageProgress++ autoPageProgress++
if (autoPageProgress >= ReadBookConfig.autoReadSpeed * 50) { if (autoPageProgress >= ReadBookConfig.autoReadSpeed * 50) {
autoPageProgress = 0 autoPageProgress = 0
binding.pageView.fillPage(PageDirection.NEXT) binding.readView.fillPage(PageDirection.NEXT)
} else { } else {
binding.pageView.invalidate() binding.readView.invalidate()
} }
} }
mHandler.postDelayed(autoPageRunnable, 20) mHandler.postDelayed(autoPageRunnable, 20)
@ -751,28 +751,28 @@ class ReadBookActivity : ReadBookBaseActivity(),
ReadBook.skipToPage(positions[0]) ReadBook.skipToPage(positions[0])
} }
withContext(Main) { withContext(Main) {
binding.pageView.curPage.selectStartMoveIndex( binding.readView.curPage.selectStartMoveIndex(
0, 0,
positions[1], positions[1],
positions[2] positions[2]
) )
delay(20L) delay(20L)
when (positions[3]) { when (positions[3]) {
0 -> binding.pageView.curPage.selectEndMoveIndex( 0 -> binding.readView.curPage.selectEndMoveIndex(
0, 0,
positions[1], positions[1],
positions[2] + viewModel.searchContentQuery.length - 1 positions[2] + viewModel.searchContentQuery.length - 1
) )
1 -> binding.pageView.curPage.selectEndMoveIndex( 1 -> binding.readView.curPage.selectEndMoveIndex(
0, 0,
positions[1] + 1, positions[1] + 1,
positions[4] positions[4]
) )
//consider change page, jump to scroll position //consider change page, jump to scroll position
-1 -> binding.pageView.curPage -1 -> binding.readView.curPage
.selectEndMoveIndex(1, 0, positions[4]) .selectEndMoveIndex(1, 0, positions[4])
} }
binding.pageView.isTextSelected = true binding.readView.isTextSelected = true
delay(100L) delay(100L)
} }
} }
@ -803,7 +803,7 @@ class ReadBookActivity : ReadBookBaseActivity(),
super.onDestroy() super.onDestroy()
mHandler.removeCallbacks(keepScreenRunnable) mHandler.removeCallbacks(keepScreenRunnable)
textActionMenu?.dismiss() textActionMenu?.dismiss()
binding.pageView.onDestroy() binding.readView.onDestroy()
ReadBook.msg = null ReadBook.msg = null
if (!BuildConfig.DEBUG) { if (!BuildConfig.DEBUG) {
SyncBookProgress.uploadBookProgress() SyncBookProgress.uploadBookProgress()
@ -813,11 +813,11 @@ class ReadBookActivity : ReadBookBaseActivity(),
override fun observeLiveBus() { override fun observeLiveBus() {
super.observeLiveBus() super.observeLiveBus()
observeEvent<String>(EventBus.TIME_CHANGED) { binding.pageView.upTime() } observeEvent<String>(EventBus.TIME_CHANGED) { binding.readView.upTime() }
observeEvent<Int>(EventBus.BATTERY_CHANGED) { binding.pageView.upBattery(it) } observeEvent<Int>(EventBus.BATTERY_CHANGED) { binding.readView.upBattery(it) }
observeEvent<BookChapter>(EventBus.OPEN_CHAPTER) { observeEvent<BookChapter>(EventBus.OPEN_CHAPTER) {
viewModel.openChapter(it.index, ReadBook.durChapterPos) viewModel.openChapter(it.index, ReadBook.durChapterPos)
binding.pageView.upContent() binding.readView.upContent()
} }
observeEvent<Boolean>(EventBus.MEDIA_BUTTON) { observeEvent<Boolean>(EventBus.MEDIA_BUTTON) {
if (it) { if (it) {
@ -828,13 +828,13 @@ class ReadBookActivity : ReadBookBaseActivity(),
} }
observeEvent<Boolean>(EventBus.UP_CONFIG) { observeEvent<Boolean>(EventBus.UP_CONFIG) {
upSystemUiVisibility() upSystemUiVisibility()
binding.pageView.upBg() binding.readView.upBg()
binding.pageView.upTipStyle() binding.readView.upTipStyle()
binding.pageView.upStyle() binding.readView.upStyle()
if (it) { if (it) {
ReadBook.loadContent(resetPageOffset = false) ReadBook.loadContent(resetPageOffset = false)
} else { } else {
binding.pageView.upContent(resetPageOffset = false) binding.readView.upContent(resetPageOffset = false)
} }
} }
observeEvent<Int>(EventBus.ALOUD_STATE) { observeEvent<Int>(EventBus.ALOUD_STATE) {
@ -843,7 +843,7 @@ class ReadBookActivity : ReadBookBaseActivity(),
val page = textChapter.getPageByReadPos(ReadBook.durChapterPos) val page = textChapter.getPageByReadPos(ReadBook.durChapterPos)
if (page != null) { if (page != null) {
page.removePageAloudSpan() page.removePageAloudSpan()
binding.pageView.upContent(resetPageOffset = false) binding.readView.upContent(resetPageOffset = false)
} }
} }
} }
@ -864,7 +864,7 @@ class ReadBookActivity : ReadBookBaseActivity(),
upScreenTimeOut() upScreenTimeOut()
} }
observeEvent<Boolean>(PreferKey.textSelectAble) { observeEvent<Boolean>(PreferKey.textSelectAble) {
binding.pageView.curPage.upSelectAble(it) binding.readView.curPage.upSelectAble(it)
} }
observeEvent<String>(PreferKey.showBrightnessView) { observeEvent<String>(PreferKey.showBrightnessView) {
binding.readMenu.upBrightnessState() binding.readMenu.upBrightnessState()

@ -15,6 +15,7 @@ import io.legado.app.service.help.ReadBook
import io.legado.app.ui.book.read.page.entities.* import io.legado.app.ui.book.read.page.entities.*
import io.legado.app.ui.book.read.page.provider.ChapterProvider import io.legado.app.ui.book.read.page.provider.ChapterProvider
import io.legado.app.ui.book.read.page.provider.ImageProvider import io.legado.app.ui.book.read.page.provider.ImageProvider
import io.legado.app.ui.book.read.page.provider.TextPageFactory
import io.legado.app.ui.widget.dialog.PhotoDialog import io.legado.app.ui.widget.dialog.PhotoDialog
import io.legado.app.utils.activity import io.legado.app.utils.activity
import io.legado.app.utils.getCompatColor import io.legado.app.utils.getCompatColor

@ -1,279 +0,0 @@
package io.legado.app.ui.book.read.page
import android.annotation.SuppressLint
import android.content.Context
import android.graphics.drawable.Drawable
import android.view.LayoutInflater
import android.widget.FrameLayout
import androidx.core.view.isGone
import androidx.core.view.isInvisible
import io.legado.app.App
import io.legado.app.R
import io.legado.app.base.BaseActivity
import io.legado.app.constant.AppConst.timeFormat
import io.legado.app.databinding.ViewBookPageBinding
import io.legado.app.help.ReadBookConfig
import io.legado.app.help.ReadTipConfig
import io.legado.app.service.help.ReadBook
import io.legado.app.ui.book.read.page.entities.PageData
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.widget.BatteryView
import io.legado.app.utils.*
import org.jetbrains.anko.topPadding
import java.util.*
/**
* 阅读界面
*/
class ContentView(context: Context) : FrameLayout(context) {
private val binding = ViewBookPageBinding.inflate(LayoutInflater.from(context), this, true)
private var battery = 100
private var tvTitle: BatteryView? = null
private var tvTime: BatteryView? = null
private var tvBattery: BatteryView? = null
private var tvPage: BatteryView? = null
private var tvTotalProgress: BatteryView? = null
private var tvPageAndTotal: BatteryView? = null
private var tvBookName: BatteryView? = null
private var tvTimeBattery: BatteryView? = null
val headerHeight: Int
get() {
val h1 = if (ReadBookConfig.hideStatusBar) 0 else context.statusBarHeight
val h2 = if (binding.llHeader.isGone) 0 else binding.llHeader.height
return h1 + h2
}
init {
if (!isInEditMode) {
//设置背景防止切换背景时文字重叠
setBackgroundColor(context.getCompatColor(R.color.background))
upTipStyle()
upStyle()
binding.contentTextView.upView = {
setProgress(it)
}
}
}
fun upStyle() = with(binding) {
ChapterProvider.let {
bvHeaderLeft.typeface = it.typeface
tvHeaderLeft.typeface = it.typeface
tvHeaderMiddle.typeface = it.typeface
tvHeaderRight.typeface = it.typeface
bvFooterLeft.typeface = it.typeface
tvFooterLeft.typeface = it.typeface
tvFooterMiddle.typeface = it.typeface
tvFooterRight.typeface = it.typeface
}
ReadBookConfig.let {
val tipColor = with(ReadTipConfig) {
if (tipColor == 0) it.textColor else tipColor
}
bvHeaderLeft.setColor(tipColor)
tvHeaderLeft.setColor(tipColor)
tvHeaderMiddle.setColor(tipColor)
tvHeaderRight.setColor(tipColor)
bvFooterLeft.setColor(tipColor)
tvFooterLeft.setColor(tipColor)
tvFooterMiddle.setColor(tipColor)
tvFooterRight.setColor(tipColor)
upStatusBar()
llHeader.setPadding(
it.headerPaddingLeft.dp,
it.headerPaddingTop.dp,
it.headerPaddingRight.dp,
it.headerPaddingBottom.dp
)
llFooter.setPadding(
it.footerPaddingLeft.dp,
it.footerPaddingTop.dp,
it.footerPaddingRight.dp,
it.footerPaddingBottom.dp
)
vwTopDivider.visible(it.showHeaderLine)
vwBottomDivider.visible(it.showFooterLine)
pageNvBar.layoutParams = pageNvBar.layoutParams.apply {
height = if (it.hideNavigationBar) 0 else App.navigationBarHeight
}
}
contentTextView.upVisibleRect()
upTime()
upBattery(battery)
}
/**
* 显示状态栏时隐藏header
*/
fun upStatusBar() {
binding.vwStatusBar.topPadding = context.statusBarHeight
binding.vwStatusBar.isGone =
ReadBookConfig.hideStatusBar || (activity as? BaseActivity<*>)?.isInMultiWindow == true
}
fun upTipStyle() = with(binding) {
ReadTipConfig.apply {
tvHeaderLeft.isInvisible = tipHeaderLeft != chapterTitle
bvHeaderLeft.isInvisible =
tipHeaderLeft == none || !tvHeaderLeft.isInvisible
tvHeaderRight.isGone = tipHeaderRight == none
tvHeaderMiddle.isGone = tipHeaderMiddle == none
tvFooterLeft.isInvisible = tipFooterLeft != chapterTitle
bvFooterLeft.isInvisible =
tipFooterLeft == none || !tvFooterLeft.isInvisible
tvFooterRight.isGone = tipFooterRight == none
tvFooterMiddle.isGone = tipFooterMiddle == none
llHeader.isGone = when (headerMode) {
1 -> false
2 -> true
else -> !ReadBookConfig.hideStatusBar
}
llFooter.isGone = when (footerMode) {
1 -> true
else -> false
}
}
tvTitle = getTipView(ReadTipConfig.chapterTitle)
tvTitle?.apply {
isBattery = false
textSize = 12f
}
tvTime = getTipView(ReadTipConfig.time)
tvTime?.apply {
isBattery = false
textSize = 12f
}
tvBattery = getTipView(ReadTipConfig.battery)
tvBattery?.apply {
isBattery = true
textSize = 10f
}
tvPage = getTipView(ReadTipConfig.page)
tvPage?.apply {
isBattery = false
textSize = 12f
}
tvTotalProgress = getTipView(ReadTipConfig.totalProgress)
tvTotalProgress?.apply {
isBattery = false
textSize = 12f
}
tvPageAndTotal = getTipView(ReadTipConfig.pageAndTotal)
tvPageAndTotal?.apply {
isBattery = false
textSize = 12f
}
tvBookName = getTipView(ReadTipConfig.bookName)
tvBookName?.apply {
isBattery = false
textSize = 12f
}
tvTimeBattery = getTipView(ReadTipConfig.timeBattery)
tvTimeBattery?.apply {
isBattery = false
textSize = 12f
}
}
private fun getTipView(tip: Int): BatteryView? = with(binding) {
return when (tip) {
ReadTipConfig.tipHeaderLeft ->
if (tip == ReadTipConfig.chapterTitle) tvHeaderLeft else bvHeaderLeft
ReadTipConfig.tipHeaderMiddle -> tvHeaderMiddle
ReadTipConfig.tipHeaderRight -> tvHeaderRight
ReadTipConfig.tipFooterLeft ->
if (tip == ReadTipConfig.chapterTitle) tvFooterLeft else bvFooterLeft
ReadTipConfig.tipFooterMiddle -> tvFooterMiddle
ReadTipConfig.tipFooterRight -> tvFooterRight
else -> null
}
}
fun setBg(bg: Drawable?) {
binding.pagePanel.background = bg
}
fun upTime() {
tvTime?.text = timeFormat.format(Date(System.currentTimeMillis()))
upTimeBattery()
}
fun upBattery(battery: Int) {
this.battery = battery
tvBattery?.setBattery(battery)
upTimeBattery()
}
@SuppressLint("SetTextI18n")
private fun upTimeBattery() {
tvTimeBattery?.let {
val time = timeFormat.format(Date(System.currentTimeMillis()))
it.text = "$time $battery%"
}
}
fun setContent(pageData: PageData, resetPageOffset: Boolean = true) {
setProgress(pageData.textPage)
if (resetPageOffset) {
resetPageOffset()
}
binding.contentTextView.setContent(pageData)
}
fun setContentDescription(content: String) {
binding.contentTextView.contentDescription = content
}
fun resetPageOffset() {
binding.contentTextView.resetPageOffset()
}
@SuppressLint("SetTextI18n")
fun setProgress(textPage: TextPage) = textPage.apply {
tvBookName?.text = ReadBook.book?.name
tvTitle?.text = textPage.title
tvPage?.text = "${index.plus(1)}/$pageSize"
tvTotalProgress?.text = readProgress
tvPageAndTotal?.text = "${index.plus(1)}/$pageSize $readProgress"
}
fun scroll(offset: Int) {
binding.contentTextView.scroll(offset)
}
fun upSelectAble(selectAble: Boolean) {
binding.contentTextView.selectAble = selectAble
}
fun selectText(
x: Float, y: Float,
select: (relativePage: Int, lineIndex: Int, charIndex: Int) -> Unit,
) {
return binding.contentTextView.selectText(x, y - headerHeight, select)
}
fun selectStartMove(x: Float, y: Float) {
binding.contentTextView.selectStartMove(x, y - headerHeight)
}
fun selectStartMoveIndex(relativePage: Int, lineIndex: Int, charIndex: Int) {
binding.contentTextView.selectStartMoveIndex(relativePage, lineIndex, charIndex)
}
fun selectEndMove(x: Float, y: Float) {
binding.contentTextView.selectEndMove(x, y - headerHeight)
}
fun selectEndMoveIndex(relativePage: Int, lineIndex: Int, charIndex: Int) {
binding.contentTextView.selectEndMoveIndex(relativePage, lineIndex, charIndex)
}
fun cancelSelect() {
binding.contentTextView.cancelSelect()
}
val selectedText: String get() = binding.contentTextView.selectedText
}

@ -2,457 +2,278 @@ package io.legado.app.ui.book.read.page
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.Context import android.content.Context
import android.graphics.Canvas import android.graphics.drawable.Drawable
import android.graphics.Paint import android.view.LayoutInflater
import android.graphics.Rect
import android.graphics.RectF
import android.util.AttributeSet
import android.view.MotionEvent
import android.view.ViewConfiguration
import android.widget.FrameLayout import android.widget.FrameLayout
import io.legado.app.help.AppConfig import androidx.core.view.isGone
import androidx.core.view.isInvisible
import io.legado.app.App
import io.legado.app.R
import io.legado.app.base.BaseActivity
import io.legado.app.constant.AppConst.timeFormat
import io.legado.app.databinding.ViewBookPageBinding
import io.legado.app.help.ReadBookConfig import io.legado.app.help.ReadBookConfig
import io.legado.app.lib.theme.accentColor import io.legado.app.help.ReadTipConfig
import io.legado.app.service.help.ReadBook import io.legado.app.service.help.ReadBook
import io.legado.app.ui.book.read.page.delegate.* import io.legado.app.ui.book.read.page.entities.PageData
import io.legado.app.ui.book.read.page.entities.PageDirection import io.legado.app.ui.book.read.page.entities.TextPage
import io.legado.app.ui.book.read.page.entities.TextChapter
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.activity import io.legado.app.ui.widget.BatteryView
import io.legado.app.utils.screenshot import io.legado.app.utils.*
import kotlin.math.abs import org.jetbrains.anko.topPadding
import java.util.*
class PageView(context: Context, attrs: AttributeSet) :
FrameLayout(context, attrs), /**
DataSource { * 阅读界面
*/
val callBack: CallBack get() = activity as CallBack class PageView(context: Context) : FrameLayout(context) {
var pageFactory: TextPageFactory = TextPageFactory(this) private val binding = ViewBookPageBinding.inflate(LayoutInflater.from(context), this, true)
var pageDelegate: PageDelegate? = null private var battery = 100
private set(value) { private var tvTitle: BatteryView? = null
field?.onDestroy() private var tvTime: BatteryView? = null
field = null private var tvBattery: BatteryView? = null
field = value private var tvPage: BatteryView? = null
upContent() private var tvTotalProgress: BatteryView? = null
} private var tvPageAndTotal: BatteryView? = null
var isScroll = false private var tvBookName: BatteryView? = null
var prevPage: ContentView = ContentView(context) private var tvTimeBattery: BatteryView? = null
var curPage: ContentView = ContentView(context)
var nextPage: ContentView = ContentView(context) val headerHeight: Int
val defaultAnimationSpeed = 300 get() {
private var pressDown = false val h1 = if (ReadBookConfig.hideStatusBar) 0 else context.statusBarHeight
private var isMove = false val h2 = if (binding.llHeader.isGone) 0 else binding.llHeader.height
return h1 + h2
//起始点
var startX: Float = 0f
var startY: Float = 0f
//上一个触碰点
var lastX: Float = 0f
var lastY: Float = 0f
//触碰点
var touchX: Float = 0f
var touchY: Float = 0f
//是否停止动画动作
var isAbortAnim = false
//长按
private var longPressed = false
private val longPressTimeout = 600L
private val longPressRunnable = Runnable {
longPressed = true
onLongPress()
}
var isTextSelected = false
private var pressOnTextSelected = false
private var firstRelativePage = 0
private var firstLineIndex: Int = 0
private var firstCharIndex: Int = 0
val slopSquare by lazy { ViewConfiguration.get(context).scaledTouchSlop }
private val tlRect = RectF(10F, 10F, width * 0.33f, height * 0.33f)
private val tcRect = RectF(width * 0.33f, 10F, width * 0.66f, height * 0.33f)
private val trRect = RectF(width * 0.36f, 10F, width - 10f, height * 0.33f)
private val mlRect = RectF(10F, height * 0.33f, width * 0.33f, height * 0.66f)
private val mcRect = RectF(width * 0.33f, height * 0.33f, width * 0.66f, height * 0.66f)
private val mrRect = RectF(width * 0.66f, height * 0.33f, width - 10f, height * 0.66f)
private val blRect = RectF(10F, height * 0.66f, width * 0.33f, height - 10f)
private val bcRect = RectF(width * 0.33f, height * 0.66f, width * 0.66f, height - 10f)
private val brRect = RectF(width * 0.66f, height * 0.66f, width - 10f, height - 10f)
private val autoPageRect by lazy {
Rect()
}
private val autoPagePint by lazy {
Paint().apply {
color = context.accentColor
} }
}
init { init {
addView(nextPage)
addView(curPage)
addView(prevPage)
if (!isInEditMode) { if (!isInEditMode) {
upBg() //设置背景防止切换背景时文字重叠
setWillNotDraw(false) setBackgroundColor(context.getCompatColor(R.color.background))
upPageAnim() upTipStyle()
} upStyle()
} binding.contentTextView.upView = {
setProgress(it)
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
super.onSizeChanged(w, h, oldw, oldh)
tlRect.set(10F, 10F, width * 0.33f, height * 0.33f)
tcRect.set(width * 0.33f, 10F, width * 0.66f, height * 0.33f)
trRect.set(width * 0.36f, 10F, width - 10f, height * 0.33f)
mlRect.set(10F, height * 0.33f, width * 0.33f, height * 0.66f)
mcRect.set(width * 0.33f, height * 0.33f, width * 0.66f, height * 0.66f)
mrRect.set(width * 0.66f, height * 0.33f, width - 10f, height * 0.66f)
blRect.set(10F, height * 0.66f, width * 0.33f, height - 10f)
bcRect.set(width * 0.33f, height * 0.66f, width * 0.66f, height - 10f)
brRect.set(width * 0.66f, height * 0.66f, width - 10f, height - 10f)
prevPage.x = -w.toFloat()
pageDelegate?.setViewSize(w, h)
}
override fun dispatchDraw(canvas: Canvas) {
super.dispatchDraw(canvas)
pageDelegate?.onDraw(canvas)
if (!isInEditMode && callBack.isAutoPage && !isScroll) {
nextPage.screenshot()?.let {
val bottom =
height * callBack.autoPageProgress / (ReadBookConfig.autoReadSpeed * 50)
autoPageRect.set(0, 0, width, bottom)
canvas.drawBitmap(it, autoPageRect, autoPageRect, null)
canvas.drawRect(
0f,
bottom.toFloat() - 1,
width.toFloat(),
bottom.toFloat(),
autoPagePint
)
} }
} }
} }
override fun computeScroll() { fun upStyle() = with(binding) {
pageDelegate?.scroll() ChapterProvider.let {
} bvHeaderLeft.typeface = it.typeface
tvHeaderLeft.typeface = it.typeface
override fun onInterceptTouchEvent(ev: MotionEvent?): Boolean { tvHeaderMiddle.typeface = it.typeface
return true tvHeaderRight.typeface = it.typeface
} bvFooterLeft.typeface = it.typeface
tvFooterLeft.typeface = it.typeface
/** tvFooterMiddle.typeface = it.typeface
* 触摸事件 tvFooterRight.typeface = it.typeface
*/ }
@SuppressLint("ClickableViewAccessibility") ReadBookConfig.let {
override fun onTouchEvent(event: MotionEvent): Boolean { val tipColor = with(ReadTipConfig) {
callBack.screenOffTimerStart() if (tipColor == 0) it.textColor else tipColor
when (event.action) {
MotionEvent.ACTION_DOWN -> {
if (isTextSelected) {
curPage.cancelSelect()
isTextSelected = false
pressOnTextSelected = true
} else {
pressOnTextSelected = false
}
longPressed = false
postDelayed(longPressRunnable, longPressTimeout)
pressDown = true
isMove = false
pageDelegate?.onTouch(event)
pageDelegate?.onDown()
setStartPoint(event.x, event.y)
}
MotionEvent.ACTION_MOVE -> {
if (!isMove) {
isMove =
abs(startX - event.x) > slopSquare || abs(startY - event.y) > slopSquare
}
if (isMove) {
longPressed = false
removeCallbacks(longPressRunnable)
if (isTextSelected) {
selectText(event.x, event.y)
} else {
pageDelegate?.onTouch(event)
}
}
} }
MotionEvent.ACTION_CANCEL, MotionEvent.ACTION_UP -> { bvHeaderLeft.setColor(tipColor)
removeCallbacks(longPressRunnable) tvHeaderLeft.setColor(tipColor)
if (!pressDown) return true tvHeaderMiddle.setColor(tipColor)
pressDown = false tvHeaderRight.setColor(tipColor)
if (!isMove) { bvFooterLeft.setColor(tipColor)
if (!longPressed && !pressOnTextSelected) { tvFooterLeft.setColor(tipColor)
onSingleTapUp() tvFooterMiddle.setColor(tipColor)
return true tvFooterRight.setColor(tipColor)
} upStatusBar()
} llHeader.setPadding(
if (isTextSelected) { it.headerPaddingLeft.dp,
callBack.showTextActionMenu() it.headerPaddingTop.dp,
} else if (isMove) { it.headerPaddingRight.dp,
pageDelegate?.onTouch(event) it.headerPaddingBottom.dp
} )
pressOnTextSelected = false llFooter.setPadding(
it.footerPaddingLeft.dp,
it.footerPaddingTop.dp,
it.footerPaddingRight.dp,
it.footerPaddingBottom.dp
)
vwTopDivider.visible(it.showHeaderLine)
vwBottomDivider.visible(it.showFooterLine)
pageNvBar.layoutParams = pageNvBar.layoutParams.apply {
height = if (it.hideNavigationBar) 0 else App.navigationBarHeight
} }
} }
return true contentTextView.upVisibleRect()
} upTime()
upBattery(battery)
fun upStatusBar() {
curPage.upStatusBar()
prevPage.upStatusBar()
nextPage.upStatusBar()
}
/**
* 保存开始位置
*/
fun setStartPoint(x: Float, y: Float, invalidate: Boolean = true) {
startX = x
startY = y
lastX = x
lastY = y
touchX = x
touchY = y
if (invalidate) {
invalidate()
}
} }
/** /**
* 保存当前位置 * 显示状态栏时隐藏header
*/ */
fun setTouchPoint(x: Float, y: Float, invalidate: Boolean = true) { fun upStatusBar() {
lastX = touchX binding.vwStatusBar.topPadding = context.statusBarHeight
lastY = touchY binding.vwStatusBar.isGone =
touchX = x ReadBookConfig.hideStatusBar || (activity as? BaseActivity<*>)?.isInMultiWindow == true
touchY = y
if (invalidate) {
invalidate()
}
pageDelegate?.onScroll()
}
/**
* 长按选择
*/
private fun onLongPress() {
curPage.selectText(startX, startY) { relativePage, lineIndex, charIndex ->
isTextSelected = true
firstRelativePage = relativePage
firstLineIndex = lineIndex
firstCharIndex = charIndex
curPage.selectStartMoveIndex(firstRelativePage, firstLineIndex, firstCharIndex)
curPage.selectEndMoveIndex(firstRelativePage, firstLineIndex, firstCharIndex)
}
} }
/** fun upTipStyle() = with(binding) {
* 单击 ReadTipConfig.apply {
*/ tvHeaderLeft.isInvisible = tipHeaderLeft != chapterTitle
private fun onSingleTapUp() { bvHeaderLeft.isInvisible =
when { tipHeaderLeft == none || !tvHeaderLeft.isInvisible
isTextSelected -> isTextSelected = false tvHeaderRight.isGone = tipHeaderRight == none
mcRect.contains(startX, startY) -> if (!isAbortAnim) { tvHeaderMiddle.isGone = tipHeaderMiddle == none
click(AppConfig.clickActionMC) tvFooterLeft.isInvisible = tipFooterLeft != chapterTitle
bvFooterLeft.isInvisible =
tipFooterLeft == none || !tvFooterLeft.isInvisible
tvFooterRight.isGone = tipFooterRight == none
tvFooterMiddle.isGone = tipFooterMiddle == none
llHeader.isGone = when (headerMode) {
1 -> false
2 -> true
else -> !ReadBookConfig.hideStatusBar
} }
bcRect.contains(startX, startY) -> { llFooter.isGone = when (footerMode) {
click(AppConfig.clickActionBC) 1 -> true
} else -> false
blRect.contains(startX, startY) -> {
click(AppConfig.clickActionBL)
}
brRect.contains(startX, startY) -> {
click(AppConfig.clickActionBR)
}
mlRect.contains(startX, startY) -> {
click(AppConfig.clickActionML)
}
mrRect.contains(startX, startY) -> {
click(AppConfig.clickActionMR)
}
tlRect.contains(startX, startY) -> {
click(AppConfig.clickActionTL)
}
tcRect.contains(startX, startY) -> {
click(AppConfig.clickActionTC)
}
trRect.contains(startX, startY) -> {
click(AppConfig.clickActionTR)
} }
} }
tvTitle = getTipView(ReadTipConfig.chapterTitle)
tvTitle?.apply {
isBattery = false
textSize = 12f
}
tvTime = getTipView(ReadTipConfig.time)
tvTime?.apply {
isBattery = false
textSize = 12f
}
tvBattery = getTipView(ReadTipConfig.battery)
tvBattery?.apply {
isBattery = true
textSize = 10f
}
tvPage = getTipView(ReadTipConfig.page)
tvPage?.apply {
isBattery = false
textSize = 12f
}
tvTotalProgress = getTipView(ReadTipConfig.totalProgress)
tvTotalProgress?.apply {
isBattery = false
textSize = 12f
}
tvPageAndTotal = getTipView(ReadTipConfig.pageAndTotal)
tvPageAndTotal?.apply {
isBattery = false
textSize = 12f
}
tvBookName = getTipView(ReadTipConfig.bookName)
tvBookName?.apply {
isBattery = false
textSize = 12f
}
tvTimeBattery = getTipView(ReadTipConfig.timeBattery)
tvTimeBattery?.apply {
isBattery = false
textSize = 12f
}
} }
private fun click(action: Int) { private fun getTipView(tip: Int): BatteryView? = with(binding) {
when (action) { return when (tip) {
0 -> callBack.showActionMenu() ReadTipConfig.tipHeaderLeft ->
1 -> pageDelegate?.nextPageByAnim(defaultAnimationSpeed) if (tip == ReadTipConfig.chapterTitle) tvHeaderLeft else bvHeaderLeft
2 -> pageDelegate?.prevPageByAnim(defaultAnimationSpeed) ReadTipConfig.tipHeaderMiddle -> tvHeaderMiddle
3 -> ReadBook.moveToNextChapter(true) ReadTipConfig.tipHeaderRight -> tvHeaderRight
4 -> ReadBook.moveToPrevChapter(upContent = true, toLast = false) ReadTipConfig.tipFooterLeft ->
if (tip == ReadTipConfig.chapterTitle) tvFooterLeft else bvFooterLeft
ReadTipConfig.tipFooterMiddle -> tvFooterMiddle
ReadTipConfig.tipFooterRight -> tvFooterRight
else -> null
} }
} }
/** fun setBg(bg: Drawable?) {
* 选择文本 binding.pagePanel.background = bg
*/
private fun selectText(x: Float, y: Float) {
curPage.selectText(x, y) { relativePage, lineIndex, charIndex ->
when {
relativePage > firstRelativePage -> {
curPage.selectStartMoveIndex(firstRelativePage, firstLineIndex, firstCharIndex)
curPage.selectEndMoveIndex(relativePage, lineIndex, charIndex)
}
relativePage < firstRelativePage -> {
curPage.selectEndMoveIndex(firstRelativePage, firstLineIndex, firstCharIndex)
curPage.selectStartMoveIndex(relativePage, lineIndex, charIndex)
}
lineIndex > firstLineIndex -> {
curPage.selectStartMoveIndex(firstRelativePage, firstLineIndex, firstCharIndex)
curPage.selectEndMoveIndex(relativePage, lineIndex, charIndex)
}
lineIndex < firstLineIndex -> {
curPage.selectEndMoveIndex(firstRelativePage, firstLineIndex, firstCharIndex)
curPage.selectStartMoveIndex(relativePage, lineIndex, charIndex)
}
charIndex > firstCharIndex -> {
curPage.selectStartMoveIndex(firstRelativePage, firstLineIndex, firstCharIndex)
curPage.selectEndMoveIndex(relativePage, lineIndex, charIndex)
}
else -> {
curPage.selectEndMoveIndex(firstRelativePage, firstLineIndex, firstCharIndex)
curPage.selectStartMoveIndex(relativePage, lineIndex, charIndex)
}
}
}
} }
fun onDestroy() { fun upTime() {
pageDelegate?.onDestroy() tvTime?.text = timeFormat.format(Date(System.currentTimeMillis()))
curPage.cancelSelect() upTimeBattery()
} }
fun fillPage(direction: PageDirection) { fun upBattery(battery: Int) {
when (direction) { this.battery = battery
PageDirection.PREV -> { tvBattery?.setBattery(battery)
pageFactory.moveToPrev(true) upTimeBattery()
}
PageDirection.NEXT -> {
pageFactory.moveToNext(true)
}
else -> Unit
}
} }
fun upPageAnim() { @SuppressLint("SetTextI18n")
isScroll = ReadBook.pageAnim() == 3 private fun upTimeBattery() {
when (ReadBook.pageAnim()) { tvTimeBattery?.let {
0 -> if (pageDelegate !is CoverPageDelegate) { val time = timeFormat.format(Date(System.currentTimeMillis()))
pageDelegate = CoverPageDelegate(this) it.text = "$time $battery%"
}
1 -> if (pageDelegate !is SlidePageDelegate) {
pageDelegate = SlidePageDelegate(this)
}
2 -> if (pageDelegate !is SimulationPageDelegate) {
pageDelegate = SimulationPageDelegate(this)
}
3 -> if (pageDelegate !is ScrollPageDelegate) {
pageDelegate = ScrollPageDelegate(this)
}
else -> if (pageDelegate !is NoAnimPageDelegate) {
pageDelegate = NoAnimPageDelegate(this)
}
} }
} }
override fun upContent(relativePosition: Int, resetPageOffset: Boolean) { fun setContent(pageData: PageData, resetPageOffset: Boolean = true) {
curPage.setContentDescription(pageFactory.curData.textPage.text) setProgress(pageData.textPage)
if (isScroll && !callBack.isAutoPage) { if (resetPageOffset) {
curPage.setContent(pageFactory.curData, resetPageOffset) resetPageOffset()
} else {
curPage.resetPageOffset()
when (relativePosition) {
-1 -> prevPage.setContent(pageFactory.prevData)
1 -> nextPage.setContent(pageFactory.nextData)
else -> {
curPage.setContent(pageFactory.curData)
nextPage.setContent(pageFactory.nextData)
prevPage.setContent(pageFactory.prevData)
}
}
} }
callBack.screenOffTimerStart() binding.contentTextView.setContent(pageData)
} }
fun upTipStyle() { fun setContentDescription(content: String) {
curPage.upTipStyle() binding.contentTextView.contentDescription = content
prevPage.upTipStyle()
nextPage.upTipStyle()
} }
fun upStyle() { fun resetPageOffset() {
ChapterProvider.upStyle() binding.contentTextView.resetPageOffset()
curPage.upStyle()
prevPage.upStyle()
nextPage.upStyle()
} }
fun upBg() { @SuppressLint("SetTextI18n")
ReadBookConfig.bg ?: let { fun setProgress(textPage: TextPage) = textPage.apply {
ReadBookConfig.upBg() tvBookName?.text = ReadBook.book?.name
} tvTitle?.text = textPage.title
curPage.setBg(ReadBookConfig.bg) tvPage?.text = "${index.plus(1)}/$pageSize"
prevPage.setBg(ReadBookConfig.bg) tvTotalProgress?.text = readProgress
nextPage.setBg(ReadBookConfig.bg) tvPageAndTotal?.text = "${index.plus(1)}/$pageSize $readProgress"
} }
fun upTime() { fun scroll(offset: Int) {
curPage.upTime() binding.contentTextView.scroll(offset)
prevPage.upTime()
nextPage.upTime()
} }
fun upBattery(battery: Int) { fun upSelectAble(selectAble: Boolean) {
curPage.upBattery(battery) binding.contentTextView.selectAble = selectAble
prevPage.upBattery(battery)
nextPage.upBattery(battery)
} }
override val currentChapter: TextChapter? fun selectText(
get() { x: Float, y: Float,
return if (callBack.isInitFinish) ReadBook.textChapter(0) else null select: (relativePage: Int, lineIndex: Int, charIndex: Int) -> Unit,
} ) {
return binding.contentTextView.selectText(x, y - headerHeight, select)
}
override val nextChapter: TextChapter? fun selectStartMove(x: Float, y: Float) {
get() { binding.contentTextView.selectStartMove(x, y - headerHeight)
return if (callBack.isInitFinish) ReadBook.textChapter(1) else null }
}
override val prevChapter: TextChapter? fun selectStartMoveIndex(relativePage: Int, lineIndex: Int, charIndex: Int) {
get() { binding.contentTextView.selectStartMoveIndex(relativePage, lineIndex, charIndex)
return if (callBack.isInitFinish) ReadBook.textChapter(-1) else null }
}
override fun hasNextChapter(): Boolean { fun selectEndMove(x: Float, y: Float) {
return ReadBook.durChapterIndex < ReadBook.chapterSize - 1 binding.contentTextView.selectEndMove(x, y - headerHeight)
} }
override fun hasPrevChapter(): Boolean { fun selectEndMoveIndex(relativePage: Int, lineIndex: Int, charIndex: Int) {
return ReadBook.durChapterIndex > 0 binding.contentTextView.selectEndMoveIndex(relativePage, lineIndex, charIndex)
} }
interface CallBack { fun cancelSelect() {
val isInitFinish: Boolean binding.contentTextView.cancelSelect()
val isAutoPage: Boolean
val autoPageProgress: Int
fun showActionMenu()
fun screenOffTimerStart()
fun showTextActionMenu()
} }
}
val selectedText: String get() = binding.contentTextView.selectedText
}

@ -0,0 +1,460 @@
package io.legado.app.ui.book.read.page
import android.annotation.SuppressLint
import android.content.Context
import android.graphics.Canvas
import android.graphics.Paint
import android.graphics.Rect
import android.graphics.RectF
import android.util.AttributeSet
import android.view.MotionEvent
import android.view.ViewConfiguration
import android.widget.FrameLayout
import io.legado.app.help.AppConfig
import io.legado.app.help.ReadBookConfig
import io.legado.app.lib.theme.accentColor
import io.legado.app.service.help.ReadBook
import io.legado.app.ui.book.read.page.api.DataSource
import io.legado.app.ui.book.read.page.delegate.*
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.provider.ChapterProvider
import io.legado.app.ui.book.read.page.provider.TextPageFactory
import io.legado.app.utils.activity
import io.legado.app.utils.screenshot
import kotlin.math.abs
class ReadView(context: Context, attrs: AttributeSet) :
FrameLayout(context, attrs),
DataSource {
val callBack: CallBack get() = activity as CallBack
var pageFactory: TextPageFactory = TextPageFactory(this)
var pageDelegate: PageDelegate? = null
private set(value) {
field?.onDestroy()
field = null
field = value
upContent()
}
var isScroll = false
var prevPage: PageView = PageView(context)
var curPage: PageView = PageView(context)
var nextPage: PageView = PageView(context)
val defaultAnimationSpeed = 300
private var pressDown = false
private var isMove = false
//起始点
var startX: Float = 0f
var startY: Float = 0f
//上一个触碰点
var lastX: Float = 0f
var lastY: Float = 0f
//触碰点
var touchX: Float = 0f
var touchY: Float = 0f
//是否停止动画动作
var isAbortAnim = false
//长按
private var longPressed = false
private val longPressTimeout = 600L
private val longPressRunnable = Runnable {
longPressed = true
onLongPress()
}
var isTextSelected = false
private var pressOnTextSelected = false
private var firstRelativePage = 0
private var firstLineIndex: Int = 0
private var firstCharIndex: Int = 0
val slopSquare by lazy { ViewConfiguration.get(context).scaledTouchSlop }
private val tlRect = RectF(10F, 10F, width * 0.33f, height * 0.33f)
private val tcRect = RectF(width * 0.33f, 10F, width * 0.66f, height * 0.33f)
private val trRect = RectF(width * 0.36f, 10F, width - 10f, height * 0.33f)
private val mlRect = RectF(10F, height * 0.33f, width * 0.33f, height * 0.66f)
private val mcRect = RectF(width * 0.33f, height * 0.33f, width * 0.66f, height * 0.66f)
private val mrRect = RectF(width * 0.66f, height * 0.33f, width - 10f, height * 0.66f)
private val blRect = RectF(10F, height * 0.66f, width * 0.33f, height - 10f)
private val bcRect = RectF(width * 0.33f, height * 0.66f, width * 0.66f, height - 10f)
private val brRect = RectF(width * 0.66f, height * 0.66f, width - 10f, height - 10f)
private val autoPageRect by lazy {
Rect()
}
private val autoPagePint by lazy {
Paint().apply {
color = context.accentColor
}
}
init {
addView(nextPage)
addView(curPage)
addView(prevPage)
if (!isInEditMode) {
upBg()
setWillNotDraw(false)
upPageAnim()
}
}
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
super.onSizeChanged(w, h, oldw, oldh)
tlRect.set(10F, 10F, width * 0.33f, height * 0.33f)
tcRect.set(width * 0.33f, 10F, width * 0.66f, height * 0.33f)
trRect.set(width * 0.36f, 10F, width - 10f, height * 0.33f)
mlRect.set(10F, height * 0.33f, width * 0.33f, height * 0.66f)
mcRect.set(width * 0.33f, height * 0.33f, width * 0.66f, height * 0.66f)
mrRect.set(width * 0.66f, height * 0.33f, width - 10f, height * 0.66f)
blRect.set(10F, height * 0.66f, width * 0.33f, height - 10f)
bcRect.set(width * 0.33f, height * 0.66f, width * 0.66f, height - 10f)
brRect.set(width * 0.66f, height * 0.66f, width - 10f, height - 10f)
prevPage.x = -w.toFloat()
pageDelegate?.setViewSize(w, h)
}
override fun dispatchDraw(canvas: Canvas) {
super.dispatchDraw(canvas)
pageDelegate?.onDraw(canvas)
if (!isInEditMode && callBack.isAutoPage && !isScroll) {
nextPage.screenshot()?.let {
val bottom =
height * callBack.autoPageProgress / (ReadBookConfig.autoReadSpeed * 50)
autoPageRect.set(0, 0, width, bottom)
canvas.drawBitmap(it, autoPageRect, autoPageRect, null)
canvas.drawRect(
0f,
bottom.toFloat() - 1,
width.toFloat(),
bottom.toFloat(),
autoPagePint
)
}
}
}
override fun computeScroll() {
pageDelegate?.scroll()
}
override fun onInterceptTouchEvent(ev: MotionEvent?): Boolean {
return true
}
/**
* 触摸事件
*/
@SuppressLint("ClickableViewAccessibility")
override fun onTouchEvent(event: MotionEvent): Boolean {
callBack.screenOffTimerStart()
when (event.action) {
MotionEvent.ACTION_DOWN -> {
if (isTextSelected) {
curPage.cancelSelect()
isTextSelected = false
pressOnTextSelected = true
} else {
pressOnTextSelected = false
}
longPressed = false
postDelayed(longPressRunnable, longPressTimeout)
pressDown = true
isMove = false
pageDelegate?.onTouch(event)
pageDelegate?.onDown()
setStartPoint(event.x, event.y)
}
MotionEvent.ACTION_MOVE -> {
if (!isMove) {
isMove =
abs(startX - event.x) > slopSquare || abs(startY - event.y) > slopSquare
}
if (isMove) {
longPressed = false
removeCallbacks(longPressRunnable)
if (isTextSelected) {
selectText(event.x, event.y)
} else {
pageDelegate?.onTouch(event)
}
}
}
MotionEvent.ACTION_CANCEL, MotionEvent.ACTION_UP -> {
removeCallbacks(longPressRunnable)
if (!pressDown) return true
pressDown = false
if (!isMove) {
if (!longPressed && !pressOnTextSelected) {
onSingleTapUp()
return true
}
}
if (isTextSelected) {
callBack.showTextActionMenu()
} else if (isMove) {
pageDelegate?.onTouch(event)
}
pressOnTextSelected = false
}
}
return true
}
fun upStatusBar() {
curPage.upStatusBar()
prevPage.upStatusBar()
nextPage.upStatusBar()
}
/**
* 保存开始位置
*/
fun setStartPoint(x: Float, y: Float, invalidate: Boolean = true) {
startX = x
startY = y
lastX = x
lastY = y
touchX = x
touchY = y
if (invalidate) {
invalidate()
}
}
/**
* 保存当前位置
*/
fun setTouchPoint(x: Float, y: Float, invalidate: Boolean = true) {
lastX = touchX
lastY = touchY
touchX = x
touchY = y
if (invalidate) {
invalidate()
}
pageDelegate?.onScroll()
}
/**
* 长按选择
*/
private fun onLongPress() {
curPage.selectText(startX, startY) { relativePage, lineIndex, charIndex ->
isTextSelected = true
firstRelativePage = relativePage
firstLineIndex = lineIndex
firstCharIndex = charIndex
curPage.selectStartMoveIndex(firstRelativePage, firstLineIndex, firstCharIndex)
curPage.selectEndMoveIndex(firstRelativePage, firstLineIndex, firstCharIndex)
}
}
/**
* 单击
*/
private fun onSingleTapUp() {
when {
isTextSelected -> isTextSelected = false
mcRect.contains(startX, startY) -> if (!isAbortAnim) {
click(AppConfig.clickActionMC)
}
bcRect.contains(startX, startY) -> {
click(AppConfig.clickActionBC)
}
blRect.contains(startX, startY) -> {
click(AppConfig.clickActionBL)
}
brRect.contains(startX, startY) -> {
click(AppConfig.clickActionBR)
}
mlRect.contains(startX, startY) -> {
click(AppConfig.clickActionML)
}
mrRect.contains(startX, startY) -> {
click(AppConfig.clickActionMR)
}
tlRect.contains(startX, startY) -> {
click(AppConfig.clickActionTL)
}
tcRect.contains(startX, startY) -> {
click(AppConfig.clickActionTC)
}
trRect.contains(startX, startY) -> {
click(AppConfig.clickActionTR)
}
}
}
private fun click(action: Int) {
when (action) {
0 -> callBack.showActionMenu()
1 -> pageDelegate?.nextPageByAnim(defaultAnimationSpeed)
2 -> pageDelegate?.prevPageByAnim(defaultAnimationSpeed)
3 -> ReadBook.moveToNextChapter(true)
4 -> ReadBook.moveToPrevChapter(upContent = true, toLast = false)
}
}
/**
* 选择文本
*/
private fun selectText(x: Float, y: Float) {
curPage.selectText(x, y) { relativePage, lineIndex, charIndex ->
when {
relativePage > firstRelativePage -> {
curPage.selectStartMoveIndex(firstRelativePage, firstLineIndex, firstCharIndex)
curPage.selectEndMoveIndex(relativePage, lineIndex, charIndex)
}
relativePage < firstRelativePage -> {
curPage.selectEndMoveIndex(firstRelativePage, firstLineIndex, firstCharIndex)
curPage.selectStartMoveIndex(relativePage, lineIndex, charIndex)
}
lineIndex > firstLineIndex -> {
curPage.selectStartMoveIndex(firstRelativePage, firstLineIndex, firstCharIndex)
curPage.selectEndMoveIndex(relativePage, lineIndex, charIndex)
}
lineIndex < firstLineIndex -> {
curPage.selectEndMoveIndex(firstRelativePage, firstLineIndex, firstCharIndex)
curPage.selectStartMoveIndex(relativePage, lineIndex, charIndex)
}
charIndex > firstCharIndex -> {
curPage.selectStartMoveIndex(firstRelativePage, firstLineIndex, firstCharIndex)
curPage.selectEndMoveIndex(relativePage, lineIndex, charIndex)
}
else -> {
curPage.selectEndMoveIndex(firstRelativePage, firstLineIndex, firstCharIndex)
curPage.selectStartMoveIndex(relativePage, lineIndex, charIndex)
}
}
}
}
fun onDestroy() {
pageDelegate?.onDestroy()
curPage.cancelSelect()
}
fun fillPage(direction: PageDirection) {
when (direction) {
PageDirection.PREV -> {
pageFactory.moveToPrev(true)
}
PageDirection.NEXT -> {
pageFactory.moveToNext(true)
}
else -> Unit
}
}
fun upPageAnim() {
isScroll = ReadBook.pageAnim() == 3
when (ReadBook.pageAnim()) {
0 -> if (pageDelegate !is CoverPageDelegate) {
pageDelegate = CoverPageDelegate(this)
}
1 -> if (pageDelegate !is SlidePageDelegate) {
pageDelegate = SlidePageDelegate(this)
}
2 -> if (pageDelegate !is SimulationPageDelegate) {
pageDelegate = SimulationPageDelegate(this)
}
3 -> if (pageDelegate !is ScrollPageDelegate) {
pageDelegate = ScrollPageDelegate(this)
}
else -> if (pageDelegate !is NoAnimPageDelegate) {
pageDelegate = NoAnimPageDelegate(this)
}
}
}
override fun upContent(relativePosition: Int, resetPageOffset: Boolean) {
curPage.setContentDescription(pageFactory.curData.textPage.text)
if (isScroll && !callBack.isAutoPage) {
curPage.setContent(pageFactory.curData, resetPageOffset)
} else {
curPage.resetPageOffset()
when (relativePosition) {
-1 -> prevPage.setContent(pageFactory.prevData)
1 -> nextPage.setContent(pageFactory.nextData)
else -> {
curPage.setContent(pageFactory.curData)
nextPage.setContent(pageFactory.nextData)
prevPage.setContent(pageFactory.prevData)
}
}
}
callBack.screenOffTimerStart()
}
fun upTipStyle() {
curPage.upTipStyle()
prevPage.upTipStyle()
nextPage.upTipStyle()
}
fun upStyle() {
ChapterProvider.upStyle()
curPage.upStyle()
prevPage.upStyle()
nextPage.upStyle()
}
fun upBg() {
ReadBookConfig.bg ?: let {
ReadBookConfig.upBg()
}
curPage.setBg(ReadBookConfig.bg)
prevPage.setBg(ReadBookConfig.bg)
nextPage.setBg(ReadBookConfig.bg)
}
fun upTime() {
curPage.upTime()
prevPage.upTime()
nextPage.upTime()
}
fun upBattery(battery: Int) {
curPage.upBattery(battery)
prevPage.upBattery(battery)
nextPage.upBattery(battery)
}
override val currentChapter: TextChapter?
get() {
return if (callBack.isInitFinish) ReadBook.textChapter(0) else null
}
override val nextChapter: TextChapter?
get() {
return if (callBack.isInitFinish) ReadBook.textChapter(1) else null
}
override val prevChapter: TextChapter?
get() {
return if (callBack.isInitFinish) ReadBook.textChapter(-1) else null
}
override fun hasNextChapter(): Boolean {
return ReadBook.durChapterIndex < ReadBook.chapterSize - 1
}
override fun hasPrevChapter(): Boolean {
return ReadBook.durChapterIndex > 0
}
interface CallBack {
val isInitFinish: Boolean
val isAutoPage: Boolean
val autoPageProgress: Int
fun showActionMenu()
fun screenOffTimerStart()
fun showTextActionMenu()
}
}

@ -1,4 +1,4 @@
package io.legado.app.ui.book.read.page package io.legado.app.ui.book.read.page.api
import io.legado.app.service.help.ReadBook import io.legado.app.service.help.ReadBook
import io.legado.app.ui.book.read.page.entities.TextChapter import io.legado.app.ui.book.read.page.entities.TextChapter

@ -1,4 +1,4 @@
package io.legado.app.ui.book.read.page package io.legado.app.ui.book.read.page.api
abstract class PageFactory<DATA>(protected val dataSource: DataSource) { abstract class PageFactory<DATA>(protected val dataSource: DataSource) {

@ -3,10 +3,10 @@ package io.legado.app.ui.book.read.page.delegate
import android.graphics.Canvas import android.graphics.Canvas
import android.graphics.Matrix import android.graphics.Matrix
import android.graphics.drawable.GradientDrawable import android.graphics.drawable.GradientDrawable
import io.legado.app.ui.book.read.page.PageView 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.PageDirection
class CoverPageDelegate(pageView: PageView) : HorizontalPageDelegate(pageView) { class CoverPageDelegate(readView: ReadView) : HorizontalPageDelegate(readView) {
private val bitmapMatrix = Matrix() private val bitmapMatrix = Matrix()
private val shadowDrawableR: GradientDrawable private val shadowDrawableR: GradientDrawable
@ -54,7 +54,7 @@ class CoverPageDelegate(pageView: PageView) : HorizontalPageDelegate(pageView) {
override fun onAnimStop() { override fun onAnimStop() {
if (!isCancel) { if (!isCancel) {
pageView.fillPage(mDirection) readView.fillPage(mDirection)
} }
} }

@ -2,11 +2,11 @@ package io.legado.app.ui.book.read.page.delegate
import android.graphics.Bitmap import android.graphics.Bitmap
import android.view.MotionEvent import android.view.MotionEvent
import io.legado.app.ui.book.read.page.PageView 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.PageDirection
import io.legado.app.utils.screenshot import io.legado.app.utils.screenshot
abstract class HorizontalPageDelegate(pageView: PageView) : PageDelegate(pageView) { abstract class HorizontalPageDelegate(readView: ReadView) : PageDelegate(readView) {
protected var curBitmap: Bitmap? = null protected var curBitmap: Bitmap? = null
protected var prevBitmap: Bitmap? = null protected var prevBitmap: Bitmap? = null
@ -44,7 +44,7 @@ abstract class HorizontalPageDelegate(pageView: PageView) : PageDelegate(pageVie
onScroll(event) onScroll(event)
} }
MotionEvent.ACTION_CANCEL, MotionEvent.ACTION_UP -> { MotionEvent.ACTION_CANCEL, MotionEvent.ACTION_UP -> {
onAnimStart(pageView.defaultAnimationSpeed) onAnimStart(readView.defaultAnimationSpeed)
} }
} }
} }
@ -72,7 +72,7 @@ abstract class HorizontalPageDelegate(pageView: PageView) : PageDelegate(pageVie
val deltaX = (focusX - startX).toInt() val deltaX = (focusX - startX).toInt()
val deltaY = (focusY - startY).toInt() val deltaY = (focusY - startY).toInt()
val distance = deltaX * deltaX + deltaY * deltaY val distance = deltaX * deltaX + deltaY * deltaY
isMoved = distance > pageView.slopSquare isMoved = distance > readView.slopSquare
if (isMoved) { if (isMoved) {
if (sumX - startX > 0) { if (sumX - startX > 0) {
//如果上一页不存在 //如果上一页不存在
@ -95,7 +95,7 @@ abstract class HorizontalPageDelegate(pageView: PageView) : PageDelegate(pageVie
isCancel = if (mDirection == PageDirection.NEXT) sumX > lastX else sumX < lastX isCancel = if (mDirection == PageDirection.NEXT) sumX > lastX else sumX < lastX
isRunning = true isRunning = true
//设置触摸点 //设置触摸点
pageView.setTouchPoint(sumX, sumY) readView.setTouchPoint(sumX, sumY)
} }
} }
@ -104,14 +104,14 @@ abstract class HorizontalPageDelegate(pageView: PageView) : PageDelegate(pageVie
isMoved = false isMoved = false
isRunning = false isRunning = false
if (!scroller.isFinished) { if (!scroller.isFinished) {
pageView.isAbortAnim = true readView.isAbortAnim = true
scroller.abortAnimation() scroller.abortAnimation()
if (!isCancel) { if (!isCancel) {
pageView.fillPage(mDirection) readView.fillPage(mDirection)
pageView.invalidate() readView.invalidate()
} }
} else { } else {
pageView.isAbortAnim = false readView.isAbortAnim = false
} }
} }
@ -119,7 +119,7 @@ abstract class HorizontalPageDelegate(pageView: PageView) : PageDelegate(pageVie
abortAnim() abortAnim()
if (!hasNext()) return if (!hasNext()) return
setDirection(PageDirection.NEXT) setDirection(PageDirection.NEXT)
pageView.setTouchPoint(viewWidth.toFloat(), 0f, false) readView.setTouchPoint(viewWidth.toFloat(), 0f, false)
onAnimStart(animationSpeed) onAnimStart(animationSpeed)
} }
@ -127,7 +127,7 @@ abstract class HorizontalPageDelegate(pageView: PageView) : PageDelegate(pageVie
abortAnim() abortAnim()
if (!hasPrev()) return if (!hasPrev()) return
setDirection(PageDirection.PREV) setDirection(PageDirection.PREV)
pageView.setTouchPoint(0f, 0f) readView.setTouchPoint(0f, 0f)
onAnimStart(animationSpeed) onAnimStart(animationSpeed)
} }

@ -1,13 +1,13 @@
package io.legado.app.ui.book.read.page.delegate package io.legado.app.ui.book.read.page.delegate
import android.graphics.Canvas import android.graphics.Canvas
import io.legado.app.ui.book.read.page.PageView import io.legado.app.ui.book.read.page.ReadView
class NoAnimPageDelegate(pageView: PageView) : HorizontalPageDelegate(pageView) { class NoAnimPageDelegate(readView: ReadView) : HorizontalPageDelegate(readView) {
override fun onAnimStart(animationSpeed: Int) { override fun onAnimStart(animationSpeed: Int) {
if (!isCancel) { if (!isCancel) {
pageView.fillPage(mDirection) readView.fillPage(mDirection)
} }
stopScroll() stopScroll()
} }

@ -8,40 +8,40 @@ import android.widget.Scroller
import androidx.annotation.CallSuper import androidx.annotation.CallSuper
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
import io.legado.app.R import io.legado.app.R
import io.legado.app.ui.book.read.page.ContentView
import io.legado.app.ui.book.read.page.PageView import io.legado.app.ui.book.read.page.PageView
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.PageDirection
import kotlin.math.abs import kotlin.math.abs
abstract class PageDelegate(protected val pageView: PageView) { abstract class PageDelegate(protected val readView: ReadView) {
protected val context: Context = pageView.context protected val context: Context = readView.context
//起始点 //起始点
protected val startX: Float get() = pageView.startX protected val startX: Float get() = readView.startX
protected val startY: Float get() = pageView.startY protected val startY: Float get() = readView.startY
//上一个触碰点 //上一个触碰点
protected val lastX: Float get() = pageView.lastX protected val lastX: Float get() = readView.lastX
protected val lastY: Float get() = pageView.lastY protected val lastY: Float get() = readView.lastY
//触碰点 //触碰点
protected val touchX: Float get() = pageView.touchX protected val touchX: Float get() = readView.touchX
protected val touchY: Float get() = pageView.touchY protected val touchY: Float get() = readView.touchY
protected val nextPage: ContentView get() = pageView.nextPage protected val nextPage: PageView get() = readView.nextPage
protected val curPage: ContentView get() = pageView.curPage protected val curPage: PageView get() = readView.curPage
protected val prevPage: ContentView get() = pageView.prevPage protected val prevPage: PageView get() = readView.prevPage
protected var viewWidth: Int = pageView.width protected var viewWidth: Int = readView.width
protected var viewHeight: Int = pageView.height protected var viewHeight: Int = readView.height
protected val scroller: Scroller by lazy { protected val scroller: Scroller by lazy {
Scroller(pageView.context, DecelerateInterpolator()) Scroller(readView.context, DecelerateInterpolator())
} }
private val snackBar: Snackbar by lazy { private val snackBar: Snackbar by lazy {
Snackbar.make(pageView, "", Snackbar.LENGTH_SHORT) Snackbar.make(readView, "", Snackbar.LENGTH_SHORT)
} }
var isMoved = false var isMoved = false
@ -66,7 +66,7 @@ abstract class PageDelegate(protected val pageView: PageView) {
scroller.fling(startX, startY, velocityX, velocityY, minX, maxX, minY, maxY) scroller.fling(startX, startY, velocityX, velocityY, minX, maxX, minY, maxY)
isRunning = true isRunning = true
isStarted = true isStarted = true
pageView.invalidate() readView.invalidate()
} }
protected fun startScroll(startX: Int, startY: Int, dx: Int, dy: Int, animationSpeed: Int) { protected fun startScroll(startX: Int, startY: Int, dx: Int, dy: Int, animationSpeed: Int) {
@ -78,15 +78,15 @@ abstract class PageDelegate(protected val pageView: PageView) {
scroller.startScroll(startX, startY, dx, dy, duration) scroller.startScroll(startX, startY, dx, dy, duration)
isRunning = true isRunning = true
isStarted = true isStarted = true
pageView.invalidate() readView.invalidate()
} }
protected fun stopScroll() { protected fun stopScroll() {
isStarted = false isStarted = false
pageView.post { readView.post {
isMoved = false isMoved = false
isRunning = false isRunning = false
pageView.invalidate() readView.invalidate()
} }
} }
@ -97,7 +97,7 @@ abstract class PageDelegate(protected val pageView: PageView) {
fun scroll() { fun scroll() {
if (scroller.computeScrollOffset()) { if (scroller.computeScrollOffset()) {
pageView.setTouchPoint(scroller.currX.toFloat(), scroller.currY.toFloat()) readView.setTouchPoint(scroller.currX.toFloat(), scroller.currY.toFloat())
} else if (isStarted) { } else if (isStarted) {
onAnimStop() onAnimStop()
stopScroll() stopScroll()
@ -157,7 +157,7 @@ abstract class PageDelegate(protected val pageView: PageView) {
* 判断是否有上一页 * 判断是否有上一页
*/ */
fun hasPrev(): Boolean { fun hasPrev(): Boolean {
val hasPrev = pageView.pageFactory.hasPrev() val hasPrev = readView.pageFactory.hasPrev()
if (!hasPrev) { if (!hasPrev) {
if (!snackBar.isShown) { if (!snackBar.isShown) {
snackBar.setText(R.string.no_prev_page) snackBar.setText(R.string.no_prev_page)
@ -171,7 +171,7 @@ abstract class PageDelegate(protected val pageView: PageView) {
* 判断是否有下一页 * 判断是否有下一页
*/ */
fun hasNext(): Boolean { fun hasNext(): Boolean {
val hasNext = pageView.pageFactory.hasNext() val hasNext = readView.pageFactory.hasNext()
if (!hasNext) { if (!hasNext) {
if (!snackBar.isShown) { if (!snackBar.isShown) {
snackBar.setText(R.string.no_next_page) snackBar.setText(R.string.no_next_page)

@ -3,13 +3,14 @@ package io.legado.app.ui.book.read.page.delegate
import android.graphics.Canvas import android.graphics.Canvas
import android.view.MotionEvent import android.view.MotionEvent
import android.view.VelocityTracker import android.view.VelocityTracker
import io.legado.app.ui.book.read.page.PageView import io.legado.app.ui.book.read.page.ReadView
import io.legado.app.ui.book.read.page.provider.ChapterProvider import io.legado.app.ui.book.read.page.provider.ChapterProvider
class ScrollPageDelegate(pageView: PageView) : PageDelegate(pageView) { class ScrollPageDelegate(readView: ReadView) : PageDelegate(readView) {
// 滑动追踪的时间 // 滑动追踪的时间
private val velocityDuration = 1000 private val velocityDuration = 1000
//速度追踪器 //速度追踪器
private val mVelocity: VelocityTracker = VelocityTracker.obtain() private val mVelocity: VelocityTracker = VelocityTracker.obtain()
@ -35,7 +36,7 @@ class ScrollPageDelegate(pageView: PageView) : PageDelegate(pageView) {
onScroll(event) onScroll(event)
} }
MotionEvent.ACTION_CANCEL, MotionEvent.ACTION_UP -> { MotionEvent.ACTION_CANCEL, MotionEvent.ACTION_UP -> {
onAnimStart(pageView.defaultAnimationSpeed) onAnimStart(readView.defaultAnimationSpeed)
} }
} }
} }
@ -67,12 +68,12 @@ class ScrollPageDelegate(pageView: PageView) : PageDelegate(pageView) {
val div = if (pointerUp) count - 1 else count val div = if (pointerUp) count - 1 else count
val focusX = sumX / div val focusX = sumX / div
val focusY = sumY / div val focusY = sumY / div
pageView.setTouchPoint(sumX, sumY) readView.setTouchPoint(sumX, sumY)
if (!isMoved) { if (!isMoved) {
val deltaX = (focusX - startX).toInt() val deltaX = (focusX - startX).toInt()
val deltaY = (focusY - startY).toInt() val deltaY = (focusY - startY).toInt()
val distance = deltaX * deltaX + deltaY * deltaY val distance = deltaX * deltaX + deltaY * deltaY
isMoved = distance > pageView.slopSquare isMoved = distance > readView.slopSquare
} }
if (isMoved) { if (isMoved) {
isRunning = true isRunning = true
@ -89,26 +90,26 @@ class ScrollPageDelegate(pageView: PageView) : PageDelegate(pageView) {
isMoved = false isMoved = false
isRunning = false isRunning = false
if (!scroller.isFinished) { if (!scroller.isFinished) {
pageView.isAbortAnim = true readView.isAbortAnim = true
scroller.abortAnimation() scroller.abortAnimation()
} else { } else {
pageView.isAbortAnim = false readView.isAbortAnim = false
} }
} }
override fun nextPageByAnim(animationSpeed: Int) { override fun nextPageByAnim(animationSpeed: Int) {
if (pageView.isAbortAnim) { if (readView.isAbortAnim) {
return return
} }
pageView.setStartPoint(0f, 0f, false) readView.setStartPoint(0f, 0f, false)
startScroll(0, 0, 0, -ChapterProvider.visibleHeight, animationSpeed) startScroll(0, 0, 0, -ChapterProvider.visibleHeight, animationSpeed)
} }
override fun prevPageByAnim(animationSpeed: Int) { override fun prevPageByAnim(animationSpeed: Int) {
if (pageView.isAbortAnim) { if (readView.isAbortAnim) {
return return
} }
pageView.setStartPoint(0f, 0f, false) readView.setStartPoint(0f, 0f, false)
startScroll(0, 0, 0, ChapterProvider.visibleHeight, animationSpeed) startScroll(0, 0, 0, ChapterProvider.visibleHeight, animationSpeed)
} }
} }

@ -5,20 +5,22 @@ import android.graphics.drawable.GradientDrawable
import android.os.Build import android.os.Build
import android.view.MotionEvent import android.view.MotionEvent
import io.legado.app.help.ReadBookConfig import io.legado.app.help.ReadBookConfig
import io.legado.app.ui.book.read.page.PageView 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.PageDirection
import kotlin.math.* import kotlin.math.*
@Suppress("DEPRECATION") @Suppress("DEPRECATION")
class SimulationPageDelegate(pageView: PageView) : HorizontalPageDelegate(pageView) { class SimulationPageDelegate(readView: ReadView) : HorizontalPageDelegate(readView) {
//不让x,y为0,否则在点计算时会有问题 //不让x,y为0,否则在点计算时会有问题
private var mTouchX = 0.1f private var mTouchX = 0.1f
private var mTouchY = 0.1f private var mTouchY = 0.1f
// 拖拽点对应的页脚 // 拖拽点对应的页脚
private var mCornerX = 1 private var mCornerX = 1
private var mCornerY = 1 private var mCornerY = 1
private val mPath0: Path = Path() private val mPath0: Path = Path()
private val mPath1: Path = Path() private val mPath1: Path = Path()
// 贝塞尔曲线起始点 // 贝塞尔曲线起始点
private val mBezierStart1 = PointF() private val mBezierStart1 = PointF()
// 贝塞尔曲线控制点 // 贝塞尔曲线控制点
@ -126,13 +128,13 @@ class SimulationPageDelegate(pageView: PageView) : HorizontalPageDelegate(pageVi
if ((startY > viewHeight / 3 && startY < viewHeight * 2 / 3) if ((startY > viewHeight / 3 && startY < viewHeight * 2 / 3)
|| mDirection == PageDirection.PREV || mDirection == PageDirection.PREV
) { ) {
pageView.touchY = viewHeight.toFloat() readView.touchY = viewHeight.toFloat()
} }
if (startY > viewHeight / 3 && startY < viewHeight / 2 if (startY > viewHeight / 3 && startY < viewHeight / 2
&& mDirection == PageDirection.NEXT && mDirection == PageDirection.NEXT
) { ) {
pageView.touchY = 1f readView.touchY = 1f
} }
} }
} }
@ -191,7 +193,7 @@ class SimulationPageDelegate(pageView: PageView) : HorizontalPageDelegate(pageVi
override fun onAnimStop() { override fun onAnimStop() {
if (!isCancel) { if (!isCancel) {
pageView.fillPage(mDirection) readView.fillPage(mDirection)
} }
} }

@ -2,10 +2,10 @@ package io.legado.app.ui.book.read.page.delegate
import android.graphics.Canvas import android.graphics.Canvas
import android.graphics.Matrix import android.graphics.Matrix
import io.legado.app.ui.book.read.page.PageView 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.PageDirection
class SlidePageDelegate(pageView: PageView) : HorizontalPageDelegate(pageView) { class SlidePageDelegate(readView: ReadView) : HorizontalPageDelegate(readView) {
private val bitmapMatrix = Matrix() private val bitmapMatrix = Matrix()
@ -55,7 +55,7 @@ class SlidePageDelegate(pageView: PageView) : HorizontalPageDelegate(pageView) {
override fun onAnimStop() { override fun onAnimStop() {
if (!isCancel) { if (!isCancel) {
pageView.fillPage(mDirection) readView.fillPage(mDirection)
} }
} }
} }

@ -1,6 +1,8 @@
package io.legado.app.ui.book.read.page package io.legado.app.ui.book.read.page.provider
import io.legado.app.service.help.ReadBook import io.legado.app.service.help.ReadBook
import io.legado.app.ui.book.read.page.api.DataSource
import io.legado.app.ui.book.read.page.api.PageFactory
import io.legado.app.ui.book.read.page.entities.PageData import io.legado.app.ui.book.read.page.entities.PageData
import io.legado.app.ui.book.read.page.entities.TextPage import io.legado.app.ui.book.read.page.entities.TextPage

@ -4,8 +4,8 @@
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="vertical"> android:orientation="vertical">
<io.legado.app.ui.book.read.page.PageView <io.legado.app.ui.book.read.page.ReadView
android:id="@+id/page_view" android:id="@+id/read_view"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" /> android:layout_height="match_parent" />

Loading…
Cancel
Save