Do Dribbble ao Android Motion



Na Internet, existem muitos modelos animados interessantes com belas interfaces de usuário de aplicativos móveis, mas não existem muitos exemplos com a implementação dessas interfaces. Apesar da abundância de várias estruturas e ferramentas internas no SDK do Android, muitas vezes não é fácil para um desenvolvedor iniciante implementar uma interface de usuário bonita, inclusive com um modelo pronto.

Neste artigo, tentaremos implementar a interface do usuário desenvolvida pelo designer Ivan Parfyonov para o estúdio PLATES.


Primeiro, crie dois fragmentos: RecyclerFragment e DetailsFragment .


Estrutura de transição do Android?


A estrutura do Android Transition funciona bem, mas existem algumas nuances. Primeiro, queremos que tudo funcione para nós, pelo menos, na API 19 e, segundo, precisamos animar vários elementos do usuário ao mesmo tempo e alguns deles estão presentes em apenas um fragmento. Portanto, implementamos a animação do elemento de transição (transição de elemento compartilhado) manualmente usando ViewPropertyAnimator .

Tudo em ordem


  1. Calculamos as coordenadas finais do elemento selecionado a partir da lista (suas coordenadas em DerailsFragment ); a lista é um RecyclerView ;
  2. Nós salvamos as coordenadas atuais (coordenadas em RecyclerFragment ) e as passamos para DetailsFragment (isso é necessário para animação reversa com API <21);
  3. Crie uma cópia do item selecionado da lista;
  4. Tornamos o elemento selecionado invisível (não uma cópia, mas o próprio elemento);
  5. Adicione a cópia criada na etapa 3 ao layout raiz do fragmento pai; no nosso caso, é RecyclerFragment ;
  6. Iniciamos a animação dos elementos restantes da interface e movemos a cópia criada para as coordenadas finais da etapa 1;
  7. Quando a animação terminar, crie uma transação e mostre DetailsFragment ;
  8. Iniciamos a animação dos elementos da interface em DetailsFragment .

Animação de elementos de interface do usuário


Para animar a Toolbar criaremos uma View adicional no RecyclerFragment e a colocaremos atrás da tela na parte superior. Essa View será animada no contêiner da Toolbar em DetailsFragment (cor azul no gif) usando o ViewPropertyAnimator .

 <View android:id="@+id/details_toolbar_helper" android:layout_width="wrap_content" android:layout_height="@dimen/details_toolbar_container_height" android:background="@color/colorPrimary" app:layout_constraintTop_toTopOf="parent"/> // In RecyclerFragment details_toolbar_helper.translationY = -details_toolbar_helper.height 

imagem

BottomNavigationView animações BottomNavigationView e RecyclerView também BottomNavigationView implementadas usando o ViewPropertyAnimator , nada complicado (alterar a transparência e a movimentação).

Um pouco da estrutura de transição


Em palavras simples, a estrutura de transição do Android, quando começa a animar o elemento de transição, cria uma cópia do conteúdo desse elemento de transição (algo como tela de impressão), cria um ImageView a partir dessa cópia e adiciona essa imagem à camada de sobreposição extra na camada de sobreposição o fragmento chamado e inicia a animação.

A estrutura de transição do Android não é adequada para nós, porque quando a animação do elemento de transição começa, todos os outros elementos da interface do usuário no fragmento são destruídos e não podemos animar. I.e. quando clicamos em um item da lista no RecyclerFragment para abrir o DetailsFragment e iniciar a animação de transição, todos os outros elementos da interface no RecyclerFragment destruídos sem animação.

Para obter o resultado desejado, criaremos manualmente uma cópia do elemento selecionado da lista, adicionaremos à camada de sobreposição e, em seguida, animaremos. Mas aqui aparece um pequeno problema, a documentação para o método ViewGroupOverlay add(view: View) viewGroupOverlay diz:
Se a vista tiver um pai, a vista será removida desse pai antes de ser adicionada à sobreposição.

Mas, para o RecyclerView isso não funciona, o item selecionado não é excluído do RecyclerView após ser adicionado à camada de sobreposição.

Aqui está o que acontece quando adicionamos o item selecionado à camada de sobreposição:



E precisamos disso:



Portanto, não usaremos a camada de sobreposição e adicionaremos a cópia imediatamente ao layout raiz. Crie uma cópia do conteúdo do item selecionado, adicione-o ao ImageView e defina as coordenadas:

 fun View.copyViewImage(): View { val copy = ImageView(context) val bitmap = drawToBitmap() copy.setImageBitmap(bitmap) //  pre-Lollipop   ,   card view  ,      card view return (if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { CardView(context).apply { cardElevation = resources.getDimension(R.dimen.card_elevation) radius = resources.getDimension(R.dimen.card_corner_radius) addView(copy) } } else { copy }).apply { layoutParams = this@copyViewImage.layoutParams layoutParams.height = this@copyViewImage.height layoutParams.width = this@copyViewImage.width x = this@copyViewImage.x y = this@copyViewImage.y } } 


Por que criar uma cópia se você pode apenas animar o item selecionado diretamente da lista?

Como o próprio RecyclerView também será animado e, consequentemente, todos os seus elementos, incluindo o selecionado, que queremos animar separadamente.

Depois disso, adicione uma cópia à marcação raiz e inicie a animação.

 override fun onClick(view: View) { val fragmentTransaction = initFragmentTransaction(view) val copy = view.createCopyView() root.addView(copy) view.visibility = View.INVISIBLE startAnimation(copy, fragmentTransaction) } 


E aqui está o que temos:



Linha de chegada


A animação gif acima ocorre em um RecyclerFragment e, após sua conclusão, precisamos mostrar DetailsFragment .

 .withEndAction { fragmentTransaction?.commitAllowingStateLoss() } 

Por que estamos usando o commitAllowingStateLoss ?

Se você não usá-lo e no momento da animação, ocorrerá, por exemplo, uma alteração na orientação da tela, obteremos IllegalStateExeption . Aqui está bem escrito sobre isso.

Em seguida, iniciamos a animação dos elementos necessários da interface do usuário em DetailsFragment .

Execute tudo junto




Não é exatamente o mesmo que o original, mas parece semelhante.

Exemplos


O código fonte está disponível no GitHub e o artigo também está disponível em inglês .

Obrigado pela atenção!

Source: https://habr.com/ru/post/pt423235/


All Articles