Adicionar suporte a animações de volta preditiva em Views

É possível criar animações e transições de propriedades personalizadas no app, animações personalizadas entre atividades e animações personalizadas entre fragmentos com gestos de volta preditiva usando Views ou Compose. Para testar a maneira do Compose, consulte Adicionar suporte a animações de volta preditiva.

Adicionar transições personalizadas usando a API Progress

Com o AndroidX Activity 1.8.0-alpha01 ou mais recente, é possível usar as APIs Predictive Back Progress para desenvolver animações personalizadas para o gesto de volta preditiva no app. As APIs Progress são úteis para animar visualizações, mas têm limitações ao animar transições entre fragmentos. No OnBackPressedCallback, introduzimos os métodos handleOnBackProgressed, handleOnBackCancelled e handleOnBackStarted para animar objetos enquanto o usuário desliza para trás. Use esses métodos se precisar personalizar mais do que as animações padrão fornecidas pelo sistema ou as animações do componente do Material Design.

É esperado que a maioria dos apps use as APIs do AndroidX compatíveis com versões anteriores, mas também há APIs de plataformas semelhantes na interface OnBackAnimationCallback disponíveis para teste no Android 14 e versões mais recentes.

Usar as APIs Progress com transições do AndroidX

As APIs Progress podem ser usadas com o AndroidX Transitions 1.5.0-alpha01 ou mais recente no Android 14 e versões mais recentes para criar transições de volta preditiva.

  1. Use TransitionManager#controlDelayedTransition em vez de beginDelayedTransition para iniciar transições quando o usuário deslizar para voltar.
  2. Crie a transição em handleOnBackStarted.
  3. Inicie a transição com o evento de retorno dentro do handleOnBackProgressed, relacionando currentFraction a BackEvent.progress, que expõe até que ponto o usuário deslizou para voltar.
  4. Conclua a transição depois que o usuário confirmar o gesto de volta em handleOnBackPressed.
  5. Por fim, redefina o estado da transição dentro de handleOnBackCancelled.

O vídeo, o código Kotlin e o XML a seguir demonstram uma transição personalizada entre duas caixas implementadas com OnBackPressedCallback:

    class MyFragment : Fragment() {

    val transitionSet = TransitionSet().apply {
        addTransition(Fade(Fade.MODE_OUT))
        addTransition(ChangeBounds())
        addTransition(Fade(Fade.MODE_IN))
    }
    ...
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        val callback = object : OnBackPressedCallback(enabled = false) {

            var controller: TransitionSeekController? = null

            @RequiresApi(34)
            override fun handleOnBackStarted(backEvent: BackEvent) {
                // Create the transition
                controller = TransitionManager.controlDelayedTransition(
                    binding.card,
                    transitionSet
                )
                changeTextVisibility(ShowText.SHORT)
            }

            @RequiresApi(34)
            override fun handleOnBackProgressed(backEvent: BackEvent) {
                // Play the transition as the user swipes back
                if (controller?.isReady == true) {
                    controller?.currentFraction = backEvent.progress
                }
            }

            override fun handleOnBackPressed() {
                // Finish playing the transition when the user commits back
                controller?.animateToEnd()
                this.isEnabled = false
            }

            @RequiresApi(34)
            override fun handleOnBackCancelled() {
                // If the user cancels the back gesture, reset the state
                transition(ShowText.LONG)
            }
        }

        binding.shortText.setOnClickListener {
            transition(ShowText.LONG)
            callback.isEnabled = true
        }

        this.requireActivity().onBackPressedDispatcher.addCallback(callback)
    }

    private fun transition(showText: ShowText) {
        TransitionManager.beginDelayedTransition(
            binding.card,
            transitionSet
        )
        changeTextVisibility(showText)
    }

    enum class ShowText { SHORT, LONG }
    private fun changeTextVisibility(showText: ShowText) {
        when (showText) {
            ShowText.SHORT -> {
                binding.shortText.isVisible = true
                binding.longText.isVisible = false
            }
            ShowText.LONG -> {
                binding.shortText.isVisible = false
                binding.longText.isVisible = true
            }
        }
    }
}
  
<?xml version="1.0" encoding="utf-8"?>
...
    <androidx.constraintlayout.widget.ConstraintLayout
        android:id="@+id/card"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        ...>

        <TextView
            android:id="@+id/short_text"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            ... />

        <TextView
            android:id="@+id/long_text"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:visibility="gone"
            .../>

    </androidx.constraintlayout.widget.ConstraintLayout>

Ao trabalhar com transições de volta preditiva, considere o seguinte:

  • Use isSeekingSupported para verificar se a transição tem suporte à volta preditiva.
  • Substitua isSeekingSupported para retornar o valor "true" para suas transições personalizadas.
  • Crie um controle por animação.
  • As transições de volta preditiva têm suporte das transições do AndroidX, mas não das de framework. Migre das transições do framework e use Animator e transições do AndroidX.
  • As transições de volta preditiva podem ser usadas em dispositivos com Android 14 e versões mais recentes e não são compatíveis com versões anteriores.
  • Também há suporte a transições criadas com cenas XML. Em handleOnBackStarted, defina TransitionSeekController como o resultado de TransitionManager.createSeekController em vez do resultado de controlDelayedTransition.

Outros recursos