android UI——RecyclerView禁止拖拽滑动指定item

背景:

    一般来讲,是用不到这个需求的,但是,今天在搞层叠卡片的时候,碰到这个问题,我希望的是只有顶层item可以滑动,其他item禁止滑动。否则,用户拖拽的时候,拖拽了非顶层卡片,就会出现错位问题。所以,就先百度了下,发现没有相关文章,就只好自己动手来解决了。先看看不处理的效果图吧:

这效果,产品能忍,测试不能忍,测试能忍,我不能忍

解决:

     出现问题不可怕,找出问题是关键。那么是什么导致的呢?其实就是拖拽的时候,拖拽了非顶层的item。那么,我的需求就变成了,禁止拖拽其他非顶层item,只能拖拽顶层item。有没有这样的方法?

    先看看ItemTouchHelper.SimpleCallback 的几个方法:

/**
 * Called when ItemTouchHelper wants to move the dragged item from its old position to
 * the new position.
 * 巴拉巴拉一堆,说了在想拖拽交换位置的时候调用,一般在这里设置监听
 */
  public abstract boolean onMove(RecyclerView recyclerView,
        ViewHolder viewHolder, ViewHolder target);

 /**
 * Called when a ViewHolder is swiped by the user.
 * <p>
 * If you are returning relative directions ({@link #START} , {@link #END}) from the
 * {@link #getMovementFlags(RecyclerView, ViewHolder)} method, this method
 * 位置交换后调用。一般在这个时候对数据进行处理
 */
public abstract void onSwiped(ViewHolder viewHolder, int direction);
/**
 * Called by ItemTouchHelper on RecyclerView's onDraw callback.
 * 又讲了一堆。简单说,就是拖拽过程子view的绘制监听。可以在这里对拖拽过程加上一些动画等。
 */
public void onChildDraw(Canvas c, RecyclerView recyclerView,
        ViewHolder viewHolder,
        float dX, float dY, int actionState, boolean isCurrentlyActive) {
    sUICallback.onDraw(c, recyclerView, viewHolder.itemView, dX, dY, actionState,
            isCurrentlyActive);
}

那有没有禁止拖拽的方法呢?有的。

/**
 * Returns whether ItemTouchHelper should start a swipe operation if a pointer is swiped
 * over the View.
 */
public boolean isItemViewSwipeEnabled() {
    return true;
}

只是重写方法后,返回false,那么所有item都不能拖拽了。这可和我的需求不符合。

问题来了。怎么办?

既然上面方法可以禁止所有item拖拽,说明,这个方法还是很有用的。找到这个方法在哪里被调用了。然后针对想要的效果处理一下,比如我想让顶层item可以滑动。那就在判断的时候,过滤掉这个判断或修改返回值不就可以了么?


找到位置了。可以看到,是在ItemTouchHelper 的checkSelectForSwipe()方法里做判断。位置找到了。在运行一遍程序,看下调用过程。发现,每次拖拽,都是先执行该方法,其他几个callback的方法才执行。其实,如果先执行其他方法的话,会更好办些,可以在重写的onChildDraw()方法里直接处理。可是不行呀。是先执行这个ItemTouchHelper里的方法。直接改android sdk的方法时不行的。那怎么办?

    那就不用sdk提供的ItemTouchHelper类了。自己写一个一模一样的。然后,专门针对checkSelectForSwipe()方法处理。注意,ItemTouchHelper会用到一些sdk里的类,这些类默认不可包外引用,所以也要一并重写。

再看看如何处理checkSelectForSwipe方法吧

/**
 * Checks whether we should select a View for swiping.
 * 可以看到,比原来方法多出了个参数: RecyclerView , 是为了拿到最大个数。可根据自己的需求,去掉或者增加其他参数。
 * 毕竟,这里是自己的类了。想怎么处理怎么处理
 */
 boolean checkSelectForSwipe(RecyclerView recyclerView,int action, MotionEvent motionEvent, int pointerIndex) {
    if (mRecyclerView.getScrollState() == RecyclerView.SCROLL_STATE_DRAGGING) {
        return false;
    }
    final RecyclerView.ViewHolder vh = findSwipedView(motionEvent);
    if (vh == null) {   
        return false;
    }
    int position = vh.getPosition();  //拿到当前拖拽的item位置
    int size = recyclerView.getAdapter().getItemCount()-1;   //顶层item所在位置
    if(position != size) return false;         //如果当前拖拽item不是顶层item,则返回false,表示不swipe。 否则拖拽item开始随手势滑动。
    if (mSelected != null || action != MotionEvent.ACTION_MOVE   
            || mActionState == ACTION_STATE_DRAG || !mCallback.isItemViewSwipeEnabled()) {   //将该部分校验下移,先拿到vh和recyclerview .
        return false;
    }
    final int movementFlags = mCallback.getAbsoluteMovementFlags(mRecyclerView, vh);

    final int swipeFlags = (movementFlags & ACTION_MODE_SWIPE_MASK)
            >> (DIRECTION_FLAG_COUNT * ACTION_STATE_SWIPE);

    if (swipeFlags == 0) {
        return false;
    }
.......................................................................................省略拖拽滑动部分的代码,可以自己去看  
 return true;
}

改完这些。在跑一下代码看看效果:

今天传图一直有问题。第一个图折腾了好久才传到github上。这个效果图上传上了。就是展示不出来。

项目demo地址:https://github.com/shoneworn/SwipeCard   喜欢就给个小star

如果你积分比较多,可以选择到此处下载:

https://download.csdn.net/download/shoneworn/10521930  


评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值