使用 ItemTouchHelper.SimpleCallback 禁用滑动查看 RecyclerView 中的位置

Disable Swipe for position in RecyclerView using ItemTouchHelper.SimpleCallback

我正在使用 recyclerview 22.2.0 和助手 class ItemTouchHelper.SimpleCallback 来为我的列表启用 swipe-to-dismiss 选项。但是因为我上面有一个 header 类型,所以我需要禁用适配器第一个位置的滑动行为。由于 RecyclerView.Adapter 没有 isEnabled() 方法,我试图通过方法禁用视图交互 isEnabled()isFocusable() 在 ViewHolder 创建本身,但没有成功。我尝试将滑动阈值调整为完整值,例如 SimpleCallback 方法 getSwipeThreshold()[= 中的 0f ot 1f 33=],但也没有成功。

我的一些代码片段希望大家帮帮我。

我的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);
}

我有一个带有 两种 视图类型的通用适配器。在我想禁用滑动的 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);      
    }
}

玩了一会儿后,我发现 SimpleCallback 有一个名为 getSwipeDirs() 的方法。由于我有一个特定的 ViewHolder 用于 not swipable 位置,我可以使用 instanceof 来避免滑动。如果不是您的情况,您可以使用 Adapter 中 ViewHolder 的位置来执行此控制。

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

如果有人在使用 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);
}

在这里代替 dragFlagsswipeFlags 你可以传递 0 来禁用相应的功能。

ItemTouchHelper.START 表示在从左到右语言环境(LTR 应用程序支持)的情况下从左向右滑动,但在从右到左语言环境中则相反( RTL 应用程序支持)。 ItemTouchHelper.END表示向START相反的方向滑动。

因此您可以根据需要删除任何标志。

有几种方法可以解决这个问题,但如果您只有一个 ViewHolder,但有多个布局,您可以采用这种方法。

覆盖 getItemViewType 并为其提供一些逻辑,以根据 object 中的位置或数据类型确定视图类型(我的 object 中有一个 getType 函数)

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

Return onCreateView 中基于 ViewType 的正确布局(确保将视图类型传递给 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);
    }
}

根据视图类型获取不同布局的内容视图 public 静态 class AppListItemHolder 扩展 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 部分的滑动 header

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

这是一种简单的方法,它只取决于被滑动项目的位置:

@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 值从适配器获取数据以用于被刷卡的项目并相应地禁用。

如果您已经有不需要刷卡的特定持有人类型,接受的答案将有效。但是,创建持有人类型作为头寸的代理是一个错误,应该避免。

首先在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);

除了@Rafael Toledo 的回答之外,如果您想根据 Adapter 中对象中数据的位置或类型删除特定的滑动手势,如向左滑动或向右滑动,那么您可以这样做。

@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 并相应地禁用了右滑动。

可以使用 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)