Arraste e deslize o dedo no RecyclerView. Parte 2: arraste e solte controladores, grades e animações personalizadas

Arraste e deslize o dedo no RecyclerView. Parte 2: arraste e solte controladores, grades e animações personalizadas


Na primeira parte, vimos o ItemTouchHelper e a implementação do ItemTouchHelper.Callback , que adiciona as funções básicas de arrastar e soltar e deslizar para dispensar no RecyclerView . Neste artigo, continuaremos o que foi feito no anterior, adicionando suporte para organizar elementos na forma de uma grade, controladores de arrastar e soltar, seleção de um item da lista e animações de furto personalizadas.


Malha de elementos


Controladores de arrastar e soltar


Ao criar uma lista que suporta arrastar e soltar , eles geralmente implementam a capacidade de arrastar e soltar itens pelo toque. Isso contribui para a clareza e facilidade de uso da lista no "modo de edição" e também é recomendado pelas diretrizes do material. Adicionar controladores de arrastar e soltar ao nosso exemplo é incrivelmente fácil.


Controladores de arrastar e soltar


Primeiro, atualize o layout elemento ( item_main.xml ).


 <?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/item" android:layout_width="match_parent" android:layout_height="?listPreferredItemHeight" android:clickable="true" android:focusable="true" android:foreground="?selectableItemBackground"> <TextView android:id="@+id/text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:layout_marginLeft="16dp" android:textAppearance="?android:attr/textAppearanceMedium" /> <ImageView android:id="@+id/handle" android:layout_width="?listPreferredItemHeight" android:layout_height="match_parent" android:layout_gravity="center_vertical|right" android:scaleType="center" android:src="@drawable/ic_reorder_grey_500_24dp" /> </FrameLayout> 

A imagem usada para o controlador de arrasto pode ser encontrada nos ícones do Material Design e adicionada ao projeto usando o conveniente plug-in gerador de ícones no Android Studio.


Conforme mencionado brevemente em um artigo anterior, você pode usar o ItemTouchHelper.startDrag(ViewHolder) para iniciar programaticamente uma ItemTouchHelper.startDrag(ViewHolder) de arrastar e soltar. Portanto, tudo o que precisamos fazer é atualizar o ViewHolder adicionando um controlador de arrastar e configurar um manipulador de toque simples que chamará startDrag() .


Vamos precisar de uma interface para transmitir eventos ao longo da cadeia:


 public interface OnStartDragListener { /** * Called when a view is requesting a start of a drag. * * @param viewHolder The holder of the view to drag. */ void onStartDrag(RecyclerView.ViewHolder viewHolder); } 

