渐变动画
This commit is contained in:
@@ -13,7 +13,8 @@ object PageAnim {
|
||||
|
||||
const val scrollPageAnim = 3
|
||||
|
||||
const val noAnim = 4
|
||||
const val fadePageAnim = 4
|
||||
const val noAnim = 5
|
||||
|
||||
@Target(AnnotationTarget.VALUE_PARAMETER)
|
||||
@Retention(AnnotationRetention.SOURCE)
|
||||
|
||||
@@ -133,7 +133,8 @@ class ReadStyleDialog : BaseBottomSheetDialogFragment(R.layout.dialog_read_book_
|
||||
R.id.rb_anim1 -> 1 // 滑动动画
|
||||
R.id.rb_simulation_anim -> 2 // 仿真翻页
|
||||
R.id.rb_scroll_anim -> 3 // 滚动动画
|
||||
R.id.rb_no_anim -> 4 // 无动画
|
||||
R.id.rb_fade_anim -> 4
|
||||
R.id.rb_no_anim -> 5 // 无动画
|
||||
else -> 0
|
||||
}
|
||||
callBack?.upPageAnim()
|
||||
|
||||
@@ -21,6 +21,7 @@ import io.legado.app.service.BaseReadAloudService
|
||||
import io.legado.app.ui.book.read.ContentEditDialog
|
||||
import io.legado.app.ui.book.read.page.api.DataSource
|
||||
import io.legado.app.ui.book.read.page.delegate.CoverPageDelegate
|
||||
import io.legado.app.ui.book.read.page.delegate.FadePageDelegate
|
||||
import io.legado.app.ui.book.read.page.delegate.HorizontalPageDelegate
|
||||
import io.legado.app.ui.book.read.page.delegate.NoAnimPageDelegate
|
||||
import io.legado.app.ui.book.read.page.delegate.PageDelegate
|
||||
@@ -537,6 +538,10 @@ class ReadView(context: Context, attrs: AttributeSet) :
|
||||
pageDelegate = ScrollPageDelegate(this)
|
||||
}
|
||||
|
||||
PageAnim.fadePageAnim -> if (pageDelegate !is FadePageDelegate) {
|
||||
pageDelegate = FadePageDelegate(this)
|
||||
}
|
||||
|
||||
else -> if (pageDelegate !is NoAnimPageDelegate) {
|
||||
pageDelegate = NoAnimPageDelegate(this)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,216 @@
|
||||
package io.legado.app.ui.book.read.page.delegate
|
||||
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Paint
|
||||
import android.view.MotionEvent
|
||||
import io.legado.app.ui.book.read.page.ReadView
|
||||
import io.legado.app.ui.book.read.page.entities.PageDirection
|
||||
import io.legado.app.utils.canvasrecorder.CanvasRecorderFactory
|
||||
import io.legado.app.utils.screenshot
|
||||
|
||||
class FadePageDelegate(readView: ReadView) : PageDelegate(readView) {
|
||||
|
||||
private var curRecorder = CanvasRecorderFactory.create()
|
||||
private var prevRecorder = CanvasRecorderFactory.create()
|
||||
private var nextRecorder = CanvasRecorderFactory.create()
|
||||
private val slopSquare get() = readView.pageSlopSquare2
|
||||
|
||||
private var fadeProgress = 0f
|
||||
private val flipThreshold = 0.2f
|
||||
|
||||
override fun setDirection(direction: PageDirection) {
|
||||
super.setDirection(direction)
|
||||
fadeProgress = 0f
|
||||
setBitmap()
|
||||
}
|
||||
|
||||
private fun setBitmap() {
|
||||
when (mDirection) {
|
||||
PageDirection.PREV -> {
|
||||
prevPage.screenshot(prevRecorder)
|
||||
curPage.screenshot(curRecorder)
|
||||
}
|
||||
PageDirection.NEXT -> {
|
||||
nextPage.screenshot(nextRecorder)
|
||||
curPage.screenshot(curRecorder)
|
||||
}
|
||||
else -> {
|
||||
curPage.screenshot(curRecorder)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun upRecorder() {
|
||||
curRecorder.recycle()
|
||||
prevRecorder.recycle()
|
||||
nextRecorder.recycle()
|
||||
curRecorder = CanvasRecorderFactory.create()
|
||||
prevRecorder = CanvasRecorderFactory.create()
|
||||
nextRecorder = CanvasRecorderFactory.create()
|
||||
}
|
||||
|
||||
override fun onTouch(event: MotionEvent) {
|
||||
when (event.action) {
|
||||
MotionEvent.ACTION_DOWN -> {
|
||||
abortAnim()
|
||||
readView.setStartPoint(event.x, event.y, false)
|
||||
}
|
||||
MotionEvent.ACTION_MOVE -> onScroll(event)
|
||||
MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
|
||||
if (!isMoved || mDirection == PageDirection.NONE) return
|
||||
// 超过阈值自动翻页,否则回弹
|
||||
val shouldFlip = fadeProgress >= flipThreshold
|
||||
if (shouldFlip) 1f else 0f
|
||||
isCancel = !shouldFlip
|
||||
onAnimStart(readView.defaultAnimationSpeed)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun onScroll(event: MotionEvent) {
|
||||
val focusX = event.x
|
||||
val deltaX = focusX - startX
|
||||
if (!isMoved) {
|
||||
val distance = deltaX * deltaX
|
||||
isMoved = distance > slopSquare
|
||||
if (isMoved) {
|
||||
animDirectionByDelta(deltaX)
|
||||
readView.setStartPoint(event.x, event.y, false)
|
||||
}
|
||||
}
|
||||
|
||||
if (isMoved && mDirection != PageDirection.NONE) {
|
||||
fadeProgress = (kotlin.math.abs(deltaX) / viewWidth).coerceIn(0f, 1f)
|
||||
isRunning = true
|
||||
readView.setTouchPoint(focusX, startY)
|
||||
readView.invalidate()
|
||||
}
|
||||
}
|
||||
|
||||
private fun animDirectionByDelta(deltaX: Float) {
|
||||
mDirection = if (deltaX > 0) {
|
||||
if (!hasPrev()) PageDirection.NONE else PageDirection.PREV
|
||||
} else {
|
||||
if (!hasNext()) PageDirection.NONE else PageDirection.NEXT
|
||||
}
|
||||
setBitmap()
|
||||
}
|
||||
|
||||
override fun onDraw(canvas: Canvas) {
|
||||
curRecorder.draw(canvas)
|
||||
if (mDirection == PageDirection.NONE) {
|
||||
// 直接绘制 curPage,不使用 curRecorder
|
||||
curPage.draw(canvas)
|
||||
return
|
||||
}
|
||||
|
||||
val alpha = (fadeProgress * 255).toInt().coerceIn(0, 255)
|
||||
val paint = Paint().apply { this.alpha = alpha }
|
||||
|
||||
val save = canvas.saveLayer(0f, 0f, viewWidth.toFloat(), viewHeight.toFloat(), paint)
|
||||
when (mDirection) {
|
||||
PageDirection.PREV -> prevRecorder.draw(canvas)
|
||||
PageDirection.NEXT -> nextRecorder.draw(canvas)
|
||||
else -> {}
|
||||
}
|
||||
canvas.restoreToCount(save)
|
||||
}
|
||||
|
||||
override fun onAnimStart(animationSpeed: Int) {
|
||||
if (mDirection == PageDirection.NONE) return
|
||||
// 使用 scroller 做松手后的自动动画
|
||||
val start = (fadeProgress * 1000).toInt()
|
||||
val end = if (isCancel) 0 else 1000
|
||||
scroller.startScroll(start, 0, end - start, 0, animationSpeed)
|
||||
isStarted = true
|
||||
isRunning = true
|
||||
readView.invalidate()
|
||||
}
|
||||
|
||||
override fun computeScroll() {
|
||||
if (scroller.computeScrollOffset()) {
|
||||
fadeProgress = scroller.currX / 1000f
|
||||
readView.invalidate()
|
||||
} else if (isStarted) {
|
||||
onAnimStop()
|
||||
stopScroll()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onAnimStop() {
|
||||
if (!isCancel) {
|
||||
// 更新当前页显示
|
||||
readView.fillPage(mDirection)
|
||||
// 延迟更新 curRecorder,确保 curPage 已经刷新
|
||||
readView.post {
|
||||
try {
|
||||
curPage.screenshot(curRecorder)
|
||||
readView.invalidate()
|
||||
} catch (_: Exception) {
|
||||
// 捕获可能的异常,避免闪退
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 重置状态
|
||||
fadeProgress = 0f
|
||||
isRunning = false
|
||||
isMoved = false
|
||||
|
||||
mDirection = PageDirection.NONE
|
||||
readView.setStartPoint(0f, 0f, false)
|
||||
readView.setTouchPoint(0f, 0f)
|
||||
}
|
||||
|
||||
|
||||
override fun abortAnim() {
|
||||
isStarted = false
|
||||
isMoved = false
|
||||
isRunning = false
|
||||
if (!scroller.isFinished) {
|
||||
scroller.abortAnimation()
|
||||
if (!isCancel) {
|
||||
readView.fillPage(mDirection)
|
||||
curPage.screenshot(curRecorder) // 同步 curRecorder
|
||||
readView.invalidate()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun nextPageByAnim(animationSpeed: Int) {
|
||||
if (!hasNext()) return
|
||||
setDirection(PageDirection.NEXT)
|
||||
fadeProgress = 0f
|
||||
isCancel = false
|
||||
onAnimStart(animationSpeed)
|
||||
}
|
||||
|
||||
override fun prevPageByAnim(animationSpeed: Int) {
|
||||
if (!hasPrev()) return
|
||||
setDirection(PageDirection.PREV)
|
||||
fadeProgress = 0f
|
||||
isCancel = false
|
||||
onAnimStart(animationSpeed)
|
||||
}
|
||||
|
||||
override fun onDown() {
|
||||
super.onDown()
|
||||
fadeProgress = 0f
|
||||
mDirection = PageDirection.NONE
|
||||
isMoved = false
|
||||
isCancel = false
|
||||
readView.setStartPoint(0f, 0f, false)
|
||||
}
|
||||
|
||||
override fun setViewSize(width: Int, height: Int) {
|
||||
super.setViewSize(width, height)
|
||||
upRecorder()
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
curRecorder.recycle()
|
||||
prevRecorder.recycle()
|
||||
nextRecorder.recycle()
|
||||
}
|
||||
}
|
||||
@@ -141,7 +141,7 @@ abstract class PageDelegate(protected val readView: ReadView) {
|
||||
/**
|
||||
* 按下
|
||||
*/
|
||||
fun onDown() {
|
||||
open fun onDown() {
|
||||
//是否移动
|
||||
isMoved = false
|
||||
//是否存在下一章
|
||||
|
||||
@@ -21,7 +21,6 @@ import io.legado.app.utils.sendToClip
|
||||
import io.legado.app.utils.setOnApplyWindowInsetsListenerCompat
|
||||
import io.legado.app.utils.showHelp
|
||||
import io.legado.app.utils.viewbindingdelegate.viewBinding
|
||||
import androidx.core.content.edit
|
||||
import io.legado.app.data.appDb
|
||||
import io.legado.app.lib.dialogs.alert
|
||||
import io.legado.app.utils.toastOnUi
|
||||
|
||||
Reference in New Issue
Block a user