Понимание фрагментов в Android: часть 3

Понимание фрагментов в Android: часть 3

23 ноября 2022 г.

В этой статье мы разберем интересные моменты анимации Fragment API.

Вы можете прочитать предыдущие части статьи по ссылке ниже.

Мы можем определить простые анимации для переходов между фрагментами в папке res/anim. Но если мы хотим управлять какими-либо атрибутами представления нашего фрагмента, мы должны указать анимации в папке res/animator. Более того, мы можем легко комбинировать их внутри транзакции.

fragmentManager.commit {
    setReorderingAllowed(true) 
    // Must be specified before add/replace otherwise they will be ignored
    setCustomAnimations( 
        R.animator.anim_enter, // InnerFragment appears on the screen
        R.anim.anim_exit, // OuterFragment goes off screen
        R.anim.anim_pop_enter, // OuterFragment returns to screen 
        R.animator.anim_pop_exit // InnerFragment goes off screen
        ) 

        replace<InnerFragment>(R.id.container) 
        addToBackStack(null) 
    }

Эти анимации автоматически применяются ко всем последующим транзакциям с использованием этого fragmentManager.

<!-- animator/anim_enter.xml --> 
<objectAnimator 
    xmlns:android="http://schemas.android.com/apk/res/android" 
    android:duration="400" 
    android:valueFrom="0" 
    android:valueTo="180" 
    android:propertyName="rotation" /> 

<!-- anim/anim_exit.xml -->     
<alpha 
    xmlns:android="http://schemas.android.com/apk/res/android" 
    android:duration="400" 
    android:interpolator="@android:anim/decelerate_interpolator" 
    android:fromAlpha="1" 
    android:toAlpha="0" /> 

<!-- anim/anim_pop_enter.xml --> 
<alpha 
    xmlns:android="http://schemas.android.com/apk/res/android" 
    android:duration="400" 
    android:interpolator="@android:anim/decelerate_interpolator" 
    android:fromAlpha="0" 
    android:toAlpha="1" /> 

<!-- animator/anim_pop_exit.xml --> 
<objectAnimator 
    xmlns:android="http://schemas.android.com/apk/res/android" 
    android:duration="400" 
    android:valueFrom="180" 
    android:valueTo="360" 
    android:propertyName="rotation" />

Если не хотите прописывать каждую анимацию, можно использовать Transition — например, подготовленный Fade() . Он указан в InnerFragment и переопределяет анимацию в транзакции, если она была указана.

// InnerFragment.kt 

override fun onCreate(savedInstanceState: Bundle?) { 
    super.onCreate(savedInstanceState) 
    // Animation when moving to the screen 
    enterTransition = Fade() 

    // Animation when leaving the screen via fragmentManager.popBackStack()
    // If not specified, will be used
    enterTransition exitTransition = Fade() 

    // Animation when exiting the screen is not via fragmentManager.popBackStack()
    // For example, via replace() 
    // If not specified, will be used
    enterTransition returnTransition = Fade() 

    // Animation when returning to the screen via
    fragmentManager.popBackStack()
    // If not specified, will be used
    enterTransition reenterTransition = Fade() 
    }

Это еще не главные функции анимации в Fragment API. Следующим этапом разработки являются общие переходы элементов, с помощью которых можно получить реализацию перехода.

Для создания такой анимации мы будем использовать метод FragmentTransaction.addSharedElement(View, String) и стандартный переход ChangeBounds().

Во-первых, нам нужно сделать элементы представления, которые мы хотим анимировать, уникальными в пределах разметки transitionName. Это можно сделать через xml или в коде. В InnerFragment мы указываем анимации:

<!--- fragment_outer_layout.xml --> 
<TextView 
    android:id="@+id/textViewStart" 
    android:layout_width="wrap_content" 
    android:layout_height="wrap_content" 
    android:text="Outer Fragment" 
    android:transitionName="text_start" /> 

<!--- fragment_inner_layout.xml --> 
<TextView 
    android:id="@+id/textViewDestination" 
    android:layout_width="wrap_content" 
    android:layout_height="wrap_content" 
    android:text="Inner Fragment" 
    android:transitionName="text_destination" />

// OuterFragment.kt 
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 
    ViewCompat.setTransitionName(imageViewStart, "image_start") 
} 

// InnerFragment.kt 
override fun onCreate(savedInstanceState: Bundle?) { 
    super.onCreate(savedInstanceState) 
    // Animation when opening a fragment
    sharedElementEnterTransition = ChangeBounds() 

    // Animations when closing a fragment
    // If not specified, sharedElementEnterTransition will be used 
    sharedElementReturnTransition = ChangeBounds() 
} 

override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 
    ViewCompat.setTransitionName(imageViewDestination, "image_destination") 
}

Вызываем транзакцию:

// OuterFragment.kt 
parentFragmentManager.commit { 
    setReorderingAllowed(true) 
    addSharedElement(imageViewStart, "image_destination") 
    addSharedElement(textViewStart, "text_destination") 
    replace<InnerFragment>(R.id.container) 
    addToBackStack(null) 
}

Если все представления отрисовываются синхронно, мы можем получить показанную выше анимацию точно так же.

Если мы используем переход общего элемента с RecyclerView, нам нужно помнить, что он отрисовывает свои элементы после того, как отрисовывается макет экрана. Получается, что анимацию перехода нужно приостановить, пока список элементов не будет готов к отрисовке.

override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 
    // Pausing the transition
    postponeEnterTransition() 

    // Waiting for everything to load
    viewModel.data.observe(viewLifecycleOwner) { 
    // Passing data to RecyclerView adapter
    adapter.setData(it) 
    // We are waiting for all the elements to be ready for drawing, and start 
    // the animation 
    (view.parent as? ViewGroup)?.doOnPreDraw { 
        startPostponedEnterTransition() 
        } 
    } 
}

Метод delayEnterTransition() требует использования FragmentTransaction.setReorderingAllowed(true).

Я также прикреплю ссылку на официальную документацию от Google по анимации фрагментов здесь.


Оригинал
PREVIOUS ARTICLE
NEXT ARTICLE