还是接着上一讲“进击的RecyclerView入门二(来点小装饰?)”,在上一讲中我们学到了怎么给不同的Item定制不同的外观,但貌似那个蓝色的框实在太丑了,咱还是把它干了吧。

@Override
public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
//太丑了,这段还是注释掉吧
// for (int i = 0; i < parent.getChildCount(); i++) {
// if ((i - 1) % 3 == 0) {
// View child = parent.getChildAt(i);
// RecyclerView.LayoutParams rLP = (RecyclerView.LayoutParams) child.getLayoutParams();
// int left = child.getLeft();
// int top = child.getTop();
// int right = child.getRight();
// int bottom = child.getBottom();
// c.drawRect(left, top, right, bottom, paint);
// }
// }
}

把那个碍眼的蓝色去掉以后就来认识一下我们今天的主角ItemTouchHelper,从名字上就可以看出它是与视图的触摸相关的,还是国际惯例看一下官方文档的定义:

This is a utility class to add swipe to dismiss and drag & drop support to RecyclerView.

简单讲这个类是用来帮助处理RecyclerView子View的拖拽放置和滑动删除的。

It works with a RecyclerView and a Callback class, which configures what type of interactions are enabled and also receives events when user performs these actions.

从上面这段可以知道ItemTouchHelper工作还需要RecyclerView和Callback来配合,那么这里的Callback是什么角色呢?

其实这里的Callback是ItemTouchHelper的一个子类,它的介绍如下:

This class is the contract between ItemTouchHelper and your application. It lets you control which touch behaviors are enabled per each ViewHolder and also receive callbacks when user performs these actions.

意思是说这个类是用来建立ItemTouchHelper和我们应用之间的桥梁的,它可以控制哪些触摸事件可用哪些不可用,还可以接受到这些事件的回调。

总而言之,要实现RecyclerView的拖拽放置和滑动删除需要以下三个类的配合:

  • RecyclerView
  • ItemTouchHelper
  • ItemTouchHelper.Callback

在看ItemTouchHelper.Callback的文档的时候发现它有一个子类ItemTouchHelper.SimpleCallback,似乎又啥猫腻,我们来看看:

A simple wrapper to the default Callback which you can construct with drag and swipe directions and this class will handle the flag callbacks. You should still override onMove or onSwiped depending on your use case.

它是对Callback的一个简单包装,它可以让你自由的通过不同的方向来构建拖拽放置或滑动,并处理这些回调,同时我们自需根据自身需求实现onMove或onSwiped即可。

另外文档中还给了一个小例子:

ItemTouchHelper mIth = new ItemTouchHelper(
new ItemTouchHelper.SimpleCallback(ItemTouchHelper.UP | ItemTouchHelper.DOWN,
ItemTouchHelper.LEFT) {
public abstract boolean onMove(RecyclerView recyclerView,
ViewHolder viewHolder, ViewHolder target) {
final int fromPos = viewHolder.getAdapterPosition();
final int toPos = viewHolder.getAdapterPosition();
// move item in `fromPos` to `toPos` in adapter.
return true;// true if moved, false otherwise
}
public void onSwiped(ViewHolder viewHolder, int direction) {
// remove from adapter
}
});

那么接下来我们就来看看具体怎么码代码?

首先准备SimpleCallback:

private ItemTouchHelper.SimpleCallback simpleCallback = new ItemTouchHelper.SimpleCallback(ItemTouchHelper.LEFT
| ItemTouchHelper.DOWN
| ItemTouchHelper.RIGHT
| ItemTouchHelper.UP
, ItemTouchHelper.ACTION_STATE_IDLE)

这里构造方法两个参数的意思是,拖拽支持上下左右,滑动删除不支持。我们这里的网格布局,滑动删除不适合这里。

接着重写如下方法:

@Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
//获得两个Item的位置
int originPosition = viewHolder.getAdapterPosition();
int targetPistion = target.getAdapterPosition();
//交换Adapter中对应的位置
MyAdapter adapter = (MyAdapter) recyclerView.getAdapter();
adapter.move(originPosition, targetPistion);
return true;
}

上面这段代码是拖拽的回调,viewHolder是用户拖拽的Item,target用户想要放置位置的Item。接着我们获得这两个Item对应在Adapter中的位置,并通知adapter去跟新数据。如下图,是将位置5的item往位置0进行拖拽:

界面上已经有所改变了,但要真正改变这两个Item的位置需要去改变他们在adapter中的位置。

在MyAdapter中增加如下方法:


public void move(int origin, int target) {
Collections.swap(datas, origin, target);
if (origin < target) {
for (int i = origin; i < target; i++) {
Collections.swap(datas, i, i + 1);
}
}
if (origin > target) {
for (int i = origin; i > target; i--) {
Collections.swap(datas, i, i - 1);
}
}
notifyItemMoved(origin, target);
}

这里元素的位置调换需要与界面上的一致,不过多赘述。

在上面的那张动图中可以看到被拖拽的Item有放大和缩小的动画效果,这个并不是ItemTouchHelper自带的效果,需要我们自己实现,这里需要重写simpleCallback的onSelectedChanged方法:

@Override
public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {
super.onSelectedChanged(viewHolder, actionState);
if(actionState == ItemTouchHelper.ACTION_STATE_DRAG){
Log.d("scott","drag");
ObjectAnimator objectAnimatorX = ObjectAnimator.ofFloat(viewHolder.itemView, "scaleX", 1.0f, 1.2f);
ObjectAnimator objectAnimatorY = ObjectAnimator.ofFloat(viewHolder.itemView, "scaleY", 1.0f, 1.2f);
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.setDuration(300);
animatorSet.playTogether(objectAnimatorX, objectAnimatorY);
animatorSet.start();
view = viewHolder.itemView;
}
if(actionState == ItemTouchHelper.ACTION_STATE_IDLE){
ObjectAnimator objectAnimatorX = ObjectAnimator.ofFloat(view, "scaleX", 1.2f, 1.0f);
ObjectAnimator objectAnimatorY = ObjectAnimator.ofFloat(view, "scaleY", 1.2f, 1.0f);
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.setDuration(300);
animatorSet.playTogether(objectAnimatorX, objectAnimatorY);
animatorSet.start();
}
}

以上准备工作做完后就需要将ItemTouchHelper,SimpleCallback,RecyclerView三者关联起来:

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

OK,差不多就这么多了,更详细代码请参考:

demo完整源码地址:

https://github.com/ZhangQinglian/RecyclerViewAdvance

