优化翻页

pull/357/head
gedoor 5 years ago
parent 2ad3c62251
commit 9595eb8990
  1. 2
      app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt
  2. 8
      app/src/main/java/io/legado/app/ui/book/read/page/ContentView.kt
  3. 187
      app/src/main/java/io/legado/app/ui/book/read/page/PageView.kt
  4. 14
      app/src/main/java/io/legado/app/ui/book/read/page/delegate/HorizontalPageDelegate.kt
  5. 5
      app/src/main/java/io/legado/app/ui/book/read/page/delegate/NoAnimPageDelegate.kt
  6. 192
      app/src/main/java/io/legado/app/ui/book/read/page/delegate/PageDelegate.kt
  7. 16
      app/src/main/java/io/legado/app/ui/book/read/page/delegate/ScrollPageDelegate.kt
  8. 21
      app/src/main/java/io/legado/app/ui/book/read/page/delegate/SimulationPageDelegate.kt

@ -496,7 +496,7 @@ class ReadBookActivity : VMBaseActivity<ReadBookViewModel>(R.layout.activity_boo
override fun onMenuActionFinally() {
textActionMenu?.dismiss()
page_view.curPage.cancelSelect()
page_view.pageDelegate?.isTextSelected = false
page_view.isTextSelected = false
}
/**

@ -3,7 +3,6 @@ package io.legado.app.ui.book.read.page
import android.annotation.SuppressLint
import android.content.Context
import android.graphics.drawable.Drawable
import android.view.MotionEvent
import android.widget.FrameLayout
import androidx.core.view.isGone
import androidx.core.view.isInvisible
@ -222,11 +221,10 @@ class ContentView(context: Context) : FrameLayout(context) {
}
fun selectText(
e: MotionEvent,
select: (relativePage: Int, lineIndex: Int, charIndex: Int) -> Unit
x: Float, y: Float,
select: (relativePage: Int, lineIndex: Int, charIndex: Int) -> Unit,
) {
val y = e.y - headerHeight
return content_text_view.selectText(e.x, y, select)
return content_text_view.selectText(x, y - headerHeight, select)
}
fun selectStartMove(x: Float, y: Float) {

@ -5,9 +5,12 @@ 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
@ -17,6 +20,7 @@ import io.legado.app.ui.book.read.page.provider.ChapterProvider
import io.legado.app.utils.activity
import io.legado.app.utils.screenshot
import kotlinx.android.synthetic.main.activity_book_read.view.*
import kotlin.math.abs
class PageView(context: Context, attrs: AttributeSet) :
FrameLayout(context, attrs),
@ -29,6 +33,37 @@ class PageView(context: Context, attrs: AttributeSet) :
var prevPage: ContentView = ContentView(context)
var curPage: ContentView = ContentView(context)
var nextPage: ContentView = ContentView(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
//长按
private var longPressed = false
private val longPressTimeout = 1000L
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 centerRectF = RectF(width * 0.33f, height * 0.33f, width * 0.66f, height * 0.66f)
private val autoPageRect by lazy { Rect() }
private val autoPagePint by lazy {
Paint().apply {
@ -49,6 +84,7 @@ class PageView(context: Context, attrs: AttributeSet) :
super.onSizeChanged(w, h, oldw, oldh)
prevPage.x = -w.toFloat()
pageDelegate?.setViewSize(w, h)
centerRectF.set(width * 0.33f, height * 0.33f, width * 0.66f, height * 0.66f)
if (oldw != 0 && oldh != 0) {
ReadBook.loadContent(resetPageOffset = false)
}
@ -82,12 +118,161 @@ class PageView(context: Context, attrs: AttributeSet) :
return true
}
/**
* 触摸事件
*/
@SuppressLint("ClickableViewAccessibility")
override fun onTouchEvent(event: MotionEvent): Boolean {
pageDelegate?.onTouch(event)
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
setStartPoint(event.x, event.y)
pageDelegate?.onTouch(event)
}
MotionEvent.ACTION_MOVE -> {
pressDown = true
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
if (!isMove) {
if (!longPressed && !pressOnTextSelected) {
onSingleTapUp()
return true
}
}
if (isTextSelected) {
callBack.showTextActionMenu()
} else if (isMove) {
pageDelegate?.onTouch(event)
}
pressOnTextSelected = false
}
}
return true
}
/**
* 保存开始位置
*/
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
}
}
/**
* 单击
*/
private fun onSingleTapUp(): Boolean {
if (isTextSelected) {
isTextSelected = false
return true
}
if (centerRectF.contains(startX, startY)) {
callBack.clickCenter()
} else if (ReadBookConfig.clickTurnPage) {
if (startX > width / 2 || AppConfig.clickAllNext) {
pageDelegate?.nextPageByAnim(defaultAnimationSpeed)
} else {
pageDelegate?.prevPageByAnim(defaultAnimationSpeed)
}
}
return true
}
/**
* 选择文本
*/
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()

