119

I am using recyclerview 22.2.0 and the helper class ItemTouchHelper.SimpleCallback to enable swipe-to-dismiss option to my list. But as I have a type of header on it, I need to disable the swipe behavior for the first position of the adapter. As RecyclerView.Adapter doesn't have a isEnabled() method, I tried to disable the view interaction through the methods isEnabled() and isFocusable() in the ViewHolder creation itself, but had no success. I tried to adjust the swipe threshold to a full value, like 0f ot 1f in the SimpleCallback's method getSwipeThreshold(), but no success too.

Some fragments of my code to help you to help me.

My Activity:

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

And I have a common adapter with two view types. In the ViewHolder that I want to disable swiping, I did:

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
  • 5,599
  • 6
  • 23
  • 33

7 Answers7

256

After playing a bit, I managed that SimpleCallback has a method called getSwipeDirs(). As I have a specific ViewHolder for the not swipable position, I can make use of instanceof to avoid the swipe. If that's not your case, you can perform this control using the position of ViewHolder in the Adapter.

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)
}
Zonker.in.Geneva
  • 1,389
  • 11
  • 19
Rafael Toledo
  • 5,599
  • 6
  • 23
  • 33
  • 2
    Exactly what I was looking for! Especially when this method does not appear on the [official documentation](http://developer.android.com/reference/android/support/v7/widget/helper/ItemTouchHelper.Callback.html)... – Stephen Vinouze Nov 10 '15 at 13:17
  • 1
    @ptitvinou it's present on `SimpleCallback` http://developer.android.com/intl/pt-br/reference/android/support/v7/widget/helper/ItemTouchHelper.SimpleCallback.html – Rafael Toledo Nov 10 '15 at 20:36
  • 4
    Worth mentioning: if You want just to allow for one direction on certain items then use `return position == 0 ? ItemTouchHelper.LEFT : super.getSwipeDirs(recyclerView, viewHolder);` - this one i.e. allow only for left swipes. – jean d'arme Nov 11 '16 at 16:46
  • Perfect. Working like a charm – King of Masses Mar 15 '18 at 12:05
  • How does this work? What do I do if I want to disable swiping right for example? – Ali Bdeir Apr 04 '18 at 19:08
  • You're the best. – Gabi Aug 21 '18 at 20:54
  • How to prevent swipe up to a specific level? Like I want to swipe a cell to left up to its center and then stop moving further left. How can I achieve that? – Bugs Happen Nov 08 '18 at 10:45
  • Thanks for this answer! I've added the Kotlin version. – Zonker.in.Geneva May 17 '20 at 22:27
  • @RafaelToledo please include jeand'arme's comment in your answer. It is really simple and was very helpful for me. – Anchith Acharya Aug 25 '21 at 07:24
72

If someone is using ItemTouchHelper.Callback. Then You can remove any related flags in getMovementFlags(..) function.

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

Here instead of dragFlags and swipeFlags You can pass 0 to disable corresponding feature.

ItemTouchHelper.START means swiping left to right in case of left to right locale (LTR application Support), but the other way around in a right to left locale (RTL application Support). ItemTouchHelper.END means swiping in the opposite direction of START.

so you can remove any flag according to your requirements.

Muhammad Adil
  • 4,358
  • 3
  • 32
  • 36
  • Thank !, I use code: @Override public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) { int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN; // movements drag return makeFlag( ItemTouchHelper.ACTION_STATE_DRAG , dragFlags); // as parameter, action drag and flags drag } – Do Xuan Nguyen Sep 10 '16 at 09:54
  • This is the preferred answer if (unlike OP) you are extending `ItemTouchHelper.Callback` directly. – tir38 Apr 12 '17 at 23:57
28

Here's a simple way to do this that only depends upon the position of the item being swiped:

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

This should also work if you're using 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);
}

If you want to disable swiping conditional upon the data in the item, use the position value to get data from the adapter for the item being swiped and disable accordingly.

If you already have specific holder types which need to not swipe, the accepted answer will work. However, creating holder types as a proxy for position is a kludge and should be avoided.

Bob Liberatore
  • 860
  • 10
  • 24
8

There are a few ways to go about this, but if you only have one ViewHolder, but more than one layout you can take this approach.

Override the getItemViewType and give it some logic as to determine view type based on position or type of data in object (I have a getType function in my object)

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

Return proper layout in onCreateView based on ViewType (Make sure to pass view type to ViewHolder class.

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

Get the content views of the different layouts based on view type public static class AppListItemHolder extends RecyclerView.ViewHolder {

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

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

And then in your ItemTouchHelper change actions based on ViewType. For me this disables swiping of a RecyclerView section header

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

First in recyclerView at onCreateViewHolder method, set tag for each viewHolder type, like below code:

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

then in ItemTouchHelper.Callback implementation, update getMovementFlags method, like below:

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

at end attach to recyclerView:

final SwipeController swipeController = new SwipeController();

    ItemTouchHelper itemTouchHelper = new ItemTouchHelper(swipeController);
    itemTouchHelper.attachToRecyclerView(recyclerView);
M.Kouchi
  • 800
  • 7
  • 12
3

In addition to the @Rafael Toledo answer, If you want to remove the specific swipe gesture like LEFT swipe or RIGHT swipe based on position or type of data in object in Adapter, then you can do like this.

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

Here in my case, In my Recyclerview I am loading the chat conversations and it have RIGHT (for ARCHIVE) & LEFT ( for EXIT) swipe gestures on each item. But in case if the conversation is not active, then I need to disable the RIGHT swipe for the specific item in the Recycler view.

So I am checking for the activeChat and accordingly and disabled the RIGHT swipe.

double-beep
  • 5,031
  • 17
  • 33
  • 41
King of Masses
  • 18,405
  • 4
  • 60
  • 77
  • this is the closest to my final solution combined with @M.Kouchi's tag approach. The only thing had to change was instead of returning ItemTouchHelper.RIGHT I returned ACTION_STATE_IDLE. – gmartinsnull Jun 18 '21 at 19:03
3

It can be disabled using 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
  • 2,285
  • 28
  • 22