android开发游记:ItemTouchHelper 使用RecyclerView打造可拖拽的GridView

摘要:
它主要是关于使用ItemTouchHelper。ItemTouchHelper是support-v7包中添加的一个实现类,用于帮助开发人员处理拖放操作。它允许您轻松实现侧滑删除和拖放功能。我们只需要实例化ItemTouchHelper。然后可以将其与RecyclerView:itemTouchHelper=newItemTouchHelper;itemTouchHelper。附加到RecyclerView;ItemTouchHelper。施工方法中需要回调。定义一个类来继承和实现ItemTouchHelper回调接口需要实现以下方法:@OverridepublicintgetMovementFlags{}@Overridepublic poolnonMove{}@OverridepublicvotionSwaped{}getMovementFlags用于设置是否处理拖动事件和滑动事件。

以下是RecyclerView结合ItemTouchHelper实现的列表和网格布局的拖拽效果。

效果图例如以下:(gif图有点顿卡,事实上执行是非常流畅的)

这里写图片描写叙述这里写图片描写叙述

demo下载地址:
DragRecyclerView

怎样实现

那么是怎样实现的呢?主要就要使用到ItemTouchHelper ,ItemTouchHelper 是support-v7包中加入的一个帮助开发人员处理拖拽和滑动的实现类,它能够让你非常easy实现側滑删除、拖拽的功能。

我们仅仅须要实例化一个ItemTouchHelper。然后关联到RecyclerView就OK了:

itemTouchHelper = new ItemTouchHelper(new ItemTouchHelper.Callback());
itemTouchHelper.attachToRecyclerView(recyclerView);

构造方法中须要一个ItemTouchHelper.Callback。ItemTouchHelper会在拖拽或剔除的时候回调Callback中对应的方法,我们仅仅需在Callback中实现自己的逻辑就能够了。

自己定义一个类继承实现ItemTouchHelper.Callback接口,须要实现以下方法:

     @Override
    public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
    }

    @Override
    public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
    }

    @Override
    public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
    }

getMovementFlags用于设置是否处理拖拽事件和滑动事件。以及拖拽和滑动操作的方向,比方假设是列表类型的RecyclerView,拖拽仅仅有UP、DOWN两个方向,而假设是网格类型的则有UP、DOWN、LEFT、RIGHT四个方向:

    @Override
    public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
        if (recyclerView.getLayoutManager() instanceof GridLayoutManager) {
            final int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN | ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;
            final int swipeFlags = 0;
        } else {
            final int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
            final int swipeFlags = 0; 
        }
        return makeMovementFlags(dragFlags, swipeFlags);
    }

dragFlags 是拖拽标志,swipeFlags是滑动标志,我们把swipeFlags 都设置为0,表示不处理滑动操作。

假设我们设置了非0的dragFlags 。那么当我们长按item的时候就会进入拖拽并在拖拽过程中不断回调onMove()方法,我们就在这种方法里获取当前拖拽的item和已经被拖拽到所处位置的item的ViewHolder。有了这2个ViewHolder。我们就能够交换他们的数据集并调用Adapter的notifyItemMoved方法来刷新item。

@Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
      int fromPosition = viewHolder.getAdapterPosition();//得到拖动ViewHolder的position
      int toPosition = target.getAdapterPosition();//得到目标ViewHolder的position
      if (fromPosition < toPosition) {
          for (int i = fromPosition; i < toPosition; i++) {
              Collections.swap(results, i, i + 1);
          }
      } else {
          for (int i = fromPosition; i > toPosition; i--) {
              Collections.swap(results, i, i - 1);
          }
      }
      adapter.notifyItemMoved(fromPosition, toPosition);
      return true;
}

同理假设我们设置了非0的swipeFlags。我们在滑动item的时候就会回调onSwiped的方法,我们不处理这个事件,空着即可了。

到这里。已经能够拖拽了。可是拖拽的时候我们拖拽的对象不能高亮显示。这是不友好的。我们希望拖拽的Item在拖拽的过程中背景颜色加深,这样就须要继续重写以下两个方法:

    //当长按选中item的时候(拖拽開始的时候)调用
    @Override
    public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {
    }

    //当手指松开的时候(拖拽完毕的时候)调用
    @Override
    public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
    }

我们在開始拖拽的时候给item加入一个背景色,然后在拖拽完毕的时候还原:

    @Override
    public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {
        if (actionState != ItemTouchHelper.ACTION_STATE_IDLE) {
            viewHolder.itemView.setBackgroundColor(Color.LTGRAY);
        }
        super.onSelectedChanged(viewHolder, actionState);
    }

    @Override
    public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
        super.clearView(recyclerView, viewHolder);
        viewHolder.itemView.setBackgroundColor(0);
    }

OK,这样就完毕了一个可拖拽的GridView。

更加复杂的需求

上面的代码完毕了基本功能,但实际的产品须要往往可能会有些不一样,比方说,产品希望,有一些item能够拖拽。一些item无法拖拽,就如上图的“很多其它”是无法拖拽的。

这个咋办呢?

事实上在上面我们实现的Callback类中有一个方法我们没有重写:

    @Override
    public boolean isLongPressDragEnabled() {
        return true;
    }