进击的RecyclerView入门三(要是能拖动就好了)的更多相关文章

  1. 进击的RecyclerView入门二(来点小装饰?)

    接着上一讲,我们看到我们的Demo可以正常的运行,并且能自动加载网络图片,那么为了后面观察的方便,我们取消这种自动加载的功能,使用两个按钮来代替,分别用来增加一个数据和减少一个数据.截图如下: 正在我 ...

  2. 进击的RecyclerView入门一(简单上手)

    虽然RecyclerView面世有一段时间了,但由于它的学习成本相对较高,很多码友只是粗略的认识了一下而没有细致的品味RecyclerView的真谛. 那么从现在开始我将带你装逼带你飞,一起领略Goo ...

  3. DevExpress XtraReports 入门三 创建 Master-Detail(主/从) 报表

    原文:DevExpress XtraReports 入门三 创建 Master-Detail(主/从) 报表 本文只是为了帮助初次接触或是需要DevExpress XtraReports报表的人群使用 ...

  4. 【原创】NIO框架入门(三):iOS与MINA2、Netty4的跨平台UDP双向通信实战

    前言 本文将演示一个iOS客户端程序,通过UDP协议与两个典型的NIO框架服务端,实现跨平台双向通信的完整Demo.服务端将分别用MINA2和Netty4进行实现,而通信时服务端你只需选其一就行了.同 ...

  5. Jqgrid入门-Jqgrid列数据拖动(七)

    上一章提到在Jqgrid中如何设置二级表头,这一章节主要探讨Jqgrid表格里面的数据如果实现拖动功能,比如你想把第一行的数据拖到当前页的最后一行,或者其他位置.     Jqgrid表格插件自己没有 ...

  6. 2015版Force Touch Mac Book激活三个手指拖动窗口

    新买的2015版的Mac Book Pro,一切都好,就是原来一直很的很习惯的三个手指拖动窗口的手势,突然找不到地方设置了,很是让我失望了一把,在想苹果怎么会把这么有用的手势去掉了呢.还好有万能的Go ...

  7. Swift语法基础入门三(函数, 闭包)

    Swift语法基础入门三(函数, 闭包) 函数: 函数是用来完成特定任务的独立的代码块.你给一个函数起一个合适的名字,用来标识函数做什么,并且当函数需要执行的时候,这个名字会被用于“调用”函数 格式: ...

  8. Thinkphp入门三—框架模板、变量(47)

    原文:Thinkphp入门三-框架模板.变量(47) [在控制器调用模板] display()   调用当前操作名称的模板 display(‘名字’)  调用指定名字的模板文件 控制器调用模板四种方式 ...

  9. 微服务(入门三):netcore ocelot api网关结合consul服务发现

    简介 api网关是提供给外部调用的统一入口,类似于dns,所有的请求统一先到api网关,由api网关进行指定内网链接. ocelot是基于netcore开发的开源API网关项目,功能强大,使用方便,它 ...

随机推荐

  1. Step by Step Learn Python(1)

    print "Hello World!" action = raw_input("please select your action{1, 2, 3, 4, 5, 6, ...

  2. atitit.激活一个窗口总结 swing java .net php

    atitit.激活一个窗口总结 1     激活窗口:鼠标激活vswindows消息激活 1.1      Web框架激活 2     退出激活窗口热键(dbg模式) 3     俩个窗口激活优先级 ...

  3. CentOS安装使用git

    yum install git yum install git-gui (可选) git config --global user.name "gg" git config --g ...

  4. [Objective C]super dealloc 调用时机

    转自:http://dcm19872007.blog.163.com/blog/static/86519374201311953739818/ objective-c 语言中最头疼的事就是内存释放,申 ...

  5. 常用Linux shell命令汇总

    1.检查远程端口是否对bash开放:echo >/dev/tcp/8.8.8.8/53 && echo "open" 2.让进程转入后台:Ctrl + z 3 ...

  6. Nginx 使用中文URL,中文目录路径

    Nginx 使用中文URL,中文目录路径 分类: linux2012-05-03 11:04 2672人阅读 评论(0) 收藏 举报 nginxurl服务器translationcentosserve ...

  7. SQL Server 2014 Agent 无法启动

    - <Event xmlns="http://schemas.microsoft.com/win/2004/08/events/event">- <System& ...

  8. [算法]滴滴笔试题——求最大子串和(O(n)复杂度)

    扫描法.一次扫描数组即可得出答案,复杂度O(n).这种方法用文字描述不容易说清楚,下面用每一步运算的图示来表达.伪代码如下: maxsofar=end=; ,n) end=max(end+x[i],) ...

  9. [转]SOA接口的两种常用实现比较:SOAP vs REST

    原文链接:http://blog.csdn.net/zhaohuabing/article/details/39643127 SOA架构用于异构系统的协作,因此需要一种跨操作系统.跨语言的通用的消息交 ...

  10. MapReduce的InputFormat过程的学习

    转自:http://blog.csdn.net/androidlushangderen/article/details/41114259 昨天经过几个小时的学习,把MapReduce的第一个阶段的过程 ...