我在项目中使用的是最新的Nav组件(2.2.0-alpha01),但出现了一个似乎无法解决的问题。
我有一个启动动画-没什么大不了的,使用ConstraintLayout的自定义背景延伸到整个屏幕,并且在中间有一个徽标。在初始同步期间,我为自定义动画VectorDrawable动画(我们将其命名为@drawable/logo_animated
),该动画使用通用的@drawable/logo
作为源,并将动画应用于其组。
为确定动画的正确时间,我创建了以下辅助函数:
fun ImageView.setRepeatingAnimatedVector(
@DrawableRes animationRes: Int,
delayMs: Long = 0,
startDelayMs: Long = 0,
shouldRunOptional: () -> Boolean = { false },
optionalRunnable: () -> Unit = {}
) {
val anim = AnimatedVectorDrawableCompat.create(context, animationRes)?.apply {
registerAnimationCallback(object : Animatable2Compat.AnimationCallback() {
override fun onAnimationEnd(drawable: Drawable?) {
this@setRepeatingAnimatedVector.postDelayed({ if (shouldRunOptional()) optionalRunnable() else start() }, delayMs)
}
})
}
setImageDrawable(anim)
postDelayed({ anim?.start() }, startDelayMs)
}
它将AnimatedVectorDrawable作为输入,并将其应用于ImageView。完成动画周期后,将执行lambda(shouldRunOptional
)形式的检查。如果返回true,则启动optionalRunnable
lambda,否则将重复动画。
有了这个,我可以等待ViewModel完成同步,然后等待动画的结尾在片段之间移动而没有任何怪异。动画本身很短(〜900ms),所以最多用户会延迟一秒钟。
我还使用自定义的NavigationManager组合进行导航。 Manager本身是被注入到ViewModels中的通用调用(例如INavigationManager
或splashToLanding()
)的接口(openDetail(id: UUID)
),其中一个额外的接口负责NavComponent的特定位:< / p>
IFragmentNavigator.kt
interface IFragmentNavigator {
val command: SingleLiveEvent<NavigationCommand>
var splashLandingExtras: Navigator.Extras?
fun setSplashLandingTransition(extras: Navigator.Extras) {
splashLandingExtras = extras
}
fun back() {
navigate(NavigationCommand.Back)
}
fun navigate(direction: NavDirections) {
navigate(NavigationCommand.To(direction))
}
fun navigate(navCommand: NavigationCommand) {
command.postValue(navCommand)
}
}
该实现只负责属性初始化,然后以下列方式使用:
class FragmentNavigationManager:
INavigationManager, IFragmentNavigator by FragmentNavigator() { [...] }
此接口的command
属性随后将通过观察者在片段中使用:
open val navigationObserver = Observer<NavigationCommand> {
when(it) {
is NavigationCommand.To -> findNavController().navigate(it.directions)
is NavigationCommand.Back -> findNavController().popBackStack()
is NavigationCommand.BackTo -> findNavController().popBackStack(it.destinationId, false)
is NavigationCommand.ToRoot -> TODO()
}
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
navigator.command.observe(this, navigationObserver)
}
在FragmentNavigationManager中创建的Directions
实例由NavController直接使用。我确保将FragmentNavigator的Extras字段添加到实际的导航调用中:
override fun splashToLanding() {
navigate(
NavigationCommand.To(
SplashFragmentDirections.actionSplashFragmentToLandingFragment(),
null, null, splashLandingExtras
)
)
}
当然,在SplashFragment中,我为splashLandingExtras
的过渡名称分配了适当的视图:
navigator.splashLandingExtras = FragmentNavigatorExtras(binding.logo to "logo")
在LandingFragment的onCreate
方法中,我确实设置了输入和退出动画:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
TransitionInflater.from(context).inflateTransition(android.R.transition.move).let {transition ->
sharedElementEnterTransition = transition
sharedElementReturnTransition = transition
}
}
布局如下:
splash.xml
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="viewModel"
type="com.felcana.app.viewmodel.SplashViewModel" />
</data>
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
<include layout="@layout/background" />
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
<ImageView
android:id="@+id/logo"
style="?attr/logoStyle"
android:transitionName="logo"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</layout>
landing.xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="viewModel"
type="com.my.app.viewmodel.LandingViewModel" />
</data>
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
<include layout="@layout/background" />
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
<ImageView
android:id="@+id/logo"
style="?attr/logoStyle"
android:layout_marginBottom="24dp"
android:transitionName="logo"
app:layout_constraintBottom_toTopOf="@id/button_register"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<com.google.android.material.button.MaterialButton
android:id="@+id/button_register"
style="?attr/flatWhiteButtonStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
android:layout_marginEnd="24dp"
android:layout_marginBottom="8dp"
android:onClick="@{() -> viewModel.goToRegister()}"
android:text="@string/button_register"
app:layout_constraintBottom_toTopOf="@id/button_login"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<com.google.android.material.button.MaterialButton
android:id="@+id/button_login"
style="?attr/borderlessWhiteButtonStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="24dp"
android:onClick="@{() -> viewModel.goToLogin()}"
android:text="@string/button_login"
app:layout_constraintBottom_toTopOf="@id/disclaimer"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<TextView
android:id="@+id/disclaimer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
android:layout_marginEnd="24dp"
android:layout_marginBottom="32dp"
android:maxLines="2"
android:textAlignment="center"
android:textColor="@color/app_white"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
tools:text="@tools:sample/lorem/random" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</layout>
由于某种原因,动画根本无法播放-ImageView只是跳转而没有任何过渡到新位置的动作。
这是怎么回事?根据文档,这应该起作用。我确实尝试过返回更稳定的NavComponent库版本,但无济于事。
答案 0 :(得分:0)
您是否尝试过推迟LandingFragment::onViewCreated
内的enter转换并手动将转换名称设置为视图?
类似这样的东西:
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
// Pause the enter transition
postponeEnterTransition()
// Manually apply the transitionName
logo.transitionName = "logo"
// Resume the transition
startPostponedEnterTransition()
super.onViewCreated(view, savedInstanceState)
}