这种方法是为了告诉ItemTouchHelper是否须要RecyclerView支持长按拖拽,默认返回是ture(即支持),理所当然我们要支持,所以我们没有重写,由于默认true。可是这样做是默认所有的item都能够拖拽。怎么实现部分item拖拽呢,查阅isLongPressDragEnabled方法的源代码发现。上面的凝视上写着:

Default value returns true but you may want to disable this if you want to start
dragging on a custom view touch using {@link #startDrag(ViewHolder)}.

意思是假设你想自己定义触摸view。那么就使用startDrag(ViewHolder)方法。

原来如此,我们能够在item的长按事件中得到当前item的ViewHolder 。然后调用ItemTouchHelper.startDrag(ViewHolder vh)就能够实现拖拽了,那就这么办:

首先我们重写isLongPressDragEnabled返回false,我们要自己调用拖拽过程:

    @Override
    public boolean isLongPressDragEnabled() {
        return false;
    }

接着我们给RecyclerView加入item长按事件。推断item是否是最后一个(最后一个是“很多其它”),不是则開始拖拽。

可是,我们都知道RecyclerView并没有提供OnItemLongClickListener。这个问题我在上一篇博客中已经完美地攻克了,就是使用OnItemTouchListener。然后识别触摸手势,这里给上传送门:RecyclerView无法加入onItemClickListener最佳的高效解决方式,后面我就直接使用上一篇的成果。不反复讲了:

recyclerView.addOnItemTouchListener(new OnRecyclerItemClickListener(recyclerView) {
    //item 长点击事件
    @Override
    public void onLongClick(RecyclerView.ViewHolder vh) {
        //假设item不是最后一个,则执行拖拽
        if (vh.getLayoutPosition()!=results.size()-1) {
            itemTouchHelper.startDrag(vh);
        }
    }
    //item 点击事件
    @Override
    public void onItemClick(RecyclerView.ViewHolder vh) {
    }
});

OK,大功告成。

额外的功能

保存位置

关闭页面以后再打开,又恢复到了初始化的位置,所以就须要保存调整的位置到本地,下次初始化的时候读取位置。


保存位置应该由开发人员自己实现。由于每一个人本地化数据的方式都不一样,我这里做一个简单的实现。使用了开源的ACache类,两个方法,搞定:

//读取
ACache.get(context).getAsObject("items");
//存储
ACache.get(context).put("items",results);

在clearView方法(拖拽完毕)中调用存储方法,在页面初始化数据是调用读取方法。

详见demo

開始拖拽时震动

支付宝的拖拽网格在长按后開始拖拽时会有一次短时间的震动提示用户開始拖拽了,非常友好的交互。我们也加一个:

加入权限:

<uses-permission android:name="android.permission.VIBRATE" />

在開始拖拽时加入以下代码:

//获取系统震动服务
Vibrator vib = (Vibrator) activity.getSystemService(Service.VIBRATOR_SERVICE);
//震动70毫秒
vib.vibrate(70);

详见demo,demo下载地址:

DragRecyclerView

免责声明:文章转载自《android开发游记:ItemTouchHelper 使用RecyclerView打造可拖拽的GridView》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇Unity 实用的设置技巧是互动还是告状 “家校通”通往何处?(转)下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

Android 开 发 资 源 分 享

一、精编教程贴 1、安卓模拟器BlueStacks安装使用教程 http://www.apkbus.com/android-64318-1-1.html 2、Mkey libgdx游戏引擎教程 http://www.apkbus.com/android-57355-1-1.html 3、java基础 与 android学习指南——实例教程 http://w...

NDK笔记(二)-在Android Studio中使用ndk-build(转)

前面一篇我们接触了CMake,这一篇写写关于ndk-build的使用过程。刚刚用到,想到哪儿写哪儿。 环境背景 Android开发IDE版本:AndroidStudio 2.2以上版本(目前已经升级到2.2.3) 计算机环境:Window 64位系统 Demo工程的创建与设置 1.新建一个工程,并在app/src/main路径下新建jni文件夹。 2.在...

Android ROM开发ubuntu下编译CyanogenMod生成SDK

由于CM2.3.7存在某些问题,所以如果需要使用稳定版本的CyanogenMod 2.3.4编译并且相应的vendor文件的话,可以到我的网盘中下载。 vendor下载地址:http://115.com/file/dpi59mxv 在开发定制ROM的时候经常会在源码中添加自定义的SDK,比如小米的MIUI里很多添加进来的功能在编码的时候都是使用自定义的方法...

理解 Android Build 系统

测试 前言 Android Build 系统是 Android 源码的一部分。关于如何获取 Android 源码,请参照 Android Source 官方网站 。 Android Build 系统用来编译 Android 系统,Android SDK 以及相关文档。该系统主要由 Make 文件,Shell 脚本以及 Python 脚本组成,其中最主要的是...

android自定义控件及自定义组合控件

一、构建自定义控件 构建自定义组件 Android中,你的应用程序程序与View类组件有着一种固定的联系,例如按钮(Button)、文本框(TextView),可编辑文本框(EditText),列表框(ListView),复选框(CheckBox),单选框(RadioButton),滚动条(Gallery),微调器(Spinner), 等等,还有一些比较先...

Android Launcher 简易Launcher开发

第一步,把我们的应用程序作为home(即实现按下home键,启动自己的launcher.) 要把我们的应用程序作为home,只需要在AndroidManifest.xml中添加:<category android:name="android.intent.category.HOME" /><category android:name="...