还是接着上一讲“进击的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. 分享一个mysql 复杂查询的例子

    发布:脚本学堂/MySQL  编辑:thebaby   2013-08-23 09:37:37  [大 中 小] 有关mysql复杂查询的一个例子,正在学习mysql的朋友,可以作为一个参考. 在my ...

  2. atitit.研发管理--标准化流程总结---java开发环境与项目部署环境的搭建工具包总结

    atitit.研发管理--标准化流程总结---java开发环境与项目部署环境的搭建工具包总结 1. ide系列(只开发环境需要,但部署环境也做好放上,很有用) 1 2. web服务器+sdk+网站程序 ...

  3. 用EA生成实体层代码

    在个人版机房重构中.实体层的代码敲得有点儿烦了.不同的实体仅仅是命名不同.代码结构全然一样.遇到反复的事情,就该动动脑.想想办法了. 以下给大家介绍使用EA生成实体层的代码. 首先.建一个类,注意选择 ...

  4. Hive入门笔记---1.Hive简单介绍

    1. Hive是什么 Hive是基于Hadoop的数据仓库解决方案.由于Hadoop本身在数据存储和计算方面有很好的可扩展性和高容错性,因此使用Hive构建的数据仓库也秉承了这些特性.这是来自官方的解 ...

  5. 使用padding和float处理带有间隙的多块布局

    . 每个间隙都是20px <div class="action-content pd10" style=""> <div class=&quo ...

  6. oozie中事件触发input-events和done-flag

    样例如下: <coordinator-app name="test_job" frequency="${coord:days(1)}" start=&qu ...

  7. 70个shell经常使用操作

    1) 怎样向脚本传递參数 ? ./script argument 样例: 显示文件名脚本 ./show.sh file1.txt cat show.sh #!/bin/bash echo $1 2) ...

  8. C++ 类的构造函数使用规则

    //类的构造函数使用规则 #define _CRT_SECURE_NO_WARNINGS #include<iostream> using namespace std; class Poi ...

  9. TensorFlow基础笔记(8) TensorFlow简单人脸识别

    数据材料 这是一个小型的人脸数据库,一共有40个人,每个人有10张照片作为样本数据.这些图片都是黑白照片,意味着这些图片都只有灰度0-255,没有rgb三通道.于是我们需要对这张大图片切分成一个个的小 ...

  10. 通过策略接口,Spring 框架是高度可配置的,而且包含多种视图技术

    通过策略接口,Spring 框架是高度可配置的,而且包含多种视图技术,例如 JavaServer Pages( JSP)技术.Velocity.Tiles.iText 和 POI.Spring MVC ...