Wyłącz funkcję Swipe dla pozycji w RecyclerView przy użyciu ItemTouchHelper.SimpleCallback

92

Używam recyclinglerview 22.2.0 i klasy pomocniczej ItemTouchHelper.SimpleCallback, aby włączyć opcję przeciągnij, aby odrzucić do mojej listy. Ale ponieważ mam na nim typ nagłówka, muszę wyłączyć zachowanie przesuwania dla pierwszej pozycji adaptera. Ponieważ RecyclerView.Adapter nie ma metody isEnabled () , próbowałem wyłączyć interakcję widoku za pomocą metod isEnabled () i isFocusable () w samym tworzeniu ViewHolder, ale bez powodzenia. Próbowałem ustawić próg przesunięcia na pełną wartość, na przykład 0f ot 1f w metodzie getSwipeThreshold () SimpleCallback , ale również bez sukcesu.

Kilka fragmentów mojego kodu, które pomogą Ci mi pomóc.

Moja aktywność:

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

Mam wspólny adapter z dwoma typami widoku. W ViewHolder, który chcę wyłączyć przesuwanie, zrobiłem:

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
źródło

Odpowiedzi:

204

Po krótkiej zabawie udało mi się, że SimpleCallback ma metodę o nazwie getSwipeDirs () . Ponieważ mam określony ViewHolder dla pozycji, której nie można przesuwać , mogę skorzystać z instanceof, aby uniknąć przesunięcia. Jeśli to nie Twój przypadek, możesz wykonać tę kontrolę, używając położenia ViewHolder w adapterze.

Jawa

@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
źródło
1
Dokładnie to, czego szukałem! Zwłaszcza, gdy ta metoda nie pojawia się w oficjalnej dokumentacji ...
ptitvinou
1
@ptitvinou jest obecny na SimpleCallback developer.android.com/intl/pt-br/reference/android/support/v7/…
Rafael Toledo,
2
Warto wspomnieć: jeśli chcesz tylko zezwolić na jeden kierunek na niektórych elementach, użyj return position == 0 ? ItemTouchHelper.LEFT : super.getSwipeDirs(recyclerView, viewHolder);- ten, tj. Zezwalaj tylko na przesunięcia w lewo.
jean d'arme
Idealny. Działa jak urok
King of Masses
Jak to działa? Co mam zrobić, jeśli chcę na przykład wyłączyć przesuwanie w prawo?
Ab
66

Jeśli ktoś używa ItemTouchHelper.Callback . Następnie możesz usunąć wszelkie powiązane flagi w funkcji 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);
}

Tutaj zamiast dragFlags i swipeFlags Możesz przekazać 0, aby wyłączyć odpowiednią funkcję.

ItemTouchHelper.START oznacza przesuwanie od lewej do prawej w przypadku ustawienia regionalnego od lewej do prawej (obsługa aplikacji LTR), ale na odwrót w ustawieniu od prawej do lewej (obsługa aplikacji RTL). ItemTouchHelper.END oznacza przesunięcie w kierunku przeciwnym do START.

więc możesz usunąć dowolną flagę zgodnie z własnymi wymaganiami.

Muhammad Adil
źródło
Dziękuję! Używam kodu: @Override public int getMovementFlags (RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN; // ruchy przeciągnij return makeFlag (ItemTouchHelper.ACTION_STATE_DRAG, dragFlags); // jako parametr, przeciągnij akcję i przeciągnij flagi}
Do Xuan Nguyen
Jest to preferowana odpowiedź, jeśli (w przeciwieństwie do OP) przedłużasz ItemTouchHelper.Callbackbezpośrednio.
tir38
23

Oto prosty sposób na zrobienie tego, który zależy tylko od pozycji przesuwanego elementu:

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

Powinno to również działać, jeśli używasz 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);
}

Jeśli chcesz wyłączyć przesuwanie w zależności od danych w elemencie, użyj positionwartości, aby pobrać dane z adaptera dla elementu, który ma być przesuwany, i wyłącz odpowiednio.

Jeśli masz już określone typy uchwytów, których nie trzeba przesuwać, zaakceptowana odpowiedź będzie działać. Jednak tworzenie typów posiadaczy jako zastępczych dla pozycji jest niewygodne i należy tego unikać.

Robert Liberatore
źródło
6

Jest na to kilka sposobów, ale jeśli masz tylko jeden ViewHolder, ale więcej niż jeden układ, możesz zastosować to podejście.

Zastąp getItemViewType i nadaj mu logikę, aby określić typ widoku na podstawie pozycji lub typu danych w obiekcie (mam funkcję getType w moim obiekcie)

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

Zwróć właściwy układ w onCreateView na podstawie ViewType (upewnij się, że typ widoku został przekazany do klasy 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);
    }
}

Uzyskaj widoki zawartości różnych układów na podstawie typu widoku publiczna klasa statyczna AppListItemHolder rozszerza RecyclerView.ViewHolder {

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

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

A następnie w akcjach zmiany ItemTouchHelper na podstawie ViewType. Dla mnie to wyłącza przesuwanie nagłówka sekcji RecyclerView

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

źródło
Fajny pomysł ale getItemViewType jest ostateczna
tim
3

Najpierw w recyclinglerView w metodzie onCreateViewHolder, ustaw tag dla każdego typu viewHolder, jak poniżej:

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

następnie w implementacji ItemTouchHelper.Callback zaktualizuj metodę getMovementFlags, jak poniżej:

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

na końcu dołącz do recyclinglerView:

final SwipeController swipeController = new SwipeController();

    ItemTouchHelper itemTouchHelper = new ItemTouchHelper(swipeController);
    itemTouchHelper.attachToRecyclerView(recyclerView);
M.Kouchi
źródło
2

Oprócz odpowiedzi @Rafael Toledo, jeśli chcesz usunąć określony gest machnięcia, taki jak przesunięcie w LEWO lub przesunięcie w PRAWO, na podstawie pozycji lub typu danych w obiekcie w adapterze, możesz to zrobić.

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

Tutaj, w moim przypadku, w moim Recyclerview ładuję rozmowy na czacie i mam gesty przesuwania w PRAWO (dla ARCHIWUM) i W LEWO (dla WYJŚCIA) na każdym elemencie. Ale w przypadku, gdy rozmowa nie jest aktywna, muszę wyłączyć przesuwanie w PRAWO dla określonego elementu w widoku Recycler.

Więc sprawdzam activeChati odpowiednio i wyłączam przesunięcie w PRAWO.

Król Mszy
źródło
2

Można go wyłączyć za pomocą 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
źródło