diff --git a/app/src/main/kotlin/com/hippo/ehviewer/ui/legacy/FabLayout.kt b/app/src/main/kotlin/com/hippo/ehviewer/ui/legacy/FabLayout.kt index dbc744a77..37a084a0b 100644 --- a/app/src/main/kotlin/com/hippo/ehviewer/ui/legacy/FabLayout.kt +++ b/app/src/main/kotlin/com/hippo/ehviewer/ui/legacy/FabLayout.kt @@ -22,9 +22,11 @@ import android.os.Parcelable import android.util.AttributeSet import android.view.View import android.view.ViewGroup +import android.view.ViewPropertyAnimator import android.view.animation.Interpolator import androidx.activity.ComponentActivity import androidx.activity.OnBackPressedCallback +import androidx.core.view.isVisible import com.google.android.material.floatingactionbutton.FloatingActionButton import com.hippo.ehviewer.R import com.hippo.ehviewer.util.AnimationUtils @@ -46,6 +48,7 @@ class FabLayout @JvmOverloads constructor( private var mMainFabCenterY = -1f private var mOnExpandListeners = arrayListOf() private var mOnClickFabListener: OnClickFabListener? = null + private var fabAnimator: ViewPropertyAnimator? = null private val mOnBackPressedCallback = object : OnBackPressedCallback(false), OnExpandListener { override fun handleOnBackPressed() { @@ -56,6 +59,11 @@ class FabLayout @JvmOverloads constructor( isEnabled = expanded } } + private val mFabAnimatorListener = object : SimpleAnimatorListener() { + override fun onAnimationEnd(animation: Animator) { + primaryFab.isVisible = false + } + } override fun onAttachedToWindow() { super.onAttachedToWindow() @@ -85,8 +93,8 @@ class FabLayout @JvmOverloads constructor( super.addView(child, index, params) } - val primaryFab: FloatingActionButton? - get() = getChildAt(childCount - 1) as? FloatingActionButton + val primaryFab: FloatingActionButton + get() = getChildAt(childCount - 1) as FloatingActionButton private val secondaryFabCount: Int get() = 0.coerceAtLeast(childCount - 1) @@ -414,6 +422,43 @@ class FabLayout @JvmOverloads constructor( } } + fun show(animation: Boolean = true, delay: Long = 0L) { + val fab = primaryFab + if (!fab.isVisible) { + fab.isVisible = true + if (animation) { + fabAnimator?.cancel() + fabAnimator = fab.animate().scaleX(1.0f).scaleY(1.0f).setListener(null) + .setDuration(ANIMATE_TIME).setStartDelay(delay) + .setInterpolator(AnimationUtils.FAST_SLOW_INTERPOLATOR).apply { start() } + } else { + fab.scaleX = 1.0f + fab.scaleY = 1.0f + } + } + } + + fun hide(animation: Boolean = true): Long { + val fab = primaryFab + setExpanded(expanded = false, animation = true) + return if (!fab.isVisible) { + 0L + } else { + if (animation) { + fabAnimator?.cancel() + fabAnimator = fab.animate().scaleX(0.0f).scaleY(0.0f).setListener(mFabAnimatorListener) + .setDuration(ANIMATE_TIME).setStartDelay(0L) + .setInterpolator(AnimationUtils.SLOW_FAST_INTERPOLATOR).apply { start() } + ANIMATE_TIME + } else { + fab.isVisible = false + fab.scaleX = 0.0f + fab.scaleY = 0.0f + 0L + } + } + } + fun interface OnExpandListener { fun onExpand(expanded: Boolean) } @@ -424,7 +469,7 @@ class FabLayout @JvmOverloads constructor( } companion object { - private const val ANIMATE_TIME = 300L + const val ANIMATE_TIME = 300L private const val STATE_KEY_SUPER = "super" private const val STATE_KEY_AUTO_CANCEL = "auto_cancel" private const val STATE_KEY_EXPANDED = "expanded" diff --git a/app/src/main/kotlin/com/hippo/ehviewer/ui/scene/DownloadsScene.kt b/app/src/main/kotlin/com/hippo/ehviewer/ui/scene/DownloadsScene.kt index b7366c69f..be8c7736f 100644 --- a/app/src/main/kotlin/com/hippo/ehviewer/ui/scene/DownloadsScene.kt +++ b/app/src/main/kotlin/com/hippo/ehviewer/ui/scene/DownloadsScene.kt @@ -362,7 +362,7 @@ class DownloadsScene : } override fun onSearchViewExpanded() { - hideActionFab(true) + binding.fabLayout.hide() super.onSearchViewExpanded() } diff --git a/app/src/main/kotlin/com/hippo/ehviewer/ui/scene/FavoritesScene.kt b/app/src/main/kotlin/com/hippo/ehviewer/ui/scene/FavoritesScene.kt index b9eeb818e..25b5f8008 100644 --- a/app/src/main/kotlin/com/hippo/ehviewer/ui/scene/FavoritesScene.kt +++ b/app/src/main/kotlin/com/hippo/ehviewer/ui/scene/FavoritesScene.kt @@ -239,7 +239,7 @@ class FavoritesScene : SearchBarScene() { updateJumpFab() val colorID = theme.resolveColor(com.google.android.material.R.attr.colorOnSurface) val addDelete = AddDeleteDrawable(context, colorID) - primaryFab!!.setImageDrawable(addDelete) + primaryFab.setImageDrawable(addDelete) setExpanded(expanded = false, animation = false) setAutoCancel(true) setHidePrimaryFab(false) @@ -296,9 +296,9 @@ class FavoritesScene : SearchBarScene() { }) addOnExpandListener { if (it) { - addDelete.setDelete(ANIMATE_TIME) + addDelete.setDelete(FabLayout.ANIMATE_TIME) } else { - addDelete.setAdd(ANIMATE_TIME) + addDelete.setAdd(FabLayout.ANIMATE_TIME) if (tracker.isInCustomChoice) tracker.clearSelection() } } @@ -475,13 +475,13 @@ class FavoritesScene : SearchBarScene() { } override fun onSearchViewExpanded() { - hideActionFab(true) + binding.fabLayout.hide() super.onSearchViewExpanded() } override fun onSearchViewHidden() { super.onSearchViewHidden() - showActionFab(true) + binding.fabLayout.show(delay = SEARCH_VIEW_ANIMATE_TIME) } private fun showGoToDialog() { diff --git a/app/src/main/kotlin/com/hippo/ehviewer/ui/scene/GalleryListScene.kt b/app/src/main/kotlin/com/hippo/ehviewer/ui/scene/GalleryListScene.kt index cdd79d717..538e06316 100644 --- a/app/src/main/kotlin/com/hippo/ehviewer/ui/scene/GalleryListScene.kt +++ b/app/src/main/kotlin/com/hippo/ehviewer/ui/scene/GalleryListScene.kt @@ -15,7 +15,6 @@ */ package com.hippo.ehviewer.ui.scene -import android.animation.Animator import android.content.Context import android.content.DialogInterface import android.os.Bundle @@ -25,7 +24,6 @@ import android.view.MenuItem import android.view.View import android.view.ViewConfiguration import android.view.ViewGroup -import android.view.ViewPropertyAnimator import android.widget.TextView import androidx.activity.OnBackPressedCallback import androidx.compose.foundation.layout.consumeWindowInsets @@ -93,9 +91,7 @@ import com.hippo.ehviewer.ui.legacy.LayoutManagerUtils.firstVisibleItemPosition import com.hippo.ehviewer.ui.legacy.ViewTransition import com.hippo.ehviewer.ui.legacy.WindowInsetsAnimationHelper import com.hippo.ehviewer.ui.tools.DialogState -import com.hippo.ehviewer.util.AnimationUtils import com.hippo.ehviewer.util.ExceptionUtils -import com.hippo.ehviewer.util.SimpleAnimatorListener import com.hippo.ehviewer.util.applyNavigationBarsPadding import com.hippo.ehviewer.util.getParcelableCompat import com.hippo.ehviewer.util.getValue @@ -192,17 +188,11 @@ class GalleryListScene : SearchBarScene() { } private var _binding: SceneGalleryListBinding? = null private val binding get() = _binding!! - private var fabAnimator: ViewPropertyAnimator? = null private var mViewTransition: ViewTransition? = null private var mAdapter: GalleryAdapter? = null lateinit var mQuickSearchList: MutableList private var mHideActionFabSlop = 0 private var mState = State.NORMAL - private val mSearchFabAnimatorListener = object : SimpleAnimatorListener() { - override fun onAnimationEnd(animation: Animator) { - binding.searchFab.visibility = View.INVISIBLE - } - } override val fabLayout get() = binding.fabLayout override val fastScroller get() = binding.fastScroller @@ -310,13 +300,12 @@ class GalleryListScene : SearchBarScene() { container.addView(ComposeWithMD3 { dialogState.Intercept() }) requireActivity().onBackPressedDispatcher.addCallback(stateBackPressedCallback) mHideActionFabSlop = ViewConfiguration.get(requireContext()).scaledTouchSlop - mShowActionFab = true ViewCompat.setWindowInsetsAnimationCallback( binding.root, WindowInsetsAnimationHelper( WindowInsetsAnimationCompat.Callback.DISPATCH_MODE_STOP, binding.fabLayout, - binding.searchFab.parent as View, + binding.searchFabLayout, ), ) mViewTransition = BringOutTransition(binding.contentLayout.contentView, binding.searchLayout) @@ -460,15 +449,15 @@ class GalleryListScene : SearchBarScene() { binding.fabLayout.addOnExpandListener { if (it) { lockDrawer() - actionFabDrawable.setDelete(ANIMATE_TIME) + actionFabDrawable.setDelete(FabLayout.ANIMATE_TIME) } else { unlockDrawer() - actionFabDrawable.setAdd(ANIMATE_TIME) + actionFabDrawable.setAdd(FabLayout.ANIMATE_TIME) } } - binding.fabLayout.primaryFab!!.setImageDrawable(actionFabDrawable) + binding.fabLayout.primaryFab.setImageDrawable(actionFabDrawable) binding.searchFab.setOnClickListener { onApplySearch() } - (binding.searchFab.parent as View).apply { + binding.searchFabLayout.apply { bringToFront() applyNavigationBarsPadding() } @@ -493,7 +482,6 @@ class GalleryListScene : SearchBarScene() { stateBackPressedCallback.remove() binding.recyclerView.stopScroll() _binding = null - fabAnimator = null mAdapter = null mViewTransition = null } @@ -692,52 +680,21 @@ class GalleryListScene : SearchBarScene() { } } - private fun showSearchFab(animation: Boolean, delay: Long = 0) { - if (animation) { - binding.searchFab.visibility = View.VISIBLE - binding.searchFab.animate().scaleX(1.0f).scaleY(1.0f).setListener(null) - .setDuration(ANIMATE_TIME).setStartDelay(delay) - .setInterpolator(AnimationUtils.FAST_SLOW_INTERPOLATOR).start() - } else { - binding.searchFab.visibility = View.VISIBLE - binding.searchFab.scaleX = 1.0f - binding.searchFab.scaleY = 1.0f - } - } - - private fun hideSearchFab(animation: Boolean): Long { - return if (View.INVISIBLE == binding.searchFab.visibility) { - 0L - } else { - if (animation) { - binding.searchFab.animate().scaleX(0.0f).scaleY(0.0f) - .setListener(mSearchFabAnimatorListener) - .setDuration(ANIMATE_TIME).setStartDelay(0L) - .setInterpolator(AnimationUtils.SLOW_FAST_INTERPOLATOR).start() - } else { - binding.searchFab.visibility = View.INVISIBLE - binding.searchFab.scaleX = 0.0f - binding.searchFab.scaleY = 0.0f - } - ANIMATE_TIME - } - } - private fun selectSearchFab(animation: Boolean) { - val delay = hideActionFab(animation) - showSearchFab(animation, delay) + val delay = binding.fabLayout.hide(animation) + binding.searchFabLayout.show(animation, delay) } private fun selectActionFab(animation: Boolean) { - val delay = hideSearchFab(animation) - showActionFab(animation, delay) + val delay = binding.searchFabLayout.hide(animation) + binding.fabLayout.show(animation, delay) } override fun onSearchViewExpanded() { if (mState == State.NORMAL) { - hideActionFab(true) + binding.fabLayout.hide() } else { - hideSearchFab(true) + binding.searchFabLayout.hide() } super.onSearchViewExpanded() } @@ -745,42 +702,23 @@ class GalleryListScene : SearchBarScene() { override fun onSearchViewHidden() { super.onSearchViewHidden() if (mState == State.NORMAL) { - showActionFab(true) + binding.fabLayout.show(delay = SEARCH_VIEW_ANIMATE_TIME) } else { - showSearchFab(true) + binding.searchFabLayout.show(delay = SEARCH_VIEW_ANIMATE_TIME) } } private fun showActionFab() { _binding ?: return - if (State.NORMAL == mState && !mShowActionFab) { - mShowActionFab = true - val fab: View? = binding.fabLayout.primaryFab - if (fabAnimator != null) { - fabAnimator!!.cancel() - } - fab!!.visibility = View.VISIBLE - fab.rotation = -45.0f - fabAnimator = fab.animate().scaleX(1.0f).scaleY(1.0f).rotation(0.0f).setListener(null) - .setDuration(ANIMATE_TIME).setStartDelay(0L) - .setInterpolator(AnimationUtils.FAST_SLOW_INTERPOLATOR) - fabAnimator!!.start() + if (State.NORMAL == mState) { + binding.fabLayout.show() } } private fun hideActionFab() { _binding ?: return - if (State.NORMAL == mState && mShowActionFab) { - mShowActionFab = false - val fab: View? = binding.fabLayout.primaryFab - if (fabAnimator != null) { - fabAnimator!!.cancel() - } - fabAnimator = - fab!!.animate().scaleX(0.0f).scaleY(0.0f).setListener(mActionFabAnimatorListener) - .setDuration(ANIMATE_TIME).setStartDelay(0L) - .setInterpolator(AnimationUtils.SLOW_FAST_INTERPOLATOR) - fabAnimator!!.start() + if (State.NORMAL == mState) { + binding.fabLayout.hide() } } diff --git a/app/src/main/kotlin/com/hippo/ehviewer/ui/scene/SearchBarScene.kt b/app/src/main/kotlin/com/hippo/ehviewer/ui/scene/SearchBarScene.kt index ef23f977b..2ddf163a4 100644 --- a/app/src/main/kotlin/com/hippo/ehviewer/ui/scene/SearchBarScene.kt +++ b/app/src/main/kotlin/com/hippo/ehviewer/ui/scene/SearchBarScene.kt @@ -1,6 +1,5 @@ package com.hippo.ehviewer.ui.scene -import android.animation.Animator import android.os.Bundle import android.view.LayoutInflater import android.view.MenuItem @@ -43,8 +42,6 @@ import com.hippo.ehviewer.databinding.SceneSearchbarBinding import com.hippo.ehviewer.ui.legacy.BaseDialogBuilder import com.hippo.ehviewer.ui.legacy.FabLayout import com.hippo.ehviewer.ui.setMD3Content -import com.hippo.ehviewer.util.AnimationUtils -import com.hippo.ehviewer.util.SimpleAnimatorListener import com.hippo.ehviewer.util.applyNavigationBarsPadding import com.hippo.ehviewer.util.isAtLeastT import com.jamal.composeprefs3.ui.ifNotNullThen @@ -65,12 +62,6 @@ abstract class SearchBarScene : BaseScene(), ToolBarScene { private var allowEmptySearch = true private val mSearchDatabase = searchDatabase.searchDao() private var onApplySearch: (String) -> Unit = {} - protected var mShowActionFab = true - protected val mActionFabAnimatorListener = object : SimpleAnimatorListener() { - override fun onAnimationEnd(animation: Animator) { - fabLayout.primaryFab?.visibility = View.INVISIBLE - } - } protected abstract val fabLayout: FabLayout protected abstract val fastScroller: View @@ -180,46 +171,6 @@ abstract class SearchBarScene : BaseScene(), ToolBarScene { } } - fun showActionFab(animation: Boolean, delay: Long = ANIMATE_TIME) { - mShowActionFab = true - if (animation) { - val fab = fabLayout.primaryFab!! - fab.visibility = View.VISIBLE - fab.animate().scaleX(1.0f).scaleY(1.0f).setListener(null) - .setDuration(ANIMATE_TIME).setStartDelay(delay) - .setInterpolator(AnimationUtils.FAST_SLOW_INTERPOLATOR).start() - } else { - fabLayout.setExpanded(expanded = false, animation = false) - val fab = fabLayout.primaryFab!! - fab.visibility = View.VISIBLE - fab.scaleX = 1.0f - fab.scaleY = 1.0f - } - } - - fun hideActionFab(animation: Boolean): Long { - mShowActionFab = false - return if (animation) { - val fab = fabLayout.primaryFab!! - if (View.INVISIBLE == fab.visibility) { - 0L - } else { - fabLayout.setExpanded(expanded = false, animation = true) - fab.animate().scaleX(0.0f).scaleY(0.0f).setListener(mActionFabAnimatorListener) - .setDuration(ANIMATE_TIME).setStartDelay(0L) - .setInterpolator(AnimationUtils.SLOW_FAST_INTERPOLATOR).start() - ANIMATE_TIME - } - } else { - fabLayout.setExpanded(expanded = false, animation = false) - val fab = fabLayout.primaryFab!! - fab.visibility = View.INVISIBLE - fab.scaleX = 0.0f - fab.scaleY = 0.0f - 0L - } - } - fun setSearchBarHint(hint: String?) { binding.toolbar.hint = hint } @@ -403,4 +354,4 @@ fun wrapTagKeyword(keyword: String, translate: Boolean = false): String { } } -const val ANIMATE_TIME = 300L +const val SEARCH_VIEW_ANIMATE_TIME = 300L diff --git a/app/src/main/res/layout/scene_gallery_list.xml b/app/src/main/res/layout/scene_gallery_list.xml index ca4066830..cdcd8b7af 100644 --- a/app/src/main/res/layout/scene_gallery_list.xml +++ b/app/src/main/res/layout/scene_gallery_list.xml @@ -61,6 +61,7 @@