Reusing scroll extension method in manga details
This commit is contained in:
parent
3fc510fe4b
commit
024b075330
8 changed files with 58 additions and 88 deletions
|
@ -291,11 +291,11 @@ class LibraryController(
|
||||||
val tv = TypedValue()
|
val tv = TypedValue()
|
||||||
activity!!.theme.resolveAttribute(R.attr.actionBarTintColor, tv, true)
|
activity!!.theme.resolveAttribute(R.attr.actionBarTintColor, tv, true)
|
||||||
swipe_refresh.setStyle()
|
swipe_refresh.setStyle()
|
||||||
scrollViewWith(recycler, swipeRefreshLayout = swipe_refresh) { insets ->
|
scrollViewWith(recycler, swipeRefreshLayout = swipe_refresh, afterInsets = { insets ->
|
||||||
fast_scroller.updateLayoutParams<ViewGroup.MarginLayoutParams> {
|
fast_scroller.updateLayoutParams<ViewGroup.MarginLayoutParams> {
|
||||||
topMargin = insets.systemWindowInsetTop
|
topMargin = insets.systemWindowInsetTop
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
|
|
||||||
swipe_refresh.setOnRefreshListener {
|
swipe_refresh.setOnRefreshListener {
|
||||||
swipe_refresh.isRefreshing = false
|
swipe_refresh.isRefreshing = false
|
||||||
|
|
|
@ -134,5 +134,6 @@ class MangaDetailsAdapter(
|
||||||
fun copyToClipboard(content: String, label: Int)
|
fun copyToClipboard(content: String, label: Int)
|
||||||
fun zoomImageFromThumb(thumbView: View)
|
fun zoomImageFromThumb(thumbView: View)
|
||||||
fun showTrackingSheet()
|
fun showTrackingSheet()
|
||||||
|
fun updateScroll()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,7 +35,6 @@ import androidx.appcompat.widget.PopupMenu
|
||||||
import androidx.appcompat.widget.SearchView
|
import androidx.appcompat.widget.SearchView
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.core.graphics.ColorUtils
|
import androidx.core.graphics.ColorUtils
|
||||||
import androidx.core.math.MathUtils
|
|
||||||
import androidx.palette.graphics.Palette
|
import androidx.palette.graphics.Palette
|
||||||
import androidx.recyclerview.widget.ItemTouchHelper
|
import androidx.recyclerview.widget.ItemTouchHelper
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
|
@ -98,8 +97,8 @@ import eu.kanade.tachiyomi.util.system.getResourceColor
|
||||||
import eu.kanade.tachiyomi.util.system.launchUI
|
import eu.kanade.tachiyomi.util.system.launchUI
|
||||||
import eu.kanade.tachiyomi.util.system.pxToDp
|
import eu.kanade.tachiyomi.util.system.pxToDp
|
||||||
import eu.kanade.tachiyomi.util.system.toast
|
import eu.kanade.tachiyomi.util.system.toast
|
||||||
import eu.kanade.tachiyomi.util.view.doOnApplyWindowInsets
|
|
||||||
import eu.kanade.tachiyomi.util.view.getText
|
import eu.kanade.tachiyomi.util.view.getText
|
||||||
|
import eu.kanade.tachiyomi.util.view.scrollViewWith
|
||||||
import eu.kanade.tachiyomi.util.view.setOnQueryTextChangeListener
|
import eu.kanade.tachiyomi.util.view.setOnQueryTextChangeListener
|
||||||
import eu.kanade.tachiyomi.util.view.setStyle
|
import eu.kanade.tachiyomi.util.view.setStyle
|
||||||
import eu.kanade.tachiyomi.util.view.snack
|
import eu.kanade.tachiyomi.util.view.snack
|
||||||
|
@ -114,7 +113,6 @@ import uy.kohesive.injekt.api.get
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
import kotlin.math.abs
|
|
||||||
import kotlin.math.max
|
import kotlin.math.max
|
||||||
|
|
||||||
class MangaDetailsController : BaseController,
|
class MangaDetailsController : BaseController,
|
||||||
|
@ -261,98 +259,54 @@ 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
|
|
||||||
swipe_refresh.setStyle()
|
swipe_refresh.setStyle()
|
||||||
swipe_refresh.setDistanceToTriggerSync(70.dpToPx)
|
swipe_refresh.setDistanceToTriggerSync(70.dpToPx)
|
||||||
activity!!.appbar.elevation = 0f
|
activity!!.appbar.elevation = 0f
|
||||||
|
|
||||||
recycler.doOnApplyWindowInsets { v, insets, _ ->
|
scrollViewWith(recycler, padBottom = true, customPadding = true, afterInsets = { insets ->
|
||||||
v.updatePaddingRelative(bottom = insets.systemWindowInsetBottom)
|
recycler.updatePaddingRelative(bottom = insets.systemWindowInsetBottom)
|
||||||
tabletRecycler?.updatePaddingRelative(bottom = insets.systemWindowInsetBottom)
|
tabletRecycler?.updatePaddingRelative(bottom = insets.systemWindowInsetBottom)
|
||||||
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)
|
||||||
if (isTablet) v.updatePaddingRelative(top = headerHeight + 1.dpToPx)
|
// 1dp extra to line up chapter header and manga header
|
||||||
|
if (isTablet) recycler.updatePaddingRelative(top = headerHeight + 1.dpToPx)
|
||||||
getHeader()?.setTopHeight(headerHeight)
|
getHeader()?.setTopHeight(headerHeight)
|
||||||
fast_scroll_layout.updateLayoutParams<ViewGroup.MarginLayoutParams> {
|
fast_scroll_layout.updateLayoutParams<ViewGroup.MarginLayoutParams> {
|
||||||
topMargin = headerHeight
|
topMargin = headerHeight
|
||||||
bottomMargin = insets.systemWindowInsetBottom
|
bottomMargin = insets.systemWindowInsetBottom
|
||||||
}
|
}
|
||||||
v.updatePaddingRelative(bottom = insets.systemWindowInsetBottom)
|
}, liftOnScroll = {
|
||||||
}
|
colorToolbar(it)
|
||||||
|
})
|
||||||
|
|
||||||
recycler.addOnScrollListener(object : RecyclerView.OnScrollListener() {
|
recycler.addOnScrollListener(object : RecyclerView.OnScrollListener() {
|
||||||
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)
|
|
||||||
if (!isTablet) {
|
if (!isTablet) {
|
||||||
|
val atTop = !recycler.canScrollVertically(-1)
|
||||||
val tY = getHeader()?.backdrop?.translationY ?: 0f
|
val tY = getHeader()?.backdrop?.translationY ?: 0f
|
||||||
getHeader()?.backdrop?.translationY = max(0f, tY + dy * 0.25f)
|
getHeader()?.backdrop?.translationY = max(0f, tY + dy * 0.25f)
|
||||||
if (router?.backstack?.lastOrNull()
|
if (atTop) getHeader()?.backdrop?.translationY = 0f
|
||||||
?.controller() == this@MangaDetailsController && statusBarHeight > -1 && activity != null && activity!!.appbar.height > 0
|
|
||||||
) {
|
|
||||||
activity!!.appbar.y -= dy
|
|
||||||
activity!!.appbar.y = MathUtils.clamp(
|
|
||||||
activity!!.appbar.y, -activity!!.appbar.height.toFloat(), 0f
|
|
||||||
)
|
|
||||||
}
|
|
||||||
val appBarY = activity?.appbar?.y ?: 0f
|
|
||||||
if ((!atTop && !toolbarIsColored && (appBarY < (-headerHeight + 1) || (dy < 0 && appBarY == 0f))) || (atTop && toolbarIsColored)) {
|
|
||||||
colorToolbar(!atTop)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if ((!atTop && !toolbarIsColored) || (atTop && toolbarIsColored)) {
|
|
||||||
colorToolbar(!atTop)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (atTop) {
|
|
||||||
getHeader()?.backdrop?.translationY = 0f
|
|
||||||
activity!!.appbar.y = 0f
|
|
||||||
}
|
|
||||||
if (!isTablet) {
|
|
||||||
val fPosition =
|
val fPosition =
|
||||||
(recycler.layoutManager as LinearLayoutManager).findFirstCompletelyVisibleItemPosition()
|
(recycler.layoutManager as LinearLayoutManager).findFirstCompletelyVisibleItemPosition()
|
||||||
|
val scrollFunc: (Boolean) -> Unit = { show ->
|
||||||
|
showScroll = show
|
||||||
|
scrollAnim?.cancel()
|
||||||
|
scrollAnim = fast_scroller.animate().setDuration(100).translationX(
|
||||||
|
if (show) 0f else 25f.dpToPx)
|
||||||
|
scrollAnim?.start()
|
||||||
|
}
|
||||||
if (fPosition > 0 && !showScroll) {
|
if (fPosition > 0 && !showScroll) {
|
||||||
showScroll = true
|
scrollFunc(true)
|
||||||
scrollAnim?.cancel()
|
|
||||||
scrollAnim = fast_scroller.animate().setDuration(100).translationX(0f)
|
|
||||||
scrollAnim?.start()
|
|
||||||
} else if (fPosition <= 0 && showScroll) {
|
} else if (fPosition <= 0 && showScroll) {
|
||||||
showScroll = false
|
scrollFunc(false)
|
||||||
scrollAnim?.cancel()
|
|
||||||
scrollAnim =
|
|
||||||
fast_scroller.animate().setDuration(100).translationX(25f.dpToPx)
|
|
||||||
scrollAnim?.start()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
|
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
|
||||||
super.onScrollStateChanged(recyclerView, newState)
|
|
||||||
val atTop = !recycler.canScrollVertically(-1)
|
val atTop = !recycler.canScrollVertically(-1)
|
||||||
if (newState == RecyclerView.SCROLL_STATE_IDLE && !isTablet) {
|
if (atTop) getHeader()?.backdrop?.translationY = 0f
|
||||||
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
|
|
||||||
activity!!.appbar.animate().y(
|
|
||||||
if (closerToTop && !atTop) (-activity!!.appbar.height.toFloat())
|
|
||||||
else 0f
|
|
||||||
).setDuration(shortAnimationDuration.toLong()).start()
|
|
||||||
if (!closerToTop && !atTop && !toolbarIsColored)
|
|
||||||
colorToolbar(true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (atTop && toolbarIsColored) colorToolbar(false)
|
|
||||||
if (atTop) {
|
|
||||||
getHeader()?.backdrop?.translationY = 0f
|
|
||||||
activity!!.appbar.y = 0f
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -1004,6 +958,15 @@ class MangaDetailsController : BaseController,
|
||||||
presenter.startDownloadingNow(chapter)
|
presenter.startDownloadingNow(chapter)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// In case the recycler is at the bottom and collapsing the header makes it unscrollable
|
||||||
|
override fun updateScroll() {
|
||||||
|
if (!recycler.canScrollVertically(-1)) {
|
||||||
|
getHeader()?.backdrop?.translationY = 0f
|
||||||
|
activity?.appbar?.y = 0f
|
||||||
|
colorToolbar(isColor = false, animate = false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun downloadChapters(chapters: List<ChapterItem>) {
|
private fun downloadChapters(chapters: List<ChapterItem>) {
|
||||||
val view = view ?: return
|
val view = view ?: return
|
||||||
presenter.downloadChapters(chapters)
|
presenter.downloadChapters(chapters)
|
||||||
|
|
|
@ -69,6 +69,9 @@ class MangaHeaderHolder(
|
||||||
adapter.delegate.favoriteManga(true)
|
adapter.delegate.favoriteManga(true)
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
title.setOnClickListener {
|
||||||
|
title.maxLines = Integer.MAX_VALUE
|
||||||
|
}
|
||||||
title.setOnLongClickListener {
|
title.setOnLongClickListener {
|
||||||
adapter.delegate.copyToClipboard(title.text.toString(), R.string.title)
|
adapter.delegate.copyToClipboard(title.text.toString(), R.string.title)
|
||||||
true
|
true
|
||||||
|
@ -102,6 +105,9 @@ class MangaHeaderHolder(
|
||||||
manga_genres_tags.gone()
|
manga_genres_tags.gone()
|
||||||
less_button.gone()
|
less_button.gone()
|
||||||
more_button_group.visible()
|
more_button_group.visible()
|
||||||
|
adapter.recyclerView.post {
|
||||||
|
adapter.delegate.updateScroll()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun bindChapters() {
|
fun bindChapters() {
|
||||||
|
|
|
@ -117,9 +117,9 @@ class RecentsController(bundle: Bundle? = null) : BaseController(bundle),
|
||||||
val appBarHeight = array.getDimensionPixelSize(0, 0)
|
val appBarHeight = array.getDimensionPixelSize(0, 0)
|
||||||
array.recycle()
|
array.recycle()
|
||||||
swipe_refresh.setStyle()
|
swipe_refresh.setStyle()
|
||||||
scrollViewWith(recycler, skipFirstSnap = true, swipeRefreshLayout = swipe_refresh) {
|
scrollViewWith(recycler, swipeRefreshLayout = swipe_refresh, afterInsets = {
|
||||||
headerHeight = it.systemWindowInsetTop + appBarHeight
|
headerHeight = it.systemWindowInsetTop + appBarHeight
|
||||||
}
|
})
|
||||||
|
|
||||||
presenter.onCreate()
|
presenter.onCreate()
|
||||||
if (presenter.recentItems.isNotEmpty()) {
|
if (presenter.recentItems.isNotEmpty()) {
|
||||||
|
|
|
@ -121,9 +121,9 @@ class SourceController : NucleusController<SourcePresenter>(),
|
||||||
val array = view.context.obtainStyledAttributes(attrsArray)
|
val array = view.context.obtainStyledAttributes(attrsArray)
|
||||||
val appBarHeight = array.getDimensionPixelSize(0, 0)
|
val appBarHeight = array.getDimensionPixelSize(0, 0)
|
||||||
array.recycle()
|
array.recycle()
|
||||||
scrollViewWith(recycler) {
|
scrollViewWith(recycler, afterInsets = {
|
||||||
headerHeight = it.systemWindowInsetTop + appBarHeight
|
headerHeight = it.systemWindowInsetTop + appBarHeight
|
||||||
}
|
})
|
||||||
|
|
||||||
requestPermissionsSafe(arrayOf(WRITE_EXTERNAL_STORAGE), 301)
|
requestPermissionsSafe(arrayOf(WRITE_EXTERNAL_STORAGE), 301)
|
||||||
ext_bottom_sheet.onCreate(this)
|
ext_bottom_sheet.onCreate(this)
|
||||||
|
|
|
@ -184,11 +184,11 @@ open class BrowseSourceController(bundle: Bundle) :
|
||||||
recycler.setHasFixedSize(true)
|
recycler.setHasFixedSize(true)
|
||||||
recycler.adapter = adapter
|
recycler.adapter = adapter
|
||||||
|
|
||||||
scrollViewWith(recycler, true) { insets ->
|
scrollViewWith(recycler, true, afterInsets = { insets ->
|
||||||
fab.updateLayoutParams<ViewGroup.MarginLayoutParams> {
|
fab.updateLayoutParams<ViewGroup.MarginLayoutParams> {
|
||||||
bottomMargin = insets.systemWindowInsetBottom + 16.dpToPx
|
bottomMargin = insets.systemWindowInsetBottom + 16.dpToPx
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
|
|
||||||
recycler.addOnScrollListener(object : RecyclerView.OnScrollListener() {
|
recycler.addOnScrollListener(object : RecyclerView.OnScrollListener() {
|
||||||
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
|
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
|
||||||
|
|
|
@ -9,7 +9,6 @@ import android.view.inputmethod.InputMethodManager
|
||||||
import androidx.appcompat.widget.SearchView
|
import androidx.appcompat.widget.SearchView
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.core.math.MathUtils
|
import androidx.core.math.MathUtils
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||||
import com.bluelinelabs.conductor.Controller
|
import com.bluelinelabs.conductor.Controller
|
||||||
|
@ -53,9 +52,9 @@ fun Controller.scrollViewWith(
|
||||||
recycler: RecyclerView,
|
recycler: RecyclerView,
|
||||||
padBottom: Boolean = false,
|
padBottom: Boolean = false,
|
||||||
customPadding: Boolean = false,
|
customPadding: Boolean = false,
|
||||||
skipFirstSnap: Boolean = false,
|
|
||||||
swipeRefreshLayout: SwipeRefreshLayout? = null,
|
swipeRefreshLayout: SwipeRefreshLayout? = null,
|
||||||
afterInsets: ((WindowInsets) -> Unit)? = null
|
afterInsets: ((WindowInsets) -> Unit)? = null,
|
||||||
|
liftOnScroll: ((Boolean) -> Unit)? = null
|
||||||
) {
|
) {
|
||||||
var statusBarHeight = -1
|
var statusBarHeight = -1
|
||||||
activity?.appbar?.y = 0f
|
activity?.appbar?.y = 0f
|
||||||
|
@ -87,14 +86,18 @@ fun Controller.scrollViewWith(
|
||||||
var elevate = false
|
var elevate = false
|
||||||
val elevateFunc: (Boolean) -> Unit = { el ->
|
val elevateFunc: (Boolean) -> Unit = { el ->
|
||||||
elevate = el
|
elevate = el
|
||||||
elevationAnim?.cancel()
|
if (liftOnScroll != null) {
|
||||||
elevationAnim = ValueAnimator.ofFloat(
|
liftOnScroll.invoke(el)
|
||||||
activity!!.appbar.elevation, if (el) 15f else 0f
|
} else {
|
||||||
)
|
elevationAnim?.cancel()
|
||||||
elevationAnim?.addUpdateListener { valueAnimator ->
|
elevationAnim = ValueAnimator.ofFloat(
|
||||||
activity!!.appbar.elevation = valueAnimator.animatedValue as Float
|
activity!!.appbar.elevation, if (el) 15f else 0f
|
||||||
|
)
|
||||||
|
elevationAnim?.addUpdateListener { valueAnimator ->
|
||||||
|
activity!!.appbar.elevation = valueAnimator.animatedValue as Float
|
||||||
|
}
|
||||||
|
elevationAnim?.start()
|
||||||
}
|
}
|
||||||
elevationAnim?.start()
|
|
||||||
}
|
}
|
||||||
addLifecycleListener(object : Controller.LifecycleListener() {
|
addLifecycleListener(object : Controller.LifecycleListener() {
|
||||||
override fun onChangeStart(
|
override fun onChangeStart(
|
||||||
|
@ -144,10 +147,7 @@ fun Controller.scrollViewWith(
|
||||||
R.integer.config_shortAnimTime
|
R.integer.config_shortAnimTime
|
||||||
) ?: 0
|
) ?: 0
|
||||||
val closerToTop = abs(activity!!.appbar.y) - halfWay > 0
|
val closerToTop = abs(activity!!.appbar.y) - halfWay > 0
|
||||||
val atTop = (!customPadding &&
|
val atTop = !recycler.canScrollVertically(-1)
|
||||||
(recycler.layoutManager as LinearLayoutManager)
|
|
||||||
.findFirstVisibleItemPosition() < 2 && !skipFirstSnap) ||
|
|
||||||
!recycler.canScrollVertically(-1)
|
|
||||||
activity!!.appbar.animate().y(
|
activity!!.appbar.animate().y(
|
||||||
if (closerToTop && !atTop) (-activity!!.appbar.height.toFloat())
|
if (closerToTop && !atTop) (-activity!!.appbar.height.toFloat())
|
||||||
else 0f
|
else 0f
|
||||||
|
|
Reference in a new issue