Deaktivieren Sie Swipe für die Position in RecyclerView mit ItemTouchHelper.SimpleCallback

84

Ich verwende recyclerview 22.2.0 und die Hilfsklasse ItemTouchHelper.SimpleCallback zu ermöglichen Swipe-to-abtun Option meiner Liste. Da ich jedoch eine Art Header habe, muss ich das Wischverhalten für die erste Position des Adapters deaktivieren. Da RecyclerView.Adapter keine isEnabled () -Methode hat, habe ich versucht, die Ansichtsinteraktion über die Methoden isEnabled () und isFocusable () in der ViewHolder-Erstellung selbst zu deaktivieren , hatte jedoch keinen Erfolg. Ich versuchte , die Swipe - Schwelle auf einen vollen Wert, wie anzupassen 0f ot 1f in dem SimpleCallback Methode getSwipeThreshold () , aber ohne Erfolg zu.

Einige Fragmente meines Codes sollen Ihnen helfen, mir zu helfen.

Meine Aktivität:

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

Und ich habe einen gemeinsamen Adapter mit zwei Ansichtstypen. In dem ViewHolder , in dem ich das Wischen deaktivieren möchte, habe ich Folgendes getan:

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
quelle

Antworten:

190

Nachdem ich ein bisschen gespielt hatte, gelang es mir, dass SimpleCallback eine Methode namens getSwipeDirs () hat . Da ich einen bestimmten ViewHolder für die nicht wischbare Position habe, kann ich instanceof verwenden , um das Wischen zu vermeiden. Wenn dies nicht der Fall ist, können Sie dieses Steuerelement über die Position von ViewHolder im Adapter ausführen.

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
quelle
1
Genau das, wonach ich gesucht habe! Besonders wenn diese Methode nicht in der offiziellen Dokumentation erscheint ...
ptitvinou
1
@ptitvinou es ist auf SimpleCallback developer.android.com/intl/pt-br/reference/android/support/v7/… vorhanden
Rafael Toledo
2
Erwähnenswert: Wenn Sie nur eine Richtung für bestimmte Elemente zulassen möchten, verwenden Sie return position == 0 ? ItemTouchHelper.LEFT : super.getSwipeDirs(recyclerView, viewHolder);diese - dh nur für linke Wischbewegungen.
Jean d'arme
Perfekt. Arbeiten wie ein Zauber
König der Massen
Wie funktioniert das? Was mache ich, wenn ich beispielsweise das Wischen nach rechts deaktivieren möchte?
Ab
66

Wenn jemand ItemTouchHelper.Callback verwendet . Anschließend können Sie alle zugehörigen Flags in der Funktion getMovementFlags (..) entfernen .

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

Hier können Sie anstelle von dragFlags und swipeFlags 0 übergeben, um die entsprechende Funktion zu deaktivieren.

ItemTouchHelper.START bedeutet, dass bei einem Gebietsschema von links nach rechts (Unterstützung für LTR-Anwendungen) von links nach rechts gewischt wird, bei einem Gebietsschema von rechts nach links umgekehrt (Unterstützung für RTL-Anwendungen). ItemTouchHelper.END bedeutet Wischen in die entgegengesetzte Richtung von START.

So können Sie jede Flagge entsprechend Ihren Anforderungen entfernen.

Muhammad Adil
quelle
Vielen Dank! Ich verwende den folgenden Code: @Override public int getMovementFlags (RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN; // Bewegungen ziehen return makeFlag (ItemTouchHelper.ACTION_STATE_DRAG, dragFlags); // als Parameter, Aktion ziehen und Flags ziehen}
Do Xuan Nguyen
Dies ist die bevorzugte Antwort, wenn Sie (im Gegensatz zu OP) ItemTouchHelper.Callbackdirekt erweitern.
Tir38
23

Hier ist eine einfache Möglichkeit, die nur von der Position des zu wischenden Elements abhängt:

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

Dies sollte auch funktionieren, wenn Sie Folgendes verwenden 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);
}

Wenn Sie das Wischen abhängig von den Daten im Element deaktivieren möchten, verwenden Sie den positionWert, um Daten vom Adapter für das zu wischende Element abzurufen, und deaktivieren Sie es entsprechend.

Wenn Sie bereits bestimmte Haltertypen haben, die nicht gewischt werden müssen, funktioniert die akzeptierte Antwort. Das Erstellen von Haltertypen als Stellvertreter für die Position ist jedoch ein Problem und sollte vermieden werden.

Robert Liberatore
quelle
6

Es gibt einige Möglichkeiten, dies zu tun. Wenn Sie jedoch nur einen ViewHolder, aber mehr als ein Layout haben, können Sie diesen Ansatz wählen.

Überschreiben Sie den getItemViewType und geben Sie ihm eine Logik, um den Ansichtstyp basierend auf der Position oder dem Datentyp im Objekt zu bestimmen (ich habe eine getType-Funktion in meinem Objekt).

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

Geben Sie das richtige Layout in onCreateView basierend auf ViewType zurück (Stellen Sie sicher, dass Sie den Ansichtstyp an die ViewHolder-Klasse übergeben.

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

Abrufen der Inhaltsansichten der verschiedenen Layouts basierend auf dem Ansichtstyp öffentliche statische Klasse AppListItemHolder erweitert RecyclerView.ViewHolder {

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

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

Ändern Sie dann in Ihrem ItemTouchHelper die Aktionen basierend auf ViewType. Für mich deaktiviert dies das Wischen eines RecyclerView-Abschnittsheaders

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

quelle
Gute Idee, aber getItemViewType ist endgültig
Tim
3

Legen Sie zunächst in recyclerView bei der onCreateViewHolder-Methode das Tag für jeden viewHolder-Typ fest, wie im folgenden Code angegeben:

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

Aktualisieren Sie dann in der Implementierung von ItemTouchHelper.Callback die Methode getMovementFlags wie folgt:

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

am Ende an recyclerView anhängen:

final SwipeController swipeController = new SwipeController();

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

Zusätzlich zur Antwort von @Rafael Toledo: Wenn Sie die spezifische Wischgeste wie LINKS oder RECHTS basierend auf der Position oder dem Datentyp im Objekt im Adapter entfernen möchten, können Sie dies tun.

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

Hier in meinem Fall lade ich in meinem Recyclerview die Chat-Konversationen und es gibt RECHTS- (für ARCHIV) und LINKS- (für EXIT) Wischgesten für jedes Element. Falls die Konversation jedoch nicht aktiv ist, muss ich das RECHTE Wischen für das bestimmte Element in der Recycler-Ansicht deaktivieren.

Also überprüfe ich das activeChatund dementsprechend und deaktiviere das RECHTE Wischen.

König der Massen
quelle
2

Es kann mit deaktiviert werden 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
quelle