@ -43,14 +43,12 @@ abstract class HorizontalPageDelegate(pageView: PageView) : PageDelegate(pageVie
}
}
MotionEvent.ACTION_MOVE -> {
if (isTextSelected) {
selectText(event)
} else {
onScroll(event)
}
MotionEvent.ACTION_CANCEL, MotionEvent.ACTION_UP -> {
onAnimStart(pageView.defaultAnimationSpeed)
}
}
super.onTouch(event)
}
private fun onScroll(event: MotionEvent) {
@ -76,7 +74,7 @@ abstract class HorizontalPageDelegate(pageView: PageView) : PageDelegate(pageVie
val deltaX = (focusX - startX).toInt()
val deltaY = (focusY - startY).toInt()
val distance = deltaX * deltaX + deltaY * deltaY
isMoved = distance > slopSquare
isMoved = distance > pageView.slopSquare
if (isMoved) {
if (sumX - startX > 0) {
//如果上一页不存在
@ -99,7 +97,7 @@ abstract class HorizontalPageDelegate(pageView: PageView) : PageDelegate(pageVie
isCancel = if (mDirection == Direction.NEXT) sumX > lastX else sumX < lastX
isRunning = true
//设置触摸点
setTouchPoint(sumX, sumY)
pageView.setTouchPoint(sumX, sumY)
}
}
@ -107,7 +105,7 @@ abstract class HorizontalPageDelegate(pageView: PageView) : PageDelegate(pageVie
abort()
if (!hasNext()) return
setDirection(Direction.NEXT)
setTouchPoint(viewWidth.toFloat(), 0f)
pageView.setTouchPoint(viewWidth.toFloat(), 0f)
onAnimStart(animationSpeed)
}
@ -115,7 +113,7 @@ abstract class HorizontalPageDelegate(pageView: PageView) : PageDelegate(pageVie
abort()
if (!hasPrev()) return
setDirection(Direction.PREV)
setTouchPoint(0f, 0f)
pageView.setTouchPoint(0f, 0f)
onAnimStart(animationSpeed)
}

@ -1,6 +1,5 @@
package io.legado.app.ui.book.read.page.delegate
import android.view.MotionEvent
import io.legado.app.ui.book.read.page.PageView
class NoAnimPageDelegate(pageView: PageView) : HorizontalPageDelegate(pageView) {
@ -12,8 +11,4 @@ class NoAnimPageDelegate(pageView: PageView) : HorizontalPageDelegate(pageView)
stopScroll()
}
override fun onDoubleTap(e: MotionEvent): Boolean {
return onSingleTapUp(e)
}
}

@ -2,40 +2,31 @@ package io.legado.app.ui.book.read.page.delegate
import android.content.Context
import android.graphics.Canvas
import android.graphics.RectF
import android.view.GestureDetector
import android.view.MotionEvent
import android.view.ViewConfiguration
import android.view.animation.DecelerateInterpolator
import android.widget.Scroller
import androidx.annotation.CallSuper
import com.google.android.material.snackbar.Snackbar
import io.legado.app.R
import io.legado.app.help.AppConfig
import io.legado.app.help.ReadBookConfig
import io.legado.app.ui.book.read.page.ContentView
import io.legado.app.ui.book.read.page.PageView
import kotlin.math.abs
abstract class PageDelegate(protected val pageView: PageView) :
GestureDetector.SimpleOnGestureListener() {
private val centerRectF = RectF(
pageView.width * 0.33f, pageView.height * 0.33f,
pageView.width * 0.66f, pageView.height * 0.66f
)
abstract class PageDelegate(protected val pageView: PageView) {
protected val context: Context = pageView.context
private val defaultAnimationSpeed = 300
private val longPressTimeout = 1000
//起始点
protected var startX: Float = 0f
protected var startY: Float = 0f
protected val startX: Float get() = pageView.startX
protected val startY: Float get() = pageView.startY
//上一个触碰点
protected var lastX: Float = 0f
protected var lastY: Float = 0f
protected val lastX: Float get() = pageView.lastX
protected val lastY: Float get() = pageView.lastY
//触碰点
protected var touchX: Float = 0f
protected var touchY: Float = 0f
protected val touchX: Float get() = pageView.touchX
protected val touchY: Float get() = pageView.touchY
protected val nextPage: ContentView get() = pageView.nextPage
protected val curPage: ContentView get() = pageView.curPage
@ -48,15 +39,6 @@ abstract class PageDelegate(protected val pageView: PageView) :
Scroller(pageView.context, DecelerateInterpolator())
}
protected val slopSquare by lazy {
val scaledTouchSlop = ViewConfiguration.get(context).scaledTouchSlop
scaledTouchSlop * scaledTouchSlop
}
private val detector: GestureDetector by lazy {
GestureDetector(pageView.context, this)
}
private val snackBar: Snackbar by lazy {
Snackbar.make(pageView, "", Snackbar.LENGTH_SHORT)
}
@ -69,43 +51,13 @@ abstract class PageDelegate(protected val pageView: PageView) :
var isCancel = false
var isRunning = false
private var isStarted = false
var isTextSelected = false
private var selectedOnDown = false
private var firstRelativePage = 0
private var firstLineIndex: Int = 0
private var firstCharIndex: Int = 0
private var selectedOnDown = false
init {
curPage.resetPageOffset()
}
open fun setStartPoint(x: Float, y: Float, invalidate: Boolean = true) {
startX = x
startY = y
lastX = x
lastY = y
touchX = x
touchY = y
if (invalidate) {
pageView.invalidate()
}
}
open fun setTouchPoint(x: Float, y: Float, invalidate: Boolean = true) {
lastX = touchX
lastY = touchY
touchX = x
touchY = y
if (invalidate) {
pageView.invalidate()
}
onScroll()
}
open fun fling(
startX: Int, startY: Int, velocityX: Int, velocityY: Int,
minX: Int, maxX: Int, minY: Int, maxY: Int
@ -140,16 +92,11 @@ abstract class PageDelegate(protected val pageView: PageView) :
open fun setViewSize(width: Int, height: Int) {
viewWidth = width
viewHeight = height
pageView.invalidate()
centerRectF.set(
width * 0.33f, height * 0.33f,
width * 0.66f, height * 0.66f
)
}
fun scroll() {
if (scroller.computeScrollOffset()) {
setTouchPoint(scroller.currX.toFloat(), scroller.currY.toFloat())
pageView.setTouchPoint(scroller.currX.toFloat(), scroller.currY.toFloat())
} else if (isStarted) {
onAnimStop()
stopScroll()
@ -193,50 +140,12 @@ abstract class PageDelegate(protected val pageView: PageView) :
/**
* 触摸事件处理
*/
@CallSuper
open fun onTouch(event: MotionEvent) {
abort()
//获取点击位置
val x = event.x
val y = event.y
setTouchPoint(x, y, false)
when (event.action) {
MotionEvent.ACTION_DOWN -> {
setStartPoint(x, y, false)
}
MotionEvent.ACTION_MOVE -> {
}
}
if (!detector.onTouchEvent(event)) {
//GestureDetector.onFling小幅移动不会触发,所以要自己判断
when (event.action) {
MotionEvent.ACTION_UP,
MotionEvent.ACTION_CANCEL -> {
if (isTextSelected) {
pageView.callBack.showTextActionMenu()
}
if (isMoved) {
if (selectedOnDown) {
selectedOnDown = false
}
if (!noNext) onAnimStart(defaultAnimationSpeed)
}
}
}
}
}
abstract fun onTouch(event: MotionEvent)
/**
* 按下
*/
override fun onDown(e: MotionEvent): Boolean {
if (isTextSelected) {
curPage.cancelSelect()
isTextSelected = false
selectedOnDown = true
}
fun onDown(): Boolean {
//是否移动
isMoved = false
//是否存在下一章
@ -248,82 +157,9 @@ abstract class PageDelegate(protected val pageView: PageView) :
//是下一章还是前一章
setDirection(Direction.NONE)
//设置起始位置的触摸点
setStartPoint(e.x, e.y)
return true
}
/**
* 单击
*/
override fun onSingleTapUp(e: MotionEvent): Boolean {
if (selectedOnDown) {
selectedOnDown = false
return true
}
if (isMoved) {
if (!noNext) onAnimStart(defaultAnimationSpeed)
return true
}
val x = e.x
val y = e.y
if (centerRectF.contains(x, y)) {
pageView.callBack.clickCenter()
setTouchPoint(x, y)
} else if (ReadBookConfig.clickTurnPage) {
if (x > viewWidth / 2 ||
AppConfig.clickAllNext
) {
nextPageByAnim(defaultAnimationSpeed)
} else {
prevPageByAnim(defaultAnimationSpeed)
}
}
return true
}
/**
* 长按选择
*/
override fun onLongPress(e: MotionEvent) {
curPage.selectText(e) { relativePage, lineIndex, charIndex ->
isTextSelected = true
firstRelativePage = relativePage
firstLineIndex = lineIndex
firstCharIndex = charIndex
}
}
protected fun selectText(event: MotionEvent) {
curPage.selectText(event) { 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)
}
}
}
}
/**
* 判断是否有上一页
*/

@ -27,19 +27,17 @@ class ScrollPageDelegate(pageView: PageView) : PageDelegate(pageView) {
override fun onTouch(event: MotionEvent) {
when (event.action) {
MotionEvent.ACTION_DOWN -> {
setStartPoint(event.x, event.y)
pageView.setStartPoint(event.x, event.y)
abort()
mVelocity.clear()
}
MotionEvent.ACTION_MOVE -> {
if (isTextSelected) {
selectText(event)
} else {
onScroll(event)
}
MotionEvent.ACTION_CANCEL, MotionEvent.ACTION_UP -> {
onAnimStart(pageView.defaultAnimationSpeed)
}
}
super.onTouch(event)
}
private fun onScroll(event: MotionEvent) {
@ -61,12 +59,12 @@ class ScrollPageDelegate(pageView: PageView) : PageDelegate(pageView) {
val div = if (pointerUp) count - 1 else count
val focusX = sumX / div
val focusY = sumY / div
setTouchPoint(sumX, sumY)
pageView.setTouchPoint(sumX, sumY)
if (!isMoved) {
val deltaX = (focusX - startX).toInt()
val deltaY = (focusY - startY).toInt()
val distance = deltaX * deltaX + deltaY * deltaY
isMoved = distance > slopSquare
isMoved = distance > pageView.slopSquare
}
if (isMoved) {
isRunning = true
@ -80,13 +78,13 @@ class ScrollPageDelegate(pageView: PageView) : PageDelegate(pageView) {
override fun nextPageByAnim(animationSpeed: Int) {
abort()
setStartPoint(0f, 0f, false)
pageView.setStartPoint(0f, 0f, false)
startScroll(0, 0, 0, -ChapterProvider.visibleHeight, animationSpeed)
}
override fun prevPageByAnim(animationSpeed: Int) {
abort()
setStartPoint(0f, 0f, false)
pageView.setStartPoint(0f, 0f, false)
startScroll(0, 0, 0, ChapterProvider.visibleHeight, animationSpeed)
}
}

@ -3,6 +3,7 @@ package io.legado.app.ui.book.read.page.delegate
import android.graphics.*
import android.graphics.drawable.GradientDrawable
import android.os.Build
import android.view.MotionEvent
import io.legado.app.help.ReadBookConfig
import io.legado.app.ui.book.read.page.PageView
import kotlin.math.*
@ -114,26 +115,26 @@ class SimulationPageDelegate(pageView: PageView) : HorizontalPageDelegate(pageVi
mMaxLength = hypot(viewWidth.toDouble(), viewHeight.toDouble()).toFloat()
}
override fun setStartPoint(x: Float, y: Float, invalidate: Boolean) {
super.setStartPoint(x, y, false)
calcCornerXY(x, y)
override fun onTouch(event: MotionEvent) {
super.onTouch(event)
when (event.action) {
MotionEvent.ACTION_DOWN -> {
calcCornerXY(event.x, event.y)
}
override fun setTouchPoint(x: Float, y: Float, invalidate: Boolean) {
super.setTouchPoint(x, y, false)
//触摸y中间位置吧y变成屏幕高度
MotionEvent.ACTION_MOVE -> {
if ((startY > viewHeight / 3 && startY < viewHeight * 2 / 3)
|| mDirection == Direction.PREV
) {
touchY = viewHeight.toFloat()
pageView.touchY = viewHeight.toFloat()
}
if (startY > viewHeight / 3 && startY < viewHeight / 2
&& mDirection == Direction.NEXT
) {
touchY = 1f
pageView.touchY = 1f
}
}
}
pageView.invalidate()
}
override fun setDirection(direction: Direction) {

Loading…
Cancel
Save