Em seguida, defina um ImageView para o controlador de arrasto no ItemViewHolder :


 public final ImageView handleView; public ItemViewHolder(View itemView) { super(itemView); // ... handleView = (ImageView) itemView.findViewById(R.id.handle); } 

e atualize o RecyclerListAdapter :


 private final OnStartDragListener mDragStartListener; public RecyclerListAdapter(OnStartDragListener dragStartListener) { mDragStartListener = dragStartListener; // ... } @Override public void onBindViewHolder(final ItemViewHolder holder, int position) { // ... holder.handleView.setOnTouchListener(new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { if (MotionEventCompat.getActionMasked(event) == MotionEvent.ACTION_DOWN) { mDragStartListener.onStartDrag(holder); } return false; } }); } 

RecyclerListAdapter agora deve ser algo assim.


Tudo o que OnStartDragListener ser feito é adicionar o OnStartDragListener ao fragmento:


 public class RecyclerListFragment extends Fragment implements OnStartDragListener { // ... @Override public void onViewCreated(View view, Bundle icicle) { super.onViewCreated(view, icicle); RecyclerListAdapter a = new RecyclerListAdapter(this); // ... } @Override public void onStartDrag(RecyclerView.ViewHolder viewHolder) { mItemTouchHelper.startDrag(viewHolder); } } 

RecyclerListFragment agora deve ficar assim . Agora, quando você inicia o aplicativo, pode começar a arrastar e soltar tocando no controlador.


Arrastando itens da lista


Destacando um Item da Lista


Agora, no nosso exemplo, não há indicação visual do item que está sendo arrastado. Obviamente, isso não deve ser, mas é fácil de corrigir. Usando o ItemTouchHelper você pode usar os efeitos de destaque de itens padrão. No Lollipop e nas versões posteriores do Android, a luz de fundo "se espalha" sobre o elemento no processo de interação com ele; nas versões anteriores, o elemento simplesmente muda de cor para escurecido.


Para implementar isso em nosso exemplo, basta adicionar um plano de fundo (propriedade de background ) ao FrameLayout raiz FrameLayout elemento FrameLayout ou defina-o no construtor RecyclerListAdapter.ItemViewHolder . Será algo parecido com isto:


Seleção de elemento


Parece legal, mas você pode querer controlar ainda mais. Uma maneira de fazer isso é permitir que o ViewHolder manipule as alterações de estado de um elemento. Para isso, o ItemTouchHelper.Callback fornece mais dois métodos:


  • onSelectedChanged(ViewHolder, int) é chamado toda vez que o estado de um elemento muda para arrastar ( ACTION_STATE_DRAG ) ou deslizar ( ACTION_STATE_SWIPE ). Este é o local ideal para alterar o estado do componente de view para ativo .
  • clearView(RecyclerView, ViewHolder) é chamado quando o componente de view arrastar e soltar é concluído e também quando o furto é concluído ( ACTION_STATE_IDLE ). Aqui, o estado inicial do seu componente de view é geralmente restaurado.

Agora, vamos juntar tudo.


Primeiro, crie uma interface que o ViewHolders implementará:


 /** * Notifies a View Holder of relevant callbacks from * {@link ItemTouchHelper.Callback}. */ public interface ItemTouchHelperViewHolder { /** * Called when the {@link ItemTouchHelper} first registers an * item as being moved or swiped. * Implementations should update the item view to indicate * it's active state. */ void onItemSelected(); /** * Called when the {@link ItemTouchHelper} has completed the * move or swipe, and the active item state should be cleared. */ void onItemClear(); } 

Em seguida, no SimpleItemTouchHelperCallback implemente os métodos apropriados:


 @Override public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) { // We only want the active item if (actionState != ItemTouchHelper.ACTION_STATE_IDLE) { if (viewHolder instanceof ItemTouchHelperViewHolder) { ItemTouchHelperViewHolder itemViewHolder = (ItemTouchHelperViewHolder) viewHolder; itemViewHolder.onItemSelected(); } } super.onSelectedChanged(viewHolder, actionState); } @Override public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) { super.clearView(recyclerView, viewHolder); if (viewHolder instanceof ItemTouchHelperViewHolder) { ItemTouchHelperViewHolder itemViewHolder = (ItemTouchHelperViewHolder) viewHolder; itemViewHolder.onItemClear(); } } 

Agora, tudo o que resta é que o RecyclerListAdapter.ItemViewHolder implemente ItemTouchHelperViewHolder :


 public class ItemViewHolder extends RecyclerView.ViewHolder implements ItemTouchHelperViewHolder { // ... @Override public void onItemSelected() { itemView.setBackgroundColor(Color.LTGRAY); } @Override public void onItemClear() { itemView.setBackgroundColor(0); } } 

Neste exemplo, simplesmente adicionamos um plano de fundo cinza enquanto o elemento está ativo e o removemos. Se o ItemTouchHelper e o adaptador estiverem intimamente conectados, você poderá abandonar facilmente essa configuração e alternar o estado do componente de view diretamente em ItemTouchHelper.Callback .


Redes


Se agora você tentar usar o GridLayoutManager , verá que ele não funciona corretamente. O motivo e a solução são simples: precisamos informar ao ItemTouchHelper que queremos suportar o arrastamento de itens para a esquerda e para a direita. Anteriormente, em SimpleItemTouchHelperCallback , já especificamos:


 @Override public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) { int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN; int swipeFlags = ItemTouchHelper.START | ItemTouchHelper.END; return makeMovementFlags(dragFlags, swipeFlags); } 

A única alteração necessária para dar suporte às grades é adicionar os sinalizadores apropriados:


 int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN | ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT; 

No entanto, deslizar para dispensar não é um comportamento muito natural para elementos de grade, portanto, swipeFlags mais provável que os swipeFlags :


 @Override public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) { int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN | ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT; int swipeFlags = 0; return makeMovementFlags(dragFlags, swipeFlags); } 

Para ver um exemplo de GridLayoutManager funcionamento, consulte RecyclerGridFragment . Aqui está o que parece na inicialização:


Elementos de grade


Animações de furto personalizadas


ItemTouchHelper.Callback fornece uma maneira realmente conveniente de controlar totalmente a animação enquanto arrasta ou varre. Como o ItemTouchHelper é um RecyclerView.ItemDecoration , podemos intervir no processo de renderização de um componente de view maneira semelhante. Na próxima parte, examinaremos essa questão com mais detalhes, mas, por enquanto, vejamos um exemplo simples de substituição da animação de furto padrão para mostrar um desaparecimento linear.


 @Override public void onChildDraw(Canvas c, RecyclerView recyclerView, ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) { if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE) { float width = (float) viewHolder.itemView.getWidth(); float alpha = 1.0f - Math.abs(dX) / width; viewHolder.itemView.setAlpha(alpha); viewHolder.itemView.setTranslationX(dX); } else { super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive); } } 

Os dY dX e dY são o deslocamento atual em relação ao componente de view selecionado, em que:


  • -1.0f é um furto completo da direita para a esquerda (de ItemTouchHelper.END para ItemTouchHelper.START )
  • 1.0f é furto completo da esquerda para a direita (de ItemTouchHelper.START para ItemTouchHelper.END )

É importante chamar super para qualquer actionState que você não esteja processando para acionar a animação padrão.


Na próxima parte, consideraremos um exemplo no qual controlaremos o desenho de um elemento no momento do arrastar e soltar.


Conclusão


De fato, configurar o ItemTouchHelper é bastante divertido. Para não aumentar o volume deste artigo, dividi-o em vários.


Código fonte


Veja o código completo desta série de artigos nos repositórios Android-ItemTouchHelper-Demo GitHub. Este artigo aborda confirmações de ef8f149 a d164fba .


← Arraste e deslize o dedo no RecyclerView. Parte 1: ItemTouchHelper

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


All Articles