pull/32/head
Administrator 5 years ago
parent b4eb0d0958
commit 8b3d956e7e
  1. 33
      app/src/main/java/io/legado/app/App.kt
  2. 3
      app/src/main/java/io/legado/app/base/BaseActivity.kt
  3. 6
      app/src/main/java/io/legado/app/help/ActivityHelp.kt
  4. 4
      app/src/main/java/io/legado/app/ui/main/my/MyFragment.kt
  5. 10
      app/src/main/java/io/legado/app/ui/widget/page/DataSource.kt
  6. 39
      app/src/main/java/io/legado/app/ui/widget/page/PageAnimDelegate.kt
  7. 10
      app/src/main/java/io/legado/app/ui/widget/page/PageFactory.kt
  8. 109
      app/src/main/java/io/legado/app/ui/widget/page/PageView.kt
  9. 8
      app/src/main/java/io/legado/app/ui/widget/page/TextPageFactory.kt
  10. 75
      app/src/main/java/io/legado/app/ui/widget/page/delegate/CoverPageDelegate.kt
  11. 247
      app/src/main/java/io/legado/app/ui/widget/page/delegate/PageDelegate.kt

@ -1,10 +1,12 @@
package io.legado.app
import android.app.Activity
import android.app.Application
import android.app.NotificationChannel
import android.app.NotificationManager
import android.content.Context
import android.os.Build
import android.os.Bundle
import androidx.annotation.RequiresApi
import androidx.appcompat.app.AppCompatDelegate
import com.jeremyliao.liveeventbus.LiveEventBus
@ -12,6 +14,7 @@ import io.legado.app.constant.AppConst.channelIdDownload
import io.legado.app.constant.AppConst.channelIdReadAloud
import io.legado.app.constant.AppConst.channelIdWeb
import io.legado.app.data.AppDatabase
import io.legado.app.help.ActivityHelp
import io.legado.app.lib.theme.ThemeStore
import io.legado.app.utils.getCompatColor
import io.legado.app.utils.getPrefInt
@ -53,6 +56,8 @@ class App : Application() {
.supportBroadcast(this)
.lifecycleObserverAlwaysActive(true)
.autoClear(false)
registerActivityLife()
}
/**
@ -134,4 +139,32 @@ class App : Application() {
it.createNotificationChannels(listOf(downloadChannel, readAloudChannel, webChannel))
}
}
private fun registerActivityLife(){
registerActivityLifecycleCallbacks(object : ActivityLifecycleCallbacks{
override fun onActivityPaused(activity: Activity) {
ActivityHelp.getInstance().add(activity)
}
override fun onActivityResumed(activity: Activity) {
}
override fun onActivityStarted(activity: Activity) {
}
override fun onActivityDestroyed(activity: Activity) {
ActivityHelp.getInstance().remove(activity)
}
override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle?) {
}
override fun onActivityStopped(activity: Activity) {
}
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
}
})
}
}

@ -8,7 +8,6 @@ import android.view.View
import android.view.WindowManager
import androidx.appcompat.app.AppCompatActivity
import io.legado.app.R
import io.legado.app.help.ActivityHelp
import io.legado.app.lib.theme.ATH
import io.legado.app.lib.theme.ColorUtils
import io.legado.app.lib.theme.ThemeStore
@ -27,7 +26,6 @@ abstract class BaseActivity(private val layoutID: Int, private val fullScreen: B
initTheme()
setupSystemBar()
super.onCreate(savedInstanceState)
ActivityHelp.getInstance()?.add(this)
setContentView(layoutID)
onActivityCreated(savedInstanceState)
observeLiveBus()
@ -36,7 +34,6 @@ abstract class BaseActivity(private val layoutID: Int, private val fullScreen: B
override fun onDestroy() {
super.onDestroy()
cancel()
ActivityHelp.getInstance()?.remove(this)
}
abstract fun onActivityCreated(savedInstanceState: Bundle?)

@ -16,7 +16,7 @@ class ActivityHelp private constructor() {
@Volatile
private var instance: ActivityHelp? = null
fun getInstance(): ActivityHelp? {
fun getInstance(): ActivityHelp {
if (null == instance) {
synchronized(ActivityHelp::class.java) {
if (null == instance) {
@ -24,7 +24,7 @@ class ActivityHelp private constructor() {
}
}
}
return instance
return instance!!
}
}
@ -69,7 +69,7 @@ class ActivityHelp private constructor() {
*/
fun finishActivity(vararg activities: Activity) {
for (activity in activities) {
activity?.finish()
activity.finish()
}
}

