Отключить смахивание для позиции в RecyclerView с помощью ItemTouchHelper.SimpleCallback

92

Я использую recyclerview 22.2.0 и вспомогательный класс ItemTouchHelper.SimpleCallback, чтобы включить в свой список опцию « смахнуть для увольнения» . Но поскольку у меня есть тип заголовка, мне нужно отключить поведение смахивания для первой позиции адаптера. Поскольку RecyclerView.Adapter не имеет метода isEnabled () , я попытался отключить взаимодействие с представлением с помощью методов isEnabled () и isFocusable () в самом создании ViewHolder, но безуспешно. Я попытался настроить порог смахивания на полное значение, например 0f ot 1f в методе SimpleCallback getSwipeThreshold () , но безуспешно.

Некоторые фрагменты моего кода помогут вам помочь мне.

Моя деятельность:

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

А у меня есть общий адаптер с двумя видами просмотра. В ViewHolder, который я хочу отключить смахивание, я сделал:

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);      
    }
}
Рафаэль Толедо
источник

Ответы:

204

Немного поиграв, я понял, что SimpleCallback имеет метод getSwipeDirs () . Поскольку у меня есть специальный ViewHolder для позиции, которую нельзя прокрутить , я могу использовать instanceof, чтобы избежать прокрутки. Если это не ваш случай, вы можете выполнить этот элемент управления, используя позицию ViewHolder в адаптере.

Ява

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

Котлин

override fun getSwipeDirs (recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder): Int {
    if (viewHolder is CartAdapter.MyViewHolder) return 0
    return super.getSwipeDirs(recyclerView, viewHolder)
}
Рафаэль Толедо
источник
1
Именно то, что я искал! Особенно, когда этот метод не
указан
1
@ptitvinou, он присутствует на SimpleCallback developer.android.com/intl/pt-br/reference/android/support/v7/…
Рафаэль Толедо
2
Стоит упомянуть: если вы хотите разрешить только одно направление для определенных элементов, используйте return position == 0 ? ItemTouchHelper.LEFT : super.getSwipeDirs(recyclerView, viewHolder);- это, то есть разрешите только смахивание влево.
jean d'arme
Отлично. Работает как шарм
Король масс
Как это работает? Что мне делать, если я хочу, например, отключить смахивание вправо?
Ab
66

Если кто-то использует ItemTouchHelper.Callback . Затем вы можете удалить все связанные флаги в функции 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);
}

Здесь вместо dragFlags и swipeFlags вы можете передать 0, чтобы отключить соответствующую функцию.

ItemTouchHelper.START означает смахивание слева направо в случае языкового стандарта слева направо (поддержка приложения LTR), но наоборот, в языковом стандарте справа налево (поддержка приложения RTL). ItemTouchHelper.END означает смахивание в направлении, противоположном направлению START.

так что вы можете удалить любой флаг в соответствии с вашими требованиями.

Мухаммад Адиль
источник
Спасибо! Я использую код: @Override public int getMovementFlags (RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN; // перемещение перетаскивания return makeFlag (ItemTouchHelper.ACTION_STATE_DRAG, dragFlags); // как параметр, перетаскивание действия и перетаскивание флагов}
Do Xuan Nguyen
Это предпочтительный ответ, если (в отличие от OP) вы расширяетесь ItemTouchHelper.Callbackнапрямую.
tir38
23

Вот простой способ сделать это, который зависит только от положения перемещаемого элемента:

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

Это также должно работать, если вы используете 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);
}

Если вы хотите отключить прокрутку в зависимости от данных в элементе, используйте positionзначение для получения данных от адаптера для считываемого элемента и соответствующим образом отключите.

Если у вас уже есть определенные типы держателей, которые не нужно проводить пальцем, принятый ответ будет работать. Однако создание типов держателей в качестве прокси для позиции - это путаница, и ее следует избегать.

Роберт Либераторе
источник
6

Есть несколько способов сделать это, но если у вас есть только один ViewHolder, но более одного макета, вы можете использовать этот подход.

Переопределите getItemViewType и дайте ему некоторую логику для определения типа представления на основе положения или типа данных в объекте (у меня есть функция getType в моем объекте)

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

Вернуть правильный макет в onCreateView на основе ViewType (не забудьте передать тип представления в класс 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);
    }
}

Получить представления содержимого различных макетов на основе открытого статического класса типа представления AppListItemHolder extends RecyclerView.ViewHolder {

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

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

А затем в вашем ItemTouchHelper измените действия на основе ViewType. Для меня это отключает прокрутку заголовка раздела RecyclerView

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

источник
Хорошая идея, но getItemViewType является окончательной
Тим
3

Сначала в recyclerView в методе onCreateViewHolder установите тег для каждого типа viewHolder, как показано ниже:

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

затем в реализации ItemTouchHelper.Callback обновите метод getMovementFlags, как показано ниже:

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

в конце прикрепите к recyclerView:

final SwipeController swipeController = new SwipeController();

    ItemTouchHelper itemTouchHelper = new ItemTouchHelper(swipeController);
    itemTouchHelper.attachToRecyclerView(recyclerView);
М.Кучи
источник
2

В дополнение к ответу @Rafael Toledo, если вы хотите удалить конкретный жест смахивания, такой как Смахивание ВЛЕВО или Смахивание ВПРАВО, в зависимости от положения или типа данных в объекте в Адаптере, вы можете сделать это.

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

Здесь, в моем случае, в моем Recyclerview я загружаю разговоры в чате, и у него есть жесты смахивания ВПРАВО (для АРХИВА) и ВЛЕВО (для ВЫХОДА) для каждого элемента. Но в случае, если разговор не активен, мне нужно отключить ВПРАВО для конкретного элемента в представлении Recycler.

Итак, я проверяю activeChatи, соответственно, отключил ПРАВЫЙ свайп.

Король масс
источник
2

Его можно отключить с помощью 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)
Педро Ромао
источник