Deshabilite Swipe para la posición en RecyclerView usando ItemTouchHelper.SimpleCallback

92

Estoy usando recyclerview 22.2.0 y la clase auxiliar ItemTouchHelper.SimpleCallback para habilitar la opción de deslizar para descartar en mi lista. Pero como tengo un tipo de encabezado, necesito desactivar el comportamiento de deslizamiento para la primera posición del adaptador. Como RecyclerView.Adapter no tiene un método isEnabled () , intenté deshabilitar la interacción de la vista a través de los métodos isEnabled () e isFocusable () en la creación de ViewHolder, pero no tuve éxito. Intenté ajustar el umbral de deslizamiento a un valor completo, como 0f ot 1f en el método getSwipeThreshold () de SimpleCallback , pero tampoco tuve éxito.

Algunos fragmentos de mi código para ayudarte a ayudarme.

Mi actividad:

@Override
protected void onCreate(Bundle bundle) {
    //... initialization
    ItemTouchHelper.SimpleCallback simpleItemTouchCallback = new ItemTouchHelper.SimpleCallback(0,
            ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT) {

        @Override
        public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder,
                          RecyclerView.ViewHolder target) {
            return false;
        }

        @Override
        public float getSwipeThreshold(RecyclerView.ViewHolder viewHolder) {
            if (viewHolder instanceof CartAdapter.MyViewHolder) return 1f;
            return super.getSwipeThreshold(viewHolder);
        }

        @Override
        public void onSwiped(RecyclerView.ViewHolder viewHolder, int swipeDir) {

        }
    };

    ItemTouchHelper itemTouchHelper = new ItemTouchHelper(simpleItemTouchCallback);
    itemTouchHelper.attachToRecyclerView(recyclerView);
}

Y tengo un adaptador común con dos tipos de vista. En el ViewHolder que quiero deshabilitar el deslizamiento, lo hice:

public static class MyViewHolder extends RecyclerView.ViewHolder {
    public ViewGroup mContainer;

    public MyViewHolder(View v) {
        super(v);
        v.setFocusable(false);
        v.setEnabled(false);
        mContainer = (ViewGroup) v.findViewById(R.id.container);      
    }
}
Rafael Toledo
fuente

Respuestas:

204

Después de jugar un poco, logré que SimpleCallback tenga un método llamado getSwipeDirs () . Como tengo un ViewHolder específico para la posición no deslizable , puedo hacer uso de instanceof para evitar el deslizamiento. Si ese no es su caso, puede realizar este control usando la posición de ViewHolder en el Adaptador.

Java

@Override
public int getSwipeDirs(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
    if (viewHolder instanceof CartAdapter.MyViewHolder) return 0;
    return super.getSwipeDirs(recyclerView, viewHolder);
}

Kotlin

override fun getSwipeDirs (recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder): Int {
    if (viewHolder is CartAdapter.MyViewHolder) return 0
    return super.getSwipeDirs(recyclerView, viewHolder)
}
Rafael Toledo
fuente
1
¡Exactamente lo que estaba buscando! Especialmente cuando este método no aparece en la documentación oficial ...
ptitvinou
1
@ptitvinou está presente en SimpleCallback developer.android.com/intl/pt-br/reference/android/support/v7/…
Rafael Toledo
2
Vale la pena mencionar: si solo desea permitir una dirección en ciertos elementos, utilice return position == 0 ? ItemTouchHelper.LEFT : super.getSwipeDirs(recyclerView, viewHolder);esta opción, es decir, permitir solo deslizamientos hacia la izquierda.
jean d'arme
Perfecto. Trabajando como un encanto
King of Masses
¿Como funciona esto? ¿Qué hago si quiero deshabilitar el deslizamiento hacia la derecha, por ejemplo?
Ab
66

Si alguien está usando ItemTouchHelper.Callback . Luego, puede eliminar cualquier indicador relacionado en la función getMovementFlags (..) .

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

Aquí, en lugar de dragFlags y swipeFlags , puede pasar 0 para desactivar la función correspondiente.

ItemTouchHelper.START significa deslizar el dedo de izquierda a derecha en el caso de una configuración regional de izquierda a derecha (compatibilidad con aplicaciones LTR), pero al revés en una configuración regional de derecha a izquierda (compatibilidad con aplicaciones RTL). ItemTouchHelper.END significa deslizar en la dirección opuesta a START.

para que pueda eliminar cualquier bandera según sus requisitos.

Muhammad Adil
fuente
¡Gracias !, uso el código: @Override public int getMovementFlags (RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN; // movimientos arrastrar volver makeFlag (ItemTouchHelper.ACTION_STATE_DRAG, dragFlags); // como parámetro, arrastre de acción y arrastre de banderas}
Do Xuan Nguyen
Esta es la respuesta preferida si (a diferencia de OP) se está extendiendo ItemTouchHelper.Callbackdirectamente.
tir38
23

Aquí hay una forma sencilla de hacer esto que solo depende de la posición del elemento que se desliza:

@Override
public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder holder) {
    int position = holder.getAdapterPosition();
    int dragFlags = 0; // whatever your dragFlags need to be
    int swipeFlags = createSwipeFlags(position)

    return makeMovementFlags(dragFlags, swipeFlags);
}

private int createSwipeFlags(int position) {
  return position == 0 ? 0 : ItemTouchHelper.START | ItemTouchHelper.END;
}

Esto también debería funcionar si estás usando SimpleCallback:

@Override
public int getSwipeDirs(RecyclerView recyclerView, RecyclerView.ViewHolder holder) {
    int position = holder.getAdapterPosition();
    return createSwipeFlags(position);
}

