Désactivez le balayage pour la position dans RecyclerView à l'aide de ItemTouchHelper.SimpleCallback

92

J'utilise recyclerview 22.2.0 et la classe d'assistance ItemTouchHelper.SimpleCallback pour activer l' option glisser pour rejeter dans ma liste. Mais comme j'ai un type d'en-tête dessus, je dois désactiver le comportement de balayage pour la première position de l'adaptateur. Comme RecyclerView.Adapter n'a pas de méthode isEnabled () , j'ai essayé de désactiver l'interaction de vue via les méthodes isEnabled () et isFocusable () dans la création de ViewHolder elle-même, mais sans succès. J'ai essayé d'ajuster le seuil de balayage à une valeur complète, comme 0f ot 1f dans la méthode getSwipeThreshold () de SimpleCallback , mais sans succès aussi.

Quelques fragments de mon code pour vous aider à m'aider.

Mon activité:

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

Et j'ai un adaptateur commun avec deux types de vue. Dans le ViewHolder que je souhaite désactiver le balayage, j'ai fait:

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
la source

Réponses:

204

Après avoir joué un peu, j'ai réussi à ce que SimpleCallback ait une méthode appelée getSwipeDirs () . Comme j'ai un ViewHolder spécifique pour la position non glissante , je peux utiliser instanceof pour éviter le balayage. Si ce n'est pas votre cas, vous pouvez effectuer ce contrôle en utilisant la position de ViewHolder dans l'adaptateur.

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
la source
1
Exactement ce que je cherchais! Surtout quand cette méthode n'apparaît pas sur la documentation officielle ...
ptitvinou
1
@ptitvinou il est présent sur SimpleCallback developer.android.com/intl/pt-br/reference/android/support/v7/…
Rafael Toledo
2
Il convient de mentionner: si vous souhaitez simplement autoriser une direction sur certains éléments, utilisez return position == 0 ? ItemTouchHelper.LEFT : super.getSwipeDirs(recyclerView, viewHolder);- celui-ci, c'est-à-dire ne permettre que les balayages vers la gauche.
jean d'arme
Parfait. Travailler comme un charme
King of Masses
Comment cela marche-t-il? Que dois-je faire si je souhaite désactiver le balayage vers la droite par exemple?
Ab
66

Si quelqu'un utilise ItemTouchHelper.Callback . Ensuite, vous pouvez supprimer tous les indicateurs associés dans la fonction 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);
}

Ici au lieu de dragFlags et swipeFlags Vous pouvez passer 0 pour désactiver la fonction correspondante.

ItemTouchHelper.START signifie glisser de gauche à droite en cas de paramètres régionaux de gauche à droite (prise en charge des applications LTR), mais l'inverse dans les paramètres régionaux de droite à gauche (prise en charge des applications RTL). ItemTouchHelper.END signifie glisser dans la direction opposée de START.

afin que vous puissiez supprimer n'importe quel drapeau selon vos besoins.

Muhammad Adil
la source
Merci!, J'utilise le code: @Override public int getMovementFlags (RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN; // mouvements drag return makeFlag (ItemTouchHelper.ACTION_STATE_DRAG, dragFlags); // en tant que paramètre, action glisser et drapeaux glisser}
Do Xuan Nguyen
C'est la réponse préférée si (contrairement à OP) vous étendez ItemTouchHelper.Callbackdirectement.
tir38
23

Voici un moyen simple de le faire qui ne dépend que de la position de l'élément balayé:

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

Cela devrait également fonctionner si vous utilisez 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 vous souhaitez désactiver le balayage en fonction des données de l'élément, utilisez la positionvaleur pour obtenir des données de l'adaptateur pour l'élément en cours de balayage et désactivez-le en conséquence.

Si vous avez déjà des types de détenteurs spécifiques qui ne doivent pas glisser, la réponse acceptée fonctionnera. Cependant, la création de types de détenteurs comme proxy pour la position est un kludge et doit être évitée.

Robert Liberatore
la source
6

Il y a plusieurs façons de procéder, mais si vous n'avez qu'un seul ViewHolder, mais plusieurs mises en page, vous pouvez adopter cette approche.

Remplacez le getItemViewType et donnez-lui une certaine logique pour déterminer le type de vue en fonction de la position ou du type de données dans l'objet (j'ai une fonction getType dans mon objet)

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

Renvoyez la mise en page appropriée dans onCreateView en fonction de ViewType (assurez-vous de transmettre le type de vue à la classe 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);
    }
}

Obtenez les vues de contenu des différentes mises en page en fonction du type de vue classe statique publique AppListItemHolder étend RecyclerView.ViewHolder {

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

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

Et puis dans votre ItemTouchHelper, changez les actions basées sur ViewType. Pour moi, cela désactive le balayage d'un en-tête de section RecyclerView

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

la source
Bonne idée mais getItemViewType est définitif
tim
3

Tout d'abord dans la méthode recyclerView at onCreateViewHolder, définissez une balise pour chaque type de viewHolder, comme le code ci-dessous:

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

puis dans l'implémentation ItemTouchHelper.Callback, mettez à jour la méthode getMovementFlags, comme ci-dessous:

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

à la fin attacher à recyclerView:

final SwipeController swipeController = new SwipeController();

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

En plus de la réponse @Rafael Toledo, si vous souhaitez supprimer le geste de balayage spécifique comme le balayage GAUCHE ou le balayage DROIT en fonction de la position ou du type de données dans l'objet dans Adapter, vous pouvez faire comme ceci.

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

Ici, dans mon cas, dans mon Recyclerview, je charge les conversations de chat et il y a des gestes de balayage DROIT (pour ARCHIVE) et GAUCHE (pour EXIT) sur chaque élément. Mais au cas où la conversation n'est pas active, je dois désactiver le balayage DROIT pour l'élément spécifique dans la vue Recycleur.

Je vérifie donc le activeChatet en conséquence et j'ai désactivé le balayage DROIT.

Roi des messes
la source
2

Il peut être désactivé en utilisant 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
la source