【问题标题】:Animating Toolbar along with Fragment Transactions动画工具栏和片段事务
【发布时间】:2015-06-13 06:53:25
【问题描述】:

我有 MainActivity,它使用下面的 xml 实现 Navigation Drawer:

<android.support.v4.widget.DrawerLayout
xmlns:android ="http://schemas.android.com/apk/res/android"
android:id ="@+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">

<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <LinearLayout
        android:id="@+id/container_toolbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        >

        <include
            android:id="@+id/toolbar"
            layout="@layout/toolbar" />
    </LinearLayout>

    <FrameLayout
        android:id="@+id/content"
        android:layout_width="fill_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:background="#FFFFFF"/>


</LinearLayout>

<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="240dp"
    android:layout_height="match_parent"
    android:layout_gravity ="start"
    >


    <ListView
        android:id="@+id/drawerList"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_marginTop="5dp"
        android:divider="@color/material_blue_grey_800"
        android:dividerHeight="1dp"
        android:background="#FFFFFF"
        />

   </RelativeLayout>

</android.support.v4.widget.DrawerLayout>

现在我的列表视图中有 3 个项目,单击其中任何一个项目时,我的代码将 Framelayout 替换为该特定片段,如下所示:

Fragment f1 = new Fragment()

 FragmentTransaction ft = getSupportFragmentManager().beginTransaction()
 ft.setCustomAnimations(R.anim.slide_in,R.anim.hyper_out,R.anim.hyper_in,R.anim.slide_out)


ft.replace(R.id.content, f1).addToBackStack(null).commit();

上面的代码可以很好地根据需要用自定义动画替换片段。但是,我的问题是如何在片段事务期间为工具栏与片段一起设置动画。

所有片段都有各自的工具栏标题,这些标题在每个片段类的 onActivityCreated() 方法中通过以下代码进行更改:

((AppCompatActivity)getActivity()).getSupportActionBar().setTitle("Title");

我应该在我的布局中应用动画来掩盖工具栏吗?

【问题讨论】:

    标签: android animation android-fragments


    【解决方案1】:

    我也有同样的问题 - 它似乎在每个应用程序的开发过程中的某个时刻突然出现,我从来没有解决它,并恢复到一些低于标准的解决方案。现在我带着解决方案回来了!

    用 Kotlin 回答,但同样的原则也适用于 Java。

    要为ToolbarFragmentTransaction 动画制作动画,您首先需要一种方法来通知持有ToolbarActivity/Fragment 动画正在开始/结束。我会打电话给Activity/Fragment 持有ToolbarToolbarHost

    首先,定义一个ToolbarHost可以实现的接口,用于接收回调:

    interface FragmentAnimationListener {
    
        fun onAnimationStart(fragment: Fragment, animation: Animation, enter: Boolean)
    
        fun onAnimationEnd(fragment: Fragment, animation: Animation, enter: Boolean)
    }
    

    我还添加了一个扩展方法来帮助孩子Fragment找到有兴趣接收回调的父母ToolbarHost

    fun Fragment.findParentAnimationListener(): FragmentAnimationListener? {
        return when (parentFragment) {
            is FragmentAnimationListener -> parentFragment as FragmentAnimationListener
            null -> return activity as? FragmentAnimationListener
            else -> parentFragment?.findParentAnimationListener()
        }
    }
    

    现在我们将以下内容添加到即将在屏幕上动画化的子 Fragment。我建议将以下内容添加到基础 Fragment 类中。这会拦截从FragmentTransaction* 提供给子Fragment 的动画,添加一个监听器,找到ToolbarHost,并通知它我们的动画开始/结束。

    override fun onCreateAnimation(transit: Int, enter: Boolean, nextAnim: Int): Animation? {
        var animation = super.onCreateAnimation(transit, enter, nextAnim)
    
        if (animation == null && nextAnim != 0) {
            animation = AnimationUtils.loadAnimation(activity!!, nextAnim)
        }
    
        val parentAnimationListener = findParentAnimationListener()
    
        if (animation != null) {    
            animation.setAnimationListener(object : Animation.AnimationListener {
    
                override fun onAnimationStart(animation: Animation) {
                    parentAnimationListener?.onAnimationStart(this@BaseFragment, animation, enter)
                }
    
                override fun onAnimationEnd(animation: Animation) {
                    parentAnimationListener?.onAnimationEnd(this@BaseFragment, animation, enter)
                }
    
                override fun onAnimationRepeat(animation: Animation?) {
                }
            })
        }
    
        return animation
    }
    

    * 这适用于通过FragmentTransaction.setCustomAnimation(int, int, ..) 提供的动画。我没有用不同类型的FragmentTransaction 动画或没有FragmentTransaction 动画对此进行测试。

    既然我们的ToolbarHost 知道孩子Fragment 何时执行其动画,我们可以考虑为工具栏设置动画。

    假设你的ToolbarHost是一个片段,我们首先实现FragmentAnimationListener

    class ParentFragment : Fragment(), FragmentAnimationListener {
    
        override fun onAnimationStart(fragment: Fragment, animation: Animation, enter: Boolean) {
            ...
        }
    
        override fun onAnimationEnd(fragment: Fragment, animation: Animation, enter: Boolean) {
            ...
        }
    }
    

    现在,我们马上遇到的一个问题是,由于我们将 AnimationListener 添加到基础 Fragment 类中,我们的 onAnimationStartonAnimationEnd 将从正在退出的片段中调用,以及正在输入的片段(假设您的 FragmentTransaction 正在用另一个片段替换一个片段)。所以在我们尝试对Toolbar 做任何事情之前,我们需要只过滤掉我们感兴趣的片段。

    我将使用“进入”Fragment 作为工具栏动画的触发器 - 当我们弹出堆栈时,我将再次使用相同的Fragment(现在“退出”)。所以,我唯一感兴趣的Fragment 是正在转换的那个

    class ParentFragment : Fragment(), FragmentAnimationListener {
    
        var newFragment: Fragment? = null 
    
        fun replaceFragment(newFragment: Fragment) {
    
            // Store a reference to the fragment we're transitioning to
            this.newFragment = newFragment
    
            childFragmentManager.beginTransaction()
                .setCustomAnimations(..)
                .replace(container, newFragment, tag)
                .commit()
        }
    
        override fun onAnimationStart(fragment: Fragment, animation: Animation, enter: Boolean) {
            if (fragment == newFragment) {
                if (enter) {
                    // Animate our Toolbar in
                } else {
                    // Animate our Toolbar out
                }
            }
        }
    }
    

    最后一步是实际执行工具栏动画。您可以使用自己的时间、插值器等创建自己的Animation - 但我更喜欢使用相同的Animator 来为孩子Fragment 设置动画。这样,如果我们淡入,Toolbar 将淡入。如果我们滑出,Toolbar 将滑出。通过使用相同的Animation,我们可以确定我们有正确的持续时间和插值器,所以我们的Animations 将同时运行。

    override fun onAnimationStart(fragment: Fragment, animation: Animation, enter: Boolean) {
        if (fragment == newFragment) {
            toolbar.animation = animation
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2015-09-26
      • 2018-09-24
      • 2022-10-17
      • 1970-01-01
      • 2015-10-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多