Arrastra y desliza en RecyclerView. Parte 2: arrastrar y soltar controladores, cuadrículas y animaciones personalizadas

Arrastra y desliza en RecyclerView. Parte 2: arrastrar y soltar controladores, cuadrículas y animaciones personalizadas


En la primera parte, analizamos ItemTouchHelper y la implementación de ItemTouchHelper.Callback , que agrega las funciones básicas de arrastrar y soltar y deslizar para descartar a RecyclerView . En este artículo, continuaremos lo que se hizo en el anterior agregando soporte para organizar elementos en forma de cuadrícula, controladores de arrastrar y soltar, selección de un elemento de la lista y animaciones de deslizamiento personalizadas.


Malla de elementos


Arrastrar y soltar controladores


Al crear una lista que admite arrastrar y soltar , generalmente implementan la capacidad de arrastrar y soltar elementos con el tacto. Esto contribuye a la claridad y facilidad de uso de la lista en el "modo de edición", y también lo recomiendan las pautas de materiales. Agregar controladores de arrastrar y soltar a nuestro ejemplo es increíblemente fácil.


Arrastrar y soltar controladores


Primero, actualice el 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> 

La imagen utilizada para el controlador de arrastre se puede encontrar en los iconos de Diseño de materiales y agregar al proyecto utilizando el conveniente complemento generador de iconos en Android Studio.


Como se mencionó brevemente en un artículo anterior, puede usar ItemTouchHelper.startDrag(ViewHolder) para iniciar mediante programación arrastrar y soltar. Entonces, todo lo que tenemos que hacer es actualizar ViewHolder agregando un controlador de arrastre y configurar un controlador táctil simple que llamará a startDrag() .


Necesitaremos una interfaz para transmitir eventos a lo largo de la cadena:


 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); } 

Luego defina un ImageView para el controlador de arrastre en ItemViewHolder :


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

y actualice el 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 ahora debería verse así.


Todo lo que queda por hacer es agregar OnStartDragListener al 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 ahora debería verse así . Ahora, cuando inicie la aplicación, puede comenzar a arrastrar y soltar tocando el controlador.


Arrastrando elementos de la lista


Destacar un elemento de la lista


Ahora, en nuestro ejemplo, no hay indicación visual del elemento que se está arrastrando. Obviamente, esto no debería ser así, pero es fácil de solucionar. Con ItemTouchHelper puede usar efectos de resaltado de elementos estándar. En Lollipop y versiones posteriores de Android, la luz de fondo "se extiende" sobre el elemento en el proceso de interacción con él; En versiones anteriores, el elemento simplemente cambia su color a oscuro.


Para implementar esto en nuestro ejemplo, simplemente agregue un fondo (propiedad de background ) al FrameLayout raíz FrameLayout elemento FrameLayout o FrameLayout en el constructor de RecyclerListAdapter.ItemViewHolder . Se verá más o menos así:


Selección de elemento


Se ve genial, pero es posible que desee controlar aún más. Una forma de hacerlo es permitir que ViewHolder maneje los cambios de estado de un elemento. Para esto, ItemTouchHelper.Callback proporciona dos métodos más:


  • onSelectedChanged(ViewHolder, int) se llama cada vez que el estado de un elemento cambia a arrastrar ( ACTION_STATE_DRAG ) o deslizar ( ACTION_STATE_SWIPE ). Este es un lugar ideal para cambiar el estado del componente de view a activo .
  • clearView(RecyclerView, ViewHolder) cuando se completa el componente de view arrastrar y soltar, y también cuando se completa el deslizamiento ( ACTION_STATE_IDLE ). Aquí, el estado inicial de su componente de view generalmente se restaura.

Ahora, pongamos todo junto.


Primero, cree una interfaz que 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(); } 

Luego, en SimpleItemTouchHelperCallback implemente los métodos apropiados:


 @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(); } } 

Ahora todo lo que queda es que 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); } } 

En este ejemplo, simplemente agregamos un fondo gris mientras el elemento está activo y luego lo eliminamos. Si su ItemTouchHelper y el adaptador están estrechamente conectados, puede abandonar fácilmente esta configuración y cambiar el estado del componente de view directamente en ItemTouchHelper.Callback .


Redes


Si ahora intenta utilizar el GridLayoutManager , verá que no funciona correctamente. La razón y la solución son simples: debemos decirle a nuestro ItemTouchHelper que queremos admitir arrastrar elementos de izquierda a derecha. Anteriormente en SimpleItemTouchHelperCallback ya 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); } 

El único cambio necesario para soportar las cuadrículas es agregar las banderas apropiadas:


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

Sin embargo, deslizar para descartar no es un comportamiento muy natural para los elementos de la cuadrícula, por swipeFlags muy probable que las 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 un ejemplo de GridLayoutManager funciona, vea RecyclerGridFragment . Así es como se ve al inicio:


Elementos de cuadrícula


Animaciones de deslizamiento personalizadas


ItemTouchHelper.Callback proporciona una forma realmente conveniente de controlar completamente la animación mientras se arrastra o barre. Dado que ItemTouchHelper es un RecyclerView.ItemDecoration , podemos intervenir en el proceso de renderizar un componente de view manera similar. En la siguiente parte, examinaremos esta pregunta con más detalle, pero por ahora, veamos un ejemplo simple de anular la animación de deslizamiento predeterminada para mostrar una desaparición lineal.


 @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); } } 

Los dY dX y dY son el cambio actual relativo al componente de view seleccionado, donde:


  • -1.0f es un deslizamiento completo de derecha a izquierda (de ItemTouchHelper.END a ItemTouchHelper.START )
  • 1.0f se desliza por completo de izquierda a derecha (desde ItemTouchHelper.START a ItemTouchHelper.END )

Es importante llamar a super para cualquier actionState que no esté procesando para activar la animación predeterminada.


En la siguiente parte, consideraremos un ejemplo en el que controlaremos el dibujo de un elemento en el momento de arrastrar y soltar.


Conclusión


De hecho, configurar ItemTouchHelper es bastante divertido. Para no aumentar el volumen de este artículo, lo dividí en varios.


Código fuente


Consulte el código completo de esta serie de artículos en los repositorios de Android-ItemTouchHelper-Demo GitHub. Este artículo cubre las confirmaciones de ef8f149 a d164fba .


← Arrastra y desliza en RecyclerView. Parte 1: ItemTouchHelper

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


All Articles