进击的RecyclerView入门三(要是能拖动就好了)
还是接着上一讲“进击的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入门三(要是能拖动就好了)的更多相关文章
- 进击的RecyclerView入门二(来点小装饰?)
接着上一讲,我们看到我们的Demo可以正常的运行,并且能自动加载网络图片,那么为了后面观察的方便,我们取消这种自动加载的功能,使用两个按钮来代替,分别用来增加一个数据和减少一个数据.截图如下: 正在我 ...
- 进击的RecyclerView入门一(简单上手)
虽然RecyclerView面世有一段时间了,但由于它的学习成本相对较高,很多码友只是粗略的认识了一下而没有细致的品味RecyclerView的真谛. 那么从现在开始我将带你装逼带你飞,一起领略Goo ...
- DevExpress XtraReports 入门三 创建 Master-Detail(主/从) 报表
原文:DevExpress XtraReports 入门三 创建 Master-Detail(主/从) 报表 本文只是为了帮助初次接触或是需要DevExpress XtraReports报表的人群使用 ...
- 【原创】NIO框架入门(三):iOS与MINA2、Netty4的跨平台UDP双向通信实战
前言 本文将演示一个iOS客户端程序,通过UDP协议与两个典型的NIO框架服务端,实现跨平台双向通信的完整Demo.服务端将分别用MINA2和Netty4进行实现,而通信时服务端你只需选其一就行了.同 ...
- Jqgrid入门-Jqgrid列数据拖动(七)
上一章提到在Jqgrid中如何设置二级表头,这一章节主要探讨Jqgrid表格里面的数据如果实现拖动功能,比如你想把第一行的数据拖到当前页的最后一行,或者其他位置. Jqgrid表格插件自己没有 ...
- 2015版Force Touch Mac Book激活三个手指拖动窗口
新买的2015版的Mac Book Pro,一切都好,就是原来一直很的很习惯的三个手指拖动窗口的手势,突然找不到地方设置了,很是让我失望了一把,在想苹果怎么会把这么有用的手势去掉了呢.还好有万能的Go ...
- Swift语法基础入门三(函数, 闭包)
Swift语法基础入门三(函数, 闭包) 函数: 函数是用来完成特定任务的独立的代码块.你给一个函数起一个合适的名字,用来标识函数做什么,并且当函数需要执行的时候,这个名字会被用于“调用”函数 格式: ...
- Thinkphp入门三—框架模板、变量(47)
原文:Thinkphp入门三-框架模板.变量(47) [在控制器调用模板] display() 调用当前操作名称的模板 display(‘名字’) 调用指定名字的模板文件 控制器调用模板四种方式 ...
- 微服务(入门三):netcore ocelot api网关结合consul服务发现
简介 api网关是提供给外部调用的统一入口,类似于dns,所有的请求统一先到api网关,由api网关进行指定内网链接. ocelot是基于netcore开发的开源API网关项目,功能强大,使用方便,它 ...
随机推荐
- MySql图解给表添加外键
关于外键约束的几种方式,请移步鄙人的另外一个博客中的博文 http://blog.csdn.net/hadues/article/details/52558184
- jQuery select添加图标
//下拉菜单样式 /*查找全部select的下拉菜单*/ function getElemsById(cot_val){ return document.getElementById(cot_val) ...
- 开源搜索引擎评估:lucene sphinx elasticsearch (zhuan)
http://lutaf.com/158.htm ************************ 开源搜索引擎程序有3大类 lucene系,java开发,包括solr和elasticsearch s ...
- CentOs下安装gcc/g++/gdb
使用yum安装gcc:yum install gcc即可.使用:which gcc 查看是否安装成功 使用yum安装g++:yum install gcc-c++ 即可.使用:which g++ 查看 ...
- CSS学习笔记(5)--导航ul,li浮动问题
为什么只给li设置浮动,没有给ul设置浮动,ul后的元素div也会跟着浮动? 添加评论 分享 默认排序按时间排序 3 个回答 张思远 程序员 2 人赞同 代码是不是这样的啊?<ul& ...
- 基于HTML5自定义文字背景生成QQ签名档
分享一款利用HTML5实现的自定义文字背景应用,首先我们可以输入需要显示的文字,并且为该文字选择一张背景图片,背景图片就像蒙版一样覆盖在文字上.点击生成QQ签名档即可将文字背景融为一体生成另外一张图片 ...
- [kernel]字符设备驱动、平台设备驱动、设备驱动模型、sysfs几者之间的比较和关联
转自:http://www.2cto.com/kf/201510/444943.html Linux驱动开发经验总结,绝对干货! 学习Linux设备驱动开发的过程中自然会遇到字符设备驱动.平台设备驱动 ...
- 16c554 的头文件
//------------------------------------------------------------------------------ #ifndef AT16C554H # ...
- UCOS2系统内核讲述(四)_创建任务
Ⅰ.写在前面 学习本文之前可以参看我前面的文章: UCOS2_STM32移植详细过程(汇总文章) UCOS2系统内核讲述(一)_总体描述 UCOS2系统内核讲述(二)_初始化调用函数 UCOS2系统内 ...
- C++ 匿名对象初始化新对象
//c++中匿名对象初始化新对象 #include<iostream> using namespace std; class Point{ public: Point(){ cout &l ...