渐变动画
This commit is contained in:
@@ -13,7 +13,8 @@ object PageAnim {
|
|||||||
|
|
||||||
const val scrollPageAnim = 3
|
const val scrollPageAnim = 3
|
||||||
|
|
||||||
const val noAnim = 4
|
const val fadePageAnim = 4
|
||||||
|
const val noAnim = 5
|
||||||
|
|
||||||
@Target(AnnotationTarget.VALUE_PARAMETER)
|
@Target(AnnotationTarget.VALUE_PARAMETER)
|
||||||
@Retention(AnnotationRetention.SOURCE)
|
@Retention(AnnotationRetention.SOURCE)
|
||||||
|
|||||||
@@ -133,7 +133,8 @@ class ReadStyleDialog : BaseBottomSheetDialogFragment(R.layout.dialog_read_book_
|
|||||||
R.id.rb_anim1 -> 1 // 滑动动画
|
R.id.rb_anim1 -> 1 // 滑动动画
|
||||||
R.id.rb_simulation_anim -> 2 // 仿真翻页
|
R.id.rb_simulation_anim -> 2 // 仿真翻页
|
||||||
R.id.rb_scroll_anim -> 3 // 滚动动画
|
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
|
else -> 0
|
||||||
}
|
}
|
||||||
callBack?.upPageAnim()
|
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.ContentEditDialog
|
||||||
import io.legado.app.ui.book.read.page.api.DataSource
|
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.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.HorizontalPageDelegate
|
||||||
import io.legado.app.ui.book.read.page.delegate.NoAnimPageDelegate
|
import io.legado.app.ui.book.read.page.delegate.NoAnimPageDelegate
|
||||||
import io.legado.app.ui.book.read.page.delegate.PageDelegate
|
import io.legado.app.ui.book.read.page.delegate.PageDelegate
|
||||||
@@ -537,6 +538,10 @@ class ReadView(context: Context, attrs: AttributeSet) :
|
|||||||
pageDelegate = ScrollPageDelegate(this)
|
pageDelegate = ScrollPageDelegate(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PageAnim.fadePageAnim -> if (pageDelegate !is FadePageDelegate) {
|
||||||
|
pageDelegate = FadePageDelegate(this)
|
||||||
|
}
|
||||||
|
|
||||||
else -> if (pageDelegate !is NoAnimPageDelegate) {
|
else -> if (pageDelegate !is NoAnimPageDelegate) {
|
||||||
pageDelegate = NoAnimPageDelegate(this)
|
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
|
isMoved = false
|
||||||
//是否存在下一章
|
//是否存在下一章
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ import io.legado.app.utils.sendToClip
|
|||||||
import io.legado.app.utils.setOnApplyWindowInsetsListenerCompat
|
import io.legado.app.utils.setOnApplyWindowInsetsListenerCompat
|
||||||
import io.legado.app.utils.showHelp
|
import io.legado.app.utils.showHelp
|
||||||
import io.legado.app.utils.viewbindingdelegate.viewBinding
|
import io.legado.app.utils.viewbindingdelegate.viewBinding
|
||||||
import androidx.core.content.edit
|
|
||||||
import io.legado.app.data.appDb
|
import io.legado.app.data.appDb
|
||||||
import io.legado.app.lib.dialogs.alert
|
import io.legado.app.lib.dialogs.alert
|
||||||
import io.legado.app.utils.toastOnUi
|
import io.legado.app.utils.toastOnUi
|
||||||
|
|||||||
@@ -216,6 +216,13 @@
|
|||||||
android:checkable="true"
|
android:checkable="true"
|
||||||
android:text="@string/page_anim_scroll" />
|
android:text="@string/page_anim_scroll" />
|
||||||
|
|
||||||
|
<com.google.android.material.chip.Chip
|
||||||
|
android:id="@+id/rb_fade_anim"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:checkable="true"
|
||||||
|
android:text="@string/page_anim_fade" />
|
||||||
|
|
||||||
<com.google.android.material.chip.Chip
|
<com.google.android.material.chip.Chip
|
||||||
android:id="@+id/rb_no_anim"
|
android:id="@+id/rb_no_anim"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
|
|||||||
@@ -1347,4 +1347,5 @@
|
|||||||
<string name="title_bar_mode">菜单标题显示方式</string>
|
<string name="title_bar_mode">菜单标题显示方式</string>
|
||||||
<string name="sure_cache_book">是否确认开始缓存?</string>
|
<string name="sure_cache_book">是否确认开始缓存?</string>
|
||||||
<string name="replace_purify_badge">计数替换净化</string>
|
<string name="replace_purify_badge">计数替换净化</string>
|
||||||
|
<string name="page_anim_fade">渐变</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -1348,4 +1348,5 @@
|
|||||||
<string name="title_bar_mode">菜单标题显示方式</string>
|
<string name="title_bar_mode">菜单标题显示方式</string>
|
||||||
<string name="sure_cache_book">是否确认开始缓存?</string>
|
<string name="sure_cache_book">是否确认开始缓存?</string>
|
||||||
<string name="replace_purify_badge">计数替换净化</string>
|
<string name="replace_purify_badge">计数替换净化</string>
|
||||||
|
<string name="page_anim_fade">渐变</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
Reference in New Issue
Block a user