You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 17:30:54