Android:如何设置WindowIsFloating Activity尺寸变化的动画时长?
前置说明
我正在开发一个类Dialog的组件,内部包含FragmentContainerView,因此整个Dialog的高度会动态变化。
已在执行Fragment事务的Fragment中设置了TransitionManager.beginDelayedTransition。
问题
内部内容的动画符合预期,但整个视图的外部边界未同步动画——尺寸变大的速度过快。

底部边界应与底部logo的动画速度保持一致,但当前底部边界的变化速度比logo快。此外,下半部分似乎完全没有动画效果,仅上半部分有。
已实现的代码
以下是我用到的代码:
Activity
最外层Activity仅在中心包含一个FragmentContainerView。
class LoginFormActivity : AppCompatActivity() { private var _binding: ActivityLoginFormBinding? = null private val binding get() = _binding!! override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) _binding = ActivityLoginFormBinding.inflate(layoutInflater) setContentView(binding.root) if (savedInstanceState == null) { supportFragmentManager.commit { setReorderingAllowed(true) this.add<LoginPopupFragment>(R.id.fragmentContainerView) } } } }
LoginPopupFragment
登录对话框的父Fragment。
class LoginPopupFragment : Fragment(), ShouldTransactListener { private var _binding: FragmentLoginPopupBinding? = null private val binding get() = _binding!! override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { _binding = FragmentLoginPopupBinding.inflate(layoutInflater) // 对话框显示时添加首个内部Fragment childFragmentManager.commit { setReorderingAllowed(true) add(binding.dialogInnerContainer.id, viewModel.currentPage.value.java, null) } return binding.root } override fun onDestroyView() { super.onDestroyView() _binding = null } override fun<T: Fragment> onTransition(targetClass: KClass<out T>) { // 为切换Fragment设置过渡动画 val transition: Transition = cardStackIn(duration = 700) TransitionManager.beginDelayedTransition(binding.root, transition) // 切换内部Fragment childFragmentManager.commit { setReorderingAllowed(true) replace(binding.dialogInnerContainer.id, targetClass.java, null) addToBackStack(null) } } /* * 动画构造方法 */ @SuppressLint("RtlHardcoded") private fun cardStackIn(duration: Long): TransitionSet { return TransitionSet().apply { this.ordering = TransitionSet.ORDERING_TOGETHER // 外部元素与前一个Fragment addTransition(TransitionSet().apply { this.ordering = TransitionSet.ORDERING_TOGETHER // 前一个Fragment应渐隐 addTransition(Fade().apply { this.mode = Visibility.MODE_OUT this.duration = duration / 2 }) // 外部元素(边界与logo)应滑动 addTransition(ChangeBounds().apply { this.duration = 50 }) }) // 新Fragment将呈现卡片堆叠效果 addTransition(TransitionSet().apply { this.ordering = TransitionSet.ORDERING_TOGETHER this.startDelay = duration / 7 * 2 addTransition(Slide().apply { this.slideEdge = Gravity.RIGHT this.mode = Visibility.MODE_IN this.duration = duration }) addTransition(Fade().apply { this.mode = Visibility.MODE_IN this.duration = duration * 2 / 3 }) }) } } }
事务操作传递接口
interface ShouldTransactListener { fun<T: Fragment> onTransition(targetClass: KClass<out T>) }
XML布局文件
为提高可读性,移除了部分非关键属性(标签保留)
Activity布局XML
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout android:layout_width="match_parent" android:layout_height="match_parent"> <androidx.fragment.app.FragmentContainerView android:id="@+id/fragmentContainerView" android:backgroundTint="@color/transparent" android:layout_width="0dp" android:layout_height="match_parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent" /> </androidx.constraintlayout.widget.ConstraintLayout>
Fragment布局XML
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/transparent"> <androidx.constraintlayout.widget.ConstraintLayout android:layout_width="0dp" android:layout_height="wrap_content" android:padding="16dp"> <androidx.constraintlayout.widget.ConstraintLayout android:id="@+id/topBox" android:layout_width="0dp" android:layout_height="wrap_content" android:paddingVertical="16dp" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent"> <Button android:id="@+id/backIcon" app:layout_constraintHorizontal_chainStyle="spread_inside"/> <androidx.appcompat.widget.AppCompatImageView android:id="@+id/logo" /> </androidx.constraintlayout.widget.ConstraintLayout> <TextView android:id="@+id/title" android:text="@string/loginPage_title" /> <androidx.core.widget.NestedScrollView android:id="@+id/scrollView" android:layout_marginHorizontal="16dp" android:layout_marginVertical="16dp"> <androidx.fragment.app.FragmentContainerView android:id="@+id/dialog_inner_container" /> </androidx.core.widget.NestedScrollView> <androidx.appcompat.widget.AppCompatImageView android:id="@+id/logo" app:srcCompat="@drawable/logo_blue" /> </androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
样式文件
<style name="Theme.MyApp" parent="Theme.Material3.DayNight.NoActionBar"> <item name="buttonStyle">@style/Widget.Material3.Button.TonalButton</item> <item name="android:windowIsTranslucent">true</item> <item name="android:windowContentOverlay">@null</item> <item name="android:windowNoTitle">true</item> <item name="android:windowIsFloating">true</item> <item name="android:windowBackground">@drawable/rounded_dialog</item> <item name="android:backgroundDimEnabled">false</item> <item name="android:windowBackgroundBlurRadius">20dp</item> <!-- WindowIsFloating相关设置 --> <item name="android:windowMinWidthMajor">50%</item> <item name="android:windowMinWidthMinor">85%</item> <item name="android:windowElevation">8dp"</item> <item name="android:windowClipToOutline">true</item> <item name="android:windowCloseOnTouchOutside">false</item> <item name="android:windowLightStatusBar">?android:isLightTheme</item> </style>
内容的提问来源于stack exchange,提问作者Todoroki
相关产品推荐
相关产品推荐