@ -6,7 +6,7 @@ import android.view.MenuItem
import android.view.View
import io.legado.app.R
import io.legado.app.base.BaseFragment
import io.legado.app.ui.chapterlist.ChapterListActivity
import io.legado.app.ui.readbook.ReadBookActivity
import io.legado.app.utils.startActivity
import kotlinx.android.synthetic.main.view_title_bar.*
@ -26,7 +26,7 @@ class MyFragment : BaseFragment(R.layout.fragment_my_config) {
override fun onCompatOptionsItemSelected(item: MenuItem) {
when (item.itemId) {
R.id.action_settings -> startActivity<ChapterListActivity>()
R.id.action_settings -> startActivity<ReadBookActivity>()
}
}
}

@ -4,9 +4,13 @@ interface DataSource {
fun getChapterPosition()
fun getChapter(position: Int): TextChapter
fun getChapter(position: Int): TextChapter?
fun getNextChapter(): TextChapter
fun getNextChapter(): TextChapter?
fun getPreviousChapter(): TextChapter
fun getPreviousChapter(): TextChapter?
fun hasNextChapter(): Boolean
fun hasPrevChapter(): Boolean
}

@ -1,39 +0,0 @@
package io.legado.app.ui.widget.page
import android.widget.Scroller
import androidx.interpolator.view.animation.FastOutLinearInInterpolator
abstract class PageAnimDelegate(protected val pageView: PageView) {
protected val scroller: Scroller = Scroller(pageView.context, FastOutLinearInInterpolator())
//起始点
protected var startX: Float = 0.toFloat()
protected var startY: Float = 0.toFloat()
//触碰点
protected var touchX: Float = 0.toFloat()
protected var touchY: Float = 0.toFloat()
//上一个触碰点
protected var lastX: Float = 0.toFloat()
protected var lastY: Float = 0.toFloat()
protected var isRunning = false
protected var isStarted = false
fun setStartPoint(x: Float, y: Float) {
startX = x
startY = y
lastX = startX
lastY = startY
}
fun setTouchPoint(x: Float, y: Float) {
lastX = touchX
lastY = touchY
touchX = x
touchY = y
}
}

@ -2,10 +2,14 @@ package io.legado.app.ui.widget.page
abstract class PageFactory<DATA>(protected val dataSource: DataSource) {
abstract fun pageAt(index: Int): DATA
abstract fun pageAt(index: Int): DATA?
abstract fun nextPage(): DATA
abstract fun nextPage(): DATA?
abstract fun previousPage(): DATA
abstract fun previousPage(): DATA?
abstract fun hasNext(): Boolean
abstract fun hasPrev(): Boolean
}

@ -1,44 +1,30 @@
package io.legado.app.ui.widget.page
import android.annotation.SuppressLint
import android.content.Context
import android.graphics.*
import android.graphics.drawable.GradientDrawable
import android.graphics.Canvas
import android.graphics.Color
import android.util.AttributeSet
import android.view.MotionEvent
import android.widget.FrameLayout
import io.legado.app.R
import io.legado.app.ui.widget.page.delegate.CoverPageDelegate
import io.legado.app.ui.widget.page.delegate.PageDelegate
import io.legado.app.utils.dp
import io.legado.app.utils.screenshot
import kotlinx.android.synthetic.main.view_book_page.view.*
import org.jetbrains.anko.backgroundColor
import org.jetbrains.anko.horizontalPadding
class PageView(context: Context, attrs: AttributeSet) : FrameLayout(context, attrs) {
class PageView(context: Context, attrs: AttributeSet) : FrameLayout(context, attrs), PageDelegate.PageInterface {
private var bitmap: Bitmap? = null
private var downX: Float = 0.toFloat()
private var offset: Float = 0.toFloat()
private var pageDelegate: PageDelegate? = null
private val shadowDrawableR: GradientDrawable
private val shadowDrawableL: GradientDrawable
private val bitmapMatrix = Matrix()
private var cover: Boolean = true
private var prevPage: ContentView? = null
private var curPage: ContentView? = null
private var nextPage: ContentView? = null
init {
val shadowColors = intArrayOf(0x66111111, 0x00000000)
shadowDrawableR = GradientDrawable(
GradientDrawable.Orientation.LEFT_RIGHT, shadowColors
)
shadowDrawableR.gradientType = GradientDrawable.LINEAR_GRADIENT
shadowDrawableL = GradientDrawable(
GradientDrawable.Orientation.RIGHT_LEFT, shadowColors
)
shadowDrawableL.gradientType = GradientDrawable.LINEAR_GRADIENT
inflate(context, R.layout.view_book_page, this)
setWillNotDraw(false)
@ -47,72 +33,41 @@ class PageView(context: Context, attrs: AttributeSet) : FrameLayout(context, att
page_panel.horizontalPadding = 16.dp
pageDelegate = CoverPageDelegate(this)
}
override fun dispatchDraw(canvas: Canvas?) {
super.dispatchDraw(canvas)
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
super.onSizeChanged(w, h, oldw, oldh)
bitmap?.let {
val dx = if (offset > 0) offset - width else offset + width
pageDelegate?.setViewSize(w, h)
}
bitmapMatrix.setTranslate(dx, 0.toFloat())
canvas?.drawBitmap(it, bitmapMatrix, null)
override fun dispatchDraw(canvas: Canvas) {
super.dispatchDraw(canvas)
if (cover) {
addShadow(dx.toInt(), canvas)
}
}
}
// bringChildToFront(prevPage)
private fun addShadow(left: Int, canvas: Canvas?) {
canvas?.let {
if (left < 0) {
shadowDrawableR.setBounds(left + width, 0, left+ width + 30, height)
shadowDrawableR.draw(it)
} else {
shadowDrawableL.setBounds(left - 30, 0, left, height)
shadowDrawableL.draw(it)
}
}
pageDelegate?.onPerform(canvas)
}
override fun onTouchEvent(event: MotionEvent?): Boolean {
when (event?.action) {
MotionEvent.ACTION_DOWN -> {
bitmap = page_panel.screenshot()
downX = event.x
offset = 0.toFloat()
if (!cover) {
page_panel.translationX = 0.toFloat()
}
invalidate()
}
MotionEvent.ACTION_MOVE -> {
offset = event.x - downX
if (!cover) {
page_panel.translationX = offset
}
invalidate()
}
MotionEvent.ACTION_UP -> {
bitmap = null
if (!cover) {
page_panel.translationX = 0.toFloat()
}
invalidate()
}
}
return true
override fun computeScroll() {
pageDelegate?.scroll()
}
fun setTranslate(translationX: Float, translationY: Float) {
page_panel.translationX = translationX
page_panel.translationY = translationY
@SuppressLint("ClickableViewAccessibility")
override fun onTouchEvent(event: MotionEvent): Boolean {
return pageDelegate?.onTouch(event) ?: super.onTouchEvent(event)
}
fun setPageFactory(factory: PageFactory<*>) {
}
override fun hasNext(): Boolean {
return true
}
override fun hasPrev(): Boolean {
return true
}
}

@ -12,6 +12,14 @@ class TextPageFactory private constructor(dataSource: DataSource) : PageFactory<
private var index: Int = 0
override fun hasPrev(): Boolean {
return true
}
override fun hasNext(): Boolean {
return true
}
override fun pageAt(index: Int): TextPage {
TODO("todo...")
}

@ -0,0 +1,75 @@
package io.legado.app.ui.widget.page.delegate
import android.graphics.Canvas
import android.graphics.Matrix
import android.graphics.drawable.GradientDrawable
import io.legado.app.ui.widget.page.PageView
class CoverPageDelegate(pageView: PageView) : PageDelegate(pageView) {
private val shadowDrawableR: GradientDrawable
private val shadowDrawableL: GradientDrawable
private val bitmapMatrix = Matrix()
init {
val shadowColors = intArrayOf(0x66111111, 0x00000000)
shadowDrawableR = GradientDrawable(
GradientDrawable.Orientation.LEFT_RIGHT, shadowColors
)
shadowDrawableR.gradientType = GradientDrawable.LINEAR_GRADIENT
shadowDrawableL = GradientDrawable(
GradientDrawable.Orientation.RIGHT_LEFT, shadowColors
)
shadowDrawableL.gradientType = GradientDrawable.LINEAR_GRADIENT
}
override fun onStart() {
val distanceX: Float
when (direction) {
Direction.NEXT -> if (isCancel) {
var dis = viewWidth - startX + touchX
if (dis > viewWidth) {
dis = viewWidth.toFloat()
}
distanceX = viewWidth - dis
} else {
distanceX = -(touchX + (viewWidth - startX))
}
else -> distanceX = if (isCancel) {
-(touchX - startX)
} else {
viewWidth - (touchX - startX)
}
}
scroller.startScroll(touchX.toInt(), 0, distanceX.toInt(), 0, getDuration(distanceX))
start()
}
override fun onPerform(canvas: Canvas) {
val offsetX = touchX - startX
if(offsetX == 0.toFloat()) return
val distanceX = if (offsetX > 0) offsetX - viewWidth else offsetX + viewWidth
bitmap?.let {
bitmapMatrix.setTranslate(distanceX, 0.toFloat())
canvas.drawBitmap(it, bitmapMatrix, null)
addShadow(distanceX.toInt(), canvas)
}
}
private fun addShadow(left: Int, canvas: Canvas?) {
canvas?.let {
if (left < 0) {
shadowDrawableR.setBounds(left + viewWidth, 0, left+ viewWidth + 30, viewHeight)
shadowDrawableR.draw(it)
} else {
shadowDrawableL.setBounds(left - 30, 0, left, viewHeight)
shadowDrawableL.draw(it)
}
}
}
}

@ -0,0 +1,247 @@
package io.legado.app.ui.widget.page.delegate
import android.graphics.Bitmap
import android.graphics.Canvas
import android.view.GestureDetector
import android.view.MotionEvent
import android.view.View
import android.widget.Scroller
import androidx.interpolator.view.animation.FastOutLinearInInterpolator
import io.legado.app.ui.widget.page.PageView
import io.legado.app.utils.screenshot
import kotlinx.android.synthetic.main.view_book_page.view.*
import kotlin.math.abs
abstract class PageDelegate(private val pageView: PageView) {
//起始点
protected var startX: Float = 0.toFloat()
protected var startY: Float = 0.toFloat()
//触碰点
protected var touchX: Float = 0.toFloat()
protected var touchY: Float = 0.toFloat()
protected val view: View
get() = pageView.page_panel
protected var bitmap: Bitmap? = null
protected var viewWidth: Int = pageView.width
protected var viewHeight: Int = pageView.height
protected val scroller: Scroller by lazy { Scroller(pageView.context, FastOutLinearInInterpolator()) }
private val detector: GestureDetector by lazy { GestureDetector(pageView.context, GestureListener()) }
private var isMoved = false
private var noNext = true
//移动方向
var direction = Direction.NONE
var isCancel = false
var isRunning = false
var isStarted = false
protected fun setStartPoint(x: Float, y: Float, invalidate: Boolean = true) {
startX = x
startY = y
if (invalidate) {
invalidate()
}
}
protected fun setTouchPoint(x: Float, y: Float, invalidate: Boolean = true) {
touchX = x
touchY = y
if (invalidate) {
invalidate()
}
}
fun setViewSize(width: Int, height: Int) {
viewWidth = width
viewHeight = height
invalidate()
}
fun invalidate() {
pageView.invalidate()
}
fun start() {
isRunning = true
isStarted = true
invalidate()
}
fun stop() {
isRunning = false
isStarted = false
bitmap = null
invalidate()
}
fun scroll() {
if (scroller.computeScrollOffset()) {
setTouchPoint(scroller.currX.toFloat(), scroller.currY.toFloat())
} else if(isStarted){
setTouchPoint(scroller.finalX.toFloat(), scroller.finalY.toFloat(), false)
stop()
}
}
fun abort() {
if (!scroller.isFinished) {
scroller.abortAnimation()
}
}
fun start(direction: Direction) {
if (isStarted) return
this.direction = direction
if (direction === Direction.NEXT) {
val x = viewWidth.toFloat()
val y = viewHeight.toFloat()
//初始化动画
setStartPoint(x, y, false)
//设置点击点
setTouchPoint(x, y, false)
//设置方向
val hasNext = pageView.hasNext()
if (!hasNext) {
return
}
} else {
val x = 0.toFloat()
val y = viewHeight.toFloat()
//初始化动画
setStartPoint(x, y, false)
//设置点击点
setTouchPoint(x, y, false)
//设置方向方向
val hashPrev = pageView.hasPrev()
if (!hashPrev) {
return
}
}
onStart()
}
fun onTouch(event: MotionEvent): Boolean {
if (isMoved && event.action == MotionEvent.ACTION_UP) {
// 开启翻页效果
if (!noNext) {
onStart()
}
return true
}
return detector.onTouchEvent(event)
}
fun getDuration(distance: Float): Int {
val duration = 300 * abs(distance) / viewWidth
return duration.toInt()
}
abstract fun onStart()
abstract fun onPerform(canvas: Canvas)
enum class Direction {
NONE, PREV, NEXT
}
private inner class GestureListener : GestureDetector.OnGestureListener {
override fun onDown(e: MotionEvent): Boolean {
abort()
//是否移动
isMoved = false
//是否存在下一章
noNext = false
//是否正在执行动画
isRunning = false
//取消
isCancel = false
//是下一章还是前一章
direction = Direction.NONE
//设置起始位置的触摸点
setStartPoint(e.x, e.y)
return true
}
override fun onShowPress(e: MotionEvent) {}
override fun onSingleTapUp(e: MotionEvent): Boolean {
val x = e.x
val y = e.y
direction = if (x > viewWidth / 2) Direction.NEXT else Direction.PREV
if (direction == Direction.NEXT) {
//判断是否下一页存在
val hasNext = pageView.hasNext()
//设置动画方向
if (!hasNext) {
return true
}
} else {
val hasPrev = pageView.hasPrev()
if (!hasPrev) {
return true
}
}
setTouchPoint(x, y)
return true
}
override fun onScroll(e1: MotionEvent, e2: MotionEvent, distanceX: Float, distanceY: Float): Boolean {
if (!isMoved && abs(distanceX) > abs(distanceY)) {
//上一页或下一页截图,还未处理
bitmap = pageView.screenshot()
if (distanceX < 0) {
//上一页的参数配置
direction = Direction.PREV
//判断是否上一页存在
val hasPrev = pageView.hasPrev()
//如果上一页不存在
if (!hasPrev) {
noNext = true
return true
}
} else {
//进行下一页的配置
direction = Direction.NEXT
//判断是否下一页存在
val hasNext = pageView.hasNext()
//如果不存在表示没有下一页了
if (!hasNext) {
noNext = true
return true
}
}
isMoved = true
}
if (isMoved) {
isCancel = if (direction == Direction.NEXT) distanceX < 0 else distanceX > 0
isRunning = true
//设置触摸点
setTouchPoint(e2.x, e2.y)
}
return isMoved
}
override fun onLongPress(e: MotionEvent) {
}
override fun onFling(e1: MotionEvent, e2: MotionEvent, velocityX: Float, velocityY: Float): Boolean {
return false
}
}
interface PageInterface {
fun hasNext(): Boolean
fun hasPrev(): Boolean
}
}
Loading…
Cancel
Save