Parallax scrolling to Manga details page

This commit is contained in:
Jay 2020-03-28 18:13:36 -04:00
parent 0d658d4b05
commit 497f965cba
2 changed files with 83 additions and 31 deletions

View File

@ -35,6 +35,7 @@ import androidx.core.content.pm.ShortcutInfoCompat
import androidx.core.content.pm.ShortcutManagerCompat import androidx.core.content.pm.ShortcutManagerCompat
import androidx.core.graphics.ColorUtils import androidx.core.graphics.ColorUtils
import androidx.core.graphics.drawable.IconCompat import androidx.core.graphics.drawable.IconCompat
import androidx.core.math.MathUtils
import androidx.palette.graphics.Palette import androidx.palette.graphics.Palette
import androidx.recyclerview.widget.DividerItemDecoration import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.ItemTouchHelper
@ -100,8 +101,6 @@ import eu.kanade.tachiyomi.util.view.setOnQueryTextChangeListener
import eu.kanade.tachiyomi.util.view.snack import eu.kanade.tachiyomi.util.view.snack
import eu.kanade.tachiyomi.util.view.updateLayoutParams import eu.kanade.tachiyomi.util.view.updateLayoutParams
import eu.kanade.tachiyomi.util.view.updatePaddingRelative import eu.kanade.tachiyomi.util.view.updatePaddingRelative
import java.io.File
import java.io.IOException
import jp.wasabeef.glide.transformations.CropSquareTransformation import jp.wasabeef.glide.transformations.CropSquareTransformation
import jp.wasabeef.glide.transformations.MaskTransformation import jp.wasabeef.glide.transformations.MaskTransformation
import kotlinx.android.synthetic.main.main_activity.* import kotlinx.android.synthetic.main.main_activity.*
@ -110,6 +109,10 @@ import kotlinx.android.synthetic.main.manga_header_item.*
import timber.log.Timber import timber.log.Timber
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
import java.io.File
import java.io.IOException
import kotlin.math.abs
import kotlin.math.max
class MangaDetailsController : BaseController, class MangaDetailsController : BaseController,
FlexibleAdapter.OnItemClickListener, FlexibleAdapter.OnItemClickListener,
@ -214,14 +217,16 @@ class MangaDetailsController : BaseController,
val appbarHeight = array.getDimensionPixelSize(0, 0) val appbarHeight = array.getDimensionPixelSize(0, 0)
array.recycle() array.recycle()
val offset = 10.dpToPx val offset = 10.dpToPx
var statusBarHeight = -1
recycler.doOnApplyWindowInsets { v, insets, _ -> recycler.doOnApplyWindowInsets { v, insets, _ ->
headerHeight = appbarHeight + insets.systemWindowInsetTop headerHeight = appbarHeight + insets.systemWindowInsetTop
statusBarHeight = insets.systemWindowInsetTop
swipe_refresh.setProgressViewOffset(false, (-40).dpToPx, headerHeight + offset) swipe_refresh.setProgressViewOffset(false, (-40).dpToPx, headerHeight + offset)
(recycler.findViewHolderForAdapterPosition(0) as? MangaHeaderHolder) (recycler.findViewHolderForAdapterPosition(0) as? MangaHeaderHolder)
?.setTopHeight(headerHeight) ?.setTopHeight(headerHeight)
fast_scroller?.updateLayoutParams<ViewGroup.MarginLayoutParams> { fast_scroller?.updateLayoutParams<ViewGroup.MarginLayoutParams> {
topMargin = headerHeight topMargin = statusBarHeight
bottomMargin = insets.systemWindowInsetBottom bottomMargin = insets.systemWindowInsetBottom
} }
v.updatePaddingRelative(bottom = insets.systemWindowInsetBottom) v.updatePaddingRelative(bottom = insets.systemWindowInsetBottom)
@ -233,36 +238,42 @@ class MangaDetailsController : BaseController,
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy) super.onScrolled(recyclerView, dx, dy)
val atTop = !recycler.canScrollVertically(-1) val atTop = !recycler.canScrollVertically(-1)
if ((!atTop && !toolbarIsColored) || (atTop && toolbarIsColored)) { val tY = getHeader()?.backdrop?.translationY ?: 0f
toolbarIsColored = !atTop getHeader()?.backdrop?.translationY = max(0f, tY + dy * 0.25f)
val isCurrentController = if (router?.backstack?.lastOrNull()
router?.backstack?.lastOrNull()?.controller() == this@MangaDetailsController ?.controller() == this@MangaDetailsController && statusBarHeight > -1 && activity != null && activity!!.appbar.height > 0
if (isCurrentController) setTitle() ) {
if (actionMode != null) { activity!!.appbar.y -= dy
(activity as MainActivity).toolbar.setBackgroundColor(Color.TRANSPARENT) activity!!.appbar.y = MathUtils.clamp(
return activity!!.appbar.y, -activity!!.appbar.height.toFloat(), 0f
}
val color =
coverColor ?: activity!!.getResourceColor(R.attr.colorPrimaryVariant)
val colorFrom =
if (colorAnimator?.isRunning == true) activity?.window?.statusBarColor
?: color
else ColorUtils.setAlphaComponent(
color, if (toolbarIsColored) 0 else 175
)
val colorTo = ColorUtils.setAlphaComponent(
color, if (toolbarIsColored) 175 else 0
) )
colorAnimator?.cancel() }
colorAnimator = ValueAnimator.ofObject( val appBarY = activity?.appbar?.y ?: 0f
android.animation.ArgbEvaluator(), colorFrom, colorTo if ((!atTop && !toolbarIsColored && (appBarY < (-headerHeight + 1) || (dy < 0 && appBarY == 0f))) || (atTop && toolbarIsColored)) {
) colorToolbar(!atTop)
colorAnimator?.duration = 250 // milliseconds }
colorAnimator?.addUpdateListener { animator -> }
(activity as MainActivity).toolbar.setBackgroundColor(animator.animatedValue as Int)
activity?.window?.statusBarColor = (animator.animatedValue as Int) override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
super.onScrollStateChanged(recyclerView, newState)
if (newState == RecyclerView.SCROLL_STATE_IDLE) {
if (router?.backstack?.lastOrNull()
?.controller() == this@MangaDetailsController && statusBarHeight > -1 && activity != null &&
activity!!.appbar.height > 0
) {
val halfWay = abs((-activity!!.appbar.height.toFloat()) / 2)
val shortAnimationDuration = resources?.getInteger(
android.R.integer.config_shortAnimTime
) ?: 0
val closerToTop = abs(activity!!.appbar.y) - halfWay > 0
val atTop = !recycler.canScrollVertically(-1)
activity!!.appbar.animate().y(
if (closerToTop && !atTop) (-activity!!.appbar.height.toFloat())
else 0f
).setDuration(shortAnimationDuration.toLong()).start()
if (!closerToTop && !atTop && !toolbarIsColored)
colorToolbar(true)
} }
colorAnimator?.start()
} }
} }
}) })
@ -275,6 +286,38 @@ class MangaDetailsController : BaseController,
swipe_refresh.setOnRefreshListener { presenter.refreshAll() } swipe_refresh.setOnRefreshListener { presenter.refreshAll() }
} }
fun colorToolbar(isColor: Boolean) {
toolbarIsColored = isColor
val isCurrentController =
router?.backstack?.lastOrNull()?.controller() == this@MangaDetailsController
if (isCurrentController) setTitle()
if (actionMode != null) {
(activity as MainActivity).toolbar.setBackgroundColor(Color.TRANSPARENT)
return
}
val color =
coverColor ?: activity!!.getResourceColor(R.attr.colorPrimaryVariant)
val colorFrom =
if (colorAnimator?.isRunning == true) activity?.window?.statusBarColor
?: color
else ColorUtils.setAlphaComponent(
color, if (toolbarIsColored) 0 else 175
)
val colorTo = ColorUtils.setAlphaComponent(
color, if (toolbarIsColored) 175 else 0
)
colorAnimator?.cancel()
colorAnimator = ValueAnimator.ofObject(
android.animation.ArgbEvaluator(), colorFrom, colorTo
)
colorAnimator?.duration = 250 // milliseconds
colorAnimator?.addUpdateListener { animator ->
(activity as MainActivity).toolbar.setBackgroundColor(animator.animatedValue as Int)
activity?.window?.statusBarColor = (animator.animatedValue as Int)
}
colorAnimator?.start()
}
fun setPaletteColor() { fun setPaletteColor() {
val view = view ?: return val view = view ?: return
GlideApp.with(view.context).load(manga) GlideApp.with(view.context).load(manga)
@ -345,6 +388,8 @@ class MangaDetailsController : BaseController,
return recycler?.findViewHolderForItemId(chapter.id!!) as? ChapterHolder return recycler?.findViewHolderForItemId(chapter.id!!) as? ChapterHolder
} }
private fun getHeader(): MangaHeaderHolder? = recycler.findViewHolderForAdapterPosition(0) as? MangaHeaderHolder
override fun onChangeStarted(handler: ControllerChangeHandler, type: ControllerChangeType) { override fun onChangeStarted(handler: ControllerChangeHandler, type: ControllerChangeType) {
super.onChangeStarted(handler, type) super.onChangeStarted(handler, type)
if (type == ControllerChangeType.PUSH_ENTER || type == ControllerChangeType.POP_ENTER) { if (type == ControllerChangeType.PUSH_ENTER || type == ControllerChangeType.POP_ENTER) {

View File

@ -38,6 +38,13 @@
android:backgroundTint="?android:attr/colorBackground" android:backgroundTint="?android:attr/colorBackground"
app:layout_constraintBottom_toBottomOf="@+id/true_backdrop" /> app:layout_constraintBottom_toBottomOf="@+id/true_backdrop" />
<View
android:layout_width="match_parent"
android:layout_height="0dp"
android:background="?android:attr/colorBackground"
app:layout_constraintTop_toBottomOf="@id/backdrop_gradient"
app:layout_constraintBottom_toBottomOf="parent"/>
<View <View
android:id="@+id/top_view" android:id="@+id/top_view"
android:layout_width="match_parent" android:layout_width="match_parent"