Carrossel com MotionLayout

Teste o jeito do Compose
O Jetpack Compose é o kit de ferramentas de UI recomendado para Android. Saiba como adicionar um carrossel no Compose.

Carousel é um objeto auxiliar de movimento para criar visualizações de carrossel personalizadas que mostram uma lista de elementos que o usuário pode percorrer. Em comparação com outras maneiras de implementar essas visualizações, esse auxiliar permite criar rapidamente movimentos complexos e mudanças de dimensão para seu Carousel aproveitando o MotionLayout.

O widget Carousel é compatível com listas com início e fim, além de listas circulares de encapsulamento.

Como o carrossel com MotionLayout funciona

Suponha que você queira criar uma visualização horizontal de Carousel com o item central ampliado:

Esse layout básico contém várias visualizações que representam os itens Carousel:

Crie um MotionLayout com os três estados a seguir e atribua IDs a eles:

  • anterior
  • iniciar
  • Próxima

Se o estado start corresponder ao layout de base, no estado previous e no estado next, os itens Carousel serão movidos um para a esquerda e para a direita, respectivamente.

Por exemplo, considere as cinco visualizações na figura 3 e suponha que, no estado start, as visualizações B, C e D estejam visíveis, e A e E estejam fora da tela. Configure o estado anterior para que as posições de A, B, C e D sejam as mesmas de B, C, D e E, com as visualizações se movendo da esquerda para a direita. No próximo estado, o oposto precisa acontecer, com B, C, D e E se movendo para onde A, B, C e D estavam, e as visualizações se movendo da direita para a esquerda. Isso é mostrado na figura 4:

É fundamental que as visualizações terminem exatamente onde as originais começam. Carousel dá a ilusão de uma coleção infinita de elementos ao mover as visualizações reais de volta para onde estavam, mas reinicializando-as com o novo conteúdo correspondente. O diagrama a seguir mostra esse mecanismo. Preste atenção aos valores de "item #":

Transições

Com esses três conjuntos de restrições definidos no arquivo de cena de movimento, crie duas transições (para frente e para trás) entre os estados start e next e os estados start e previous. Adicione um gerenciador OnSwipe para acionar as transições em resposta a um gesto, conforme mostrado no exemplo a seguir:

    <Transition
        motion:constraintSetStart="@id/start"
        motion:constraintSetEnd="@+id/next"
        motion:duration="1000"
        android:id="@+id/forward">
        <OnSwipe
            motion:dragDirection="dragLeft"
            motion:touchAnchorSide="left" />
    </Transition>

    <Transition
        motion:constraintSetStart="@+id/start"
        motion:constraintSetEnd="@+id/previous"
        android:id="@+id/backward">
        <OnSwipe
            motion:dragDirection="dragRight"
            motion:touchAnchorSide="right" />
    </Transition>

Depois que essa cena de movimento básica for criada, adicione um auxiliar Carousel ao layout e faça referência às visualizações na mesma ordem em que você implementa a animação anterior e a próxima.

Defina os seguintes atributos para o auxiliar Carousel:

  • app:carousel_firstView: a visualização que representa o primeiro elemento do Carousel. Neste exemplo, C.
  • app:carousel_previousState: o ID ConstraintSet do estado anterior.
  • app:carousel_nextState: o ID ConstraintSet do estado seguinte.
  • app:carousel_backwardTransition: o ID Transition aplicado entre os estados start e previous.
  • app:carousel_forwardTransition: o ID Transition aplicado entre os estados start e next.

Por exemplo, você tem algo assim no arquivo XML de layout:

    <androidx.constraintlayout.motion.widget.MotionLayout ... >

        <ImageView  android:id="@+id/imageView0" .. />
        <ImageView  android:id="@+id/imageView1" .. />
        <ImageView  android:id="@+id/imageView2" .. />
        <ImageView  android:id="@+id/imageView3" .. />
        <ImageView  android:id="@+id/imageView4" .. />

        <androidx.constraintlayout.helper.widget.Carousel
            android:id="@+id/carousel"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:carousel_forwardTransition="@+id/forward"
            app:carousel_backwardTransition="@+id/backward"
            app:carousel_previousState="@+id/previous"
            app:carousel_nextState="@+id/next"
            app:carousel_infinite="true"
            app:carousel_firstView="@+id/imageView2"
            app:constraint_referenced_ids="imageView0,imageView1,imageView2,imageView3,imageView4" />

    </androidx.constraintlayout.motion.widget.MotionLayout>

Configure um adaptador Carousel no código:

Kotlin

carousel.setAdapter(object : Carousel.Adapter {
            override fun count(): Int {
              // Return the number of items in the Carousel.
            }

            override fun populate(view: View, index: Int) {
                // Implement this to populate the view at the given index.
            }

            override fun onNewItem(index: Int) {
                // Called when an item is set.
            }
        })

Java

carousel.setAdapter(new Carousel.Adapter() {
            @Override
            public int count() {
                // Return the number of items in the Carousel.
            }

            @Override
            public void populate(View view, int index) {
                // Populate the view at the given index.
            }

            @Override
            public void onNewItem(int index) {
                 // Called when an item is set.
            }
        });

Outras observações

Dependendo do item "selecionado" no Carousel, as visualizações que representam os itens anteriores ou posteriores podem precisar ser ocultadas para contabilizar corretamente o início e o fim do Carousel. O auxiliar Carousel processa isso automaticamente. Por padrão, ele marca essas visualizações como View.INVISIBLE nessas situações, para que o layout geral não mude.

Um modo alternativo está disponível, em que o auxiliar Carousel marca essas visualizações como View.GONE. Você pode definir esse modo usando a seguinte propriedade:

app:carousel_emptyViewsBehavior="gone"

Exemplos

Para mais exemplos usando o auxiliar de carrossel, consulte os projetos de exemplo no GitHub.