private int createSwipeFlags(int position) {
  return position == 0 ? 0 : super.getSwipeDirs(recyclerView, viewHolder);
}

Si desea deshabilitar el deslizamiento condicional a los datos del elemento, use el positionvalor para obtener datos del adaptador para el elemento que se desliza y deshabilite en consecuencia.

Si ya tiene tipos de titulares específicos que no necesitan deslizar, la respuesta aceptada funcionará. Sin embargo, la creación de tipos de titulares como sustitutos de la posición es una tontería y debe evitarse.

Robert Liberatore
fuente
6

Hay algunas formas de hacerlo, pero si solo tiene un ViewHolder, pero más de un diseño, puede tomar este enfoque.

Anule getItemViewType y déle algo de lógica para determinar el tipo de vista según la posición o el tipo de datos en el objeto (tengo una función getType en mi objeto)

@Override
public int getItemViewType(int position) {
    return data.get(position).getType;
}

Devuelva el diseño adecuado en onCreateView basado en ViewType (asegúrese de pasar el tipo de vista a la clase ViewHolder.

@Override
public AppListItemHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    mContext = parent.getContext();

    if (viewType == 0){
        return new AppListItemHolder(LayoutInflater.from(mContext).inflate(R.layout.layout, parent, false), viewType);
    else
        return new AppListItemHolder(LayoutInflater.from(mContext).inflate(R.layout.header, parent, false), viewType);
    }
}

Obtenga las vistas de contenido de los diferentes diseños en función del tipo de vista public static class AppListItemHolder extiende RecyclerView.ViewHolder {

    public AppListItemHolder (View v, int viewType) {
        super(v);

        if (viewType == 0)
            ... get your views contents
        else
            ... get other views contents
        }
    }
}

Y luego, en su ItemTouchHelper, cambie las acciones basadas en ViewType. Para mí, esto deshabilita el deslizamiento de un encabezado de sección RecyclerView

@Override
public int getSwipeDirs(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
    if (viewHolder.getItemViewType() == 1) return 0;
        return super.getSwipeDirs(recyclerView, viewHolder);
    }
}

fuente
Buena idea, pero getItemViewType es final
tim
3

Primero en el método recyclerView en onCreateViewHolder, configure la etiqueta para cada tipo de viewHolder, como el siguiente código:

@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
    if(ITEM_TYPE_NORMAL == viewType) {
        View v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.deposite_card_view, viewGroup, false);
        ItemViewHolder holder = new ItemViewHolder(context, v);
        holder.itemView.setTag("normal");
        return holder;
    } else {
        View v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.card_header, viewGroup, false);
        HeaderViewHolder holder = new HeaderViewHolder(context, v);
        holder.itemView.setTag("header");
        return holder;
    }
} 

luego, en la implementación ItemTouchHelper.Callback, actualice el método getMovementFlags, como se muestra a continuación:

public class SwipeController extends ItemTouchHelper.Callback {

@Override
public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
    if("normal".equalsIgnoreCase((String) viewHolder.itemView.getTag())) {
        return makeMovementFlags(0, LEFT | RIGHT);
    } else {
        return 0;
    }
}

al final adjuntar a recyclingView:

final SwipeController swipeController = new SwipeController();

    ItemTouchHelper itemTouchHelper = new ItemTouchHelper(swipeController);
    itemTouchHelper.attachToRecyclerView(recyclerView);
M.Kouchi
fuente
2

Además de la respuesta de @Rafael Toledo, si desea eliminar el gesto de deslizamiento específico como deslizar hacia la IZQUIERDA o deslizar hacia la DERECHA en función de la posición o el tipo de datos en el objeto en el Adaptador, puede hacerlo así.

@Override
  public int getSwipeDirs(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder)
       {
          int position = viewHolder.getAdapterPosition();
          ConversationDataModel conversationModel = null;
          conversationModel = mHomeAdapter.getItems().get(position);
          boolean activeChat = true;
          if(conversationModel!=null && !conversationModel.isActive())
           {
             activeChat = false;
           }
  return activeChat ? super.getSwipeDirs(recyclerView, viewHolder): ItemTouchHelper.RIGHT;
        }

Aquí, en mi caso, en mi Recyclerview estoy cargando las conversaciones de chat y tiene gestos de deslizamiento DERECHA (para ARCHIVO) e IZQUIERDA (para SALIDA) en cada elemento. Pero en caso de que la conversación no esté activa, entonces necesito deshabilitar el deslizamiento DERECHO para el elemento específico en la vista Recycler.

Así que estoy verificando el activeChaty, en consecuencia, y deshabilito el deslizamiento DERECHO.

Rey de misas
fuente
2

Puede desactivarse usando ItemTouchHelper.ACTION_STATE_IDLE

ItemTouchHelper.SimpleCallback(
    ItemTouchHelper.UP or ItemTouchHelper.DOWN, //drag directions
    ItemTouchHelper.ACTION_STATE_IDLE //swipe directions
)
val touchHelperCallback = object : ItemTouchHelper.SimpleCallback(ItemTouchHelper.UP or ItemTouchHelper.DOWN, ItemTouchHelper.ACTION_STATE_IDLE) {
    override fun onMove(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder, target: RecyclerView.ViewHolder): Boolean {
        (recyclerView.adapter as MyAdapter).onItemMove(viewHolder.adapterPosition, target.adapterPosition)
        return true
    }

    override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
        return
    }

}
val touchHelper = ItemTouchHelper(touchHelperCallback)
touchHelper.attachToRecyclerView(recyclerViewX)
Pedro Romão
fuente