Migrate to TransitionManager from custom animations
This commit is contained in:
@@ -18,6 +18,9 @@ import androidx.core.view.postDelayed
|
||||
import androidx.core.view.updatePadding
|
||||
import androidx.fragment.app.commit
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.transition.Slide
|
||||
import androidx.transition.TransitionManager
|
||||
import androidx.transition.TransitionSet
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
@@ -47,8 +50,9 @@ import org.koitharu.kotatsu.reader.ui.thumbnails.PagesThumbnailsSheet
|
||||
import org.koitharu.kotatsu.utils.GridTouchHelper
|
||||
import org.koitharu.kotatsu.utils.ScreenOrientationHelper
|
||||
import org.koitharu.kotatsu.utils.ShareHelper
|
||||
import org.koitharu.kotatsu.utils.anim.Motion
|
||||
import org.koitharu.kotatsu.utils.ext.*
|
||||
import org.koitharu.kotatsu.utils.ext.getDisplayMessage
|
||||
import org.koitharu.kotatsu.utils.ext.hasGlobalPoint
|
||||
import org.koitharu.kotatsu.utils.ext.observeWithPrevious
|
||||
|
||||
class ReaderActivity : BaseFullscreenActivity<ActivityReaderBinding>(),
|
||||
ChaptersBottomSheet.OnChapterChangeListener,
|
||||
@@ -310,13 +314,16 @@ class ReaderActivity : BaseFullscreenActivity<ActivityReaderBinding>(),
|
||||
|
||||
private fun setUiIsVisible(isUiVisible: Boolean) {
|
||||
if (binding.appbarTop.isVisible != isUiVisible) {
|
||||
val transition = TransitionSet()
|
||||
.setOrdering(TransitionSet.ORDERING_TOGETHER)
|
||||
.addTransition(Slide(Gravity.BOTTOM).addTarget(binding.appbarBottom))
|
||||
.addTransition(Slide(Gravity.TOP).addTarget(binding.appbarTop))
|
||||
TransitionManager.beginDelayedTransition(binding.root, transition)
|
||||
binding.appbarTop.isVisible = isUiVisible
|
||||
binding.appbarBottom.isVisible = isUiVisible
|
||||
if (isUiVisible) {
|
||||
binding.appbarTop.showAnimated(Motion.SlideTop)
|
||||
binding.appbarBottom.showAnimated(Motion.SlideBottom)
|
||||
showSystemUI()
|
||||
} else {
|
||||
binding.appbarTop.hideAnimated(Motion.SlideTop)
|
||||
binding.appbarBottom.hideAnimated(Motion.SlideBottom)
|
||||
hideSystemUI()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,13 +3,16 @@ package org.koitharu.kotatsu.reader.ui
|
||||
import android.content.Context
|
||||
import android.graphics.Color
|
||||
import android.util.AttributeSet
|
||||
import android.view.Gravity
|
||||
import android.view.ViewGroup
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.swiperefreshlayout.widget.CircularProgressDrawable
|
||||
import androidx.transition.Fade
|
||||
import androidx.transition.Slide
|
||||
import androidx.transition.TransitionManager
|
||||
import androidx.transition.TransitionSet
|
||||
import com.google.android.material.textview.MaterialTextView
|
||||
import org.koitharu.kotatsu.utils.anim.Duration
|
||||
import org.koitharu.kotatsu.utils.anim.Motion
|
||||
import org.koitharu.kotatsu.utils.ext.hideAnimated
|
||||
import org.koitharu.kotatsu.utils.ext.showAnimated
|
||||
|
||||
class ReaderToastView @JvmOverloads constructor(
|
||||
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
|
||||
@@ -22,7 +25,8 @@ class ReaderToastView @JvmOverloads constructor(
|
||||
fun show(message: CharSequence, isLoading: Boolean) {
|
||||
removeCallbacks(hideRunnable)
|
||||
text = message
|
||||
this.showAnimated(Motion.Toast, Duration.SHORT)
|
||||
setupTransition()
|
||||
isVisible = true
|
||||
}
|
||||
|
||||
fun show(@StringRes messageId: Int, isLoading: Boolean) {
|
||||
@@ -36,7 +40,8 @@ class ReaderToastView @JvmOverloads constructor(
|
||||
|
||||
fun hide() {
|
||||
removeCallbacks(hideRunnable)
|
||||
this.hideAnimated(Motion.Toast, Duration.SHORT)
|
||||
setupTransition()
|
||||
isVisible = false
|
||||
}
|
||||
|
||||
override fun onDetachedFromWindow() {
|
||||
@@ -44,6 +49,16 @@ class ReaderToastView @JvmOverloads constructor(
|
||||
super.onDetachedFromWindow()
|
||||
}
|
||||
|
||||
private fun setupTransition () {
|
||||
val parentView = parent as? ViewGroup ?: return
|
||||
val transition = TransitionSet()
|
||||
.setOrdering(TransitionSet.ORDERING_TOGETHER)
|
||||
.addTarget(this)
|
||||
.addTransition(Slide(Gravity.BOTTOM))
|
||||
.addTransition(Fade())
|
||||
TransitionManager.beginDelayedTransition(parentView, transition)
|
||||
}
|
||||
|
||||
// FIXME use it as compound drawable
|
||||
private fun createProgressDrawable(): CircularProgressDrawable {
|
||||
val drawable = CircularProgressDrawable(context)
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
package org.koitharu.kotatsu.utils.anim
|
||||
|
||||
import androidx.annotation.IntegerRes
|
||||
|
||||
enum class Duration(@IntegerRes val resId: Int) {
|
||||
SHORT(android.R.integer.config_shortAnimTime),
|
||||
MEDIUM(android.R.integer.config_mediumAnimTime),
|
||||
LONG(android.R.integer.config_longAnimTime)
|
||||
}
|
||||
@@ -1,94 +0,0 @@
|
||||
package org.koitharu.kotatsu.utils.anim
|
||||
|
||||
import android.view.View
|
||||
import android.view.ViewPropertyAnimator
|
||||
import android.view.animation.AccelerateInterpolator
|
||||
import android.view.animation.DecelerateInterpolator
|
||||
import org.koitharu.kotatsu.utils.ext.measureHeight
|
||||
|
||||
sealed class Motion {
|
||||
|
||||
abstract fun reset(v: View)
|
||||
|
||||
abstract fun hideView(v: View)
|
||||
|
||||
abstract fun hide(v: View, anim: ViewPropertyAnimator)
|
||||
|
||||
abstract fun show(v: View, anim: ViewPropertyAnimator)
|
||||
|
||||
object CrossFade : Motion() {
|
||||
override fun reset(v: View) {
|
||||
v.alpha = 1f
|
||||
}
|
||||
|
||||
override fun hideView(v: View) {
|
||||
v.alpha = 0f
|
||||
}
|
||||
|
||||
override fun hide(v: View, anim: ViewPropertyAnimator) {
|
||||
anim.alpha(0f)
|
||||
}
|
||||
|
||||
override fun show(v: View, anim: ViewPropertyAnimator) {
|
||||
anim.alpha(1f)
|
||||
}
|
||||
}
|
||||
|
||||
object SlideBottom : Motion() {
|
||||
override fun reset(v: View) {
|
||||
v.translationY = 0f
|
||||
}
|
||||
|
||||
override fun hideView(v: View) {
|
||||
v.translationY = v.measureHeight().toFloat()
|
||||
}
|
||||
|
||||
override fun hide(v: View, anim: ViewPropertyAnimator) {
|
||||
anim.translationY(v.measureHeight().toFloat())
|
||||
}
|
||||
|
||||
override fun show(v: View, anim: ViewPropertyAnimator) {
|
||||
anim.translationY(0f)
|
||||
}
|
||||
}
|
||||
|
||||
object SlideTop : Motion() {
|
||||
override fun reset(v: View) {
|
||||
v.translationY = 0f
|
||||
}
|
||||
|
||||
override fun hideView(v: View) {
|
||||
v.translationY = -v.measureHeight().toFloat()
|
||||
}
|
||||
|
||||
override fun hide(v: View, anim: ViewPropertyAnimator) {
|
||||
anim.translationY(-v.measureHeight().toFloat())
|
||||
}
|
||||
|
||||
override fun show(v: View, anim: ViewPropertyAnimator) {
|
||||
anim.translationY(0f)
|
||||
}
|
||||
}
|
||||
|
||||
object Toast : Motion() {
|
||||
override fun reset(v: View) {
|
||||
v.alpha = 1f
|
||||
}
|
||||
|
||||
override fun hideView(v: View) {
|
||||
v.alpha = 0f
|
||||
}
|
||||
|
||||
override fun hide(v: View, anim: ViewPropertyAnimator) {
|
||||
anim.alpha(0f)
|
||||
anim.translationY(v.measureHeight().toFloat())
|
||||
anim.interpolator = AccelerateInterpolator()
|
||||
}
|
||||
|
||||
override fun show(v: View, anim: ViewPropertyAnimator) {
|
||||
anim.alpha(1f)
|
||||
anim.translationY(0f)
|
||||
anim.interpolator = DecelerateInterpolator()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
package org.koitharu.kotatsu.utils.ext
|
||||
|
||||
import android.view.View
|
||||
import org.koitharu.kotatsu.utils.anim.Duration
|
||||
import org.koitharu.kotatsu.utils.anim.Motion
|
||||
|
||||
fun View.showAnimated(
|
||||
effect: Motion,
|
||||
duration: Duration = Duration.MEDIUM,
|
||||
onDone: (() -> Unit)? = null
|
||||
) {
|
||||
if (this.visibility == View.VISIBLE) {
|
||||
onDone?.invoke()
|
||||
return
|
||||
}
|
||||
this.clearAnimation()
|
||||
effect.hideView(this)
|
||||
this.visibility = View.VISIBLE
|
||||
this.animate().also {
|
||||
it.duration = context.resources.getInteger(duration.resId).toLong()
|
||||
effect.show(this, it)
|
||||
}.withEndAction(onDone)
|
||||
.start()
|
||||
}
|
||||
|
||||
fun View.hideAnimated(
|
||||
effect: Motion,
|
||||
duration: Duration = Duration.MEDIUM,
|
||||
onDone: (() -> Unit)? = null
|
||||
) {
|
||||
if (this.visibility != View.VISIBLE) {
|
||||
onDone?.invoke()
|
||||
return
|
||||
}
|
||||
this.clearAnimation()
|
||||
this.animate().also {
|
||||
it.duration = context.resources.getInteger(duration.resId).toLong()
|
||||
effect.hide(this, it)
|
||||
}.withEndAction {
|
||||
this.visibility = View.GONE
|
||||
effect.reset(this)
|
||||
onDone?.invoke()
|
||||
}.start()
|
||||
}
|
||||
|
||||
fun View.showOrHideAnimated(
|
||||
predicate: Boolean,
|
||||
effect: Motion,
|
||||
duration: Duration = Duration.MEDIUM,
|
||||
onDone: (() -> Unit)? = null
|
||||
) {
|
||||
if (predicate) {
|
||||
showAnimated(effect, duration, onDone)
|
||||
} else {
|
||||
hideAnimated(effect, duration, onDone)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user