##Recyclerview 实现仿电脑文件夹自由拖拽
大家好,今天是我第一次写博客,希望能将学到的技术点记录起来,方便以后借鉴。不足之处,请指出,大家一起学习。
想实现和电脑拖拽文件夹一样的效果其实很简单,因为Recyclerview 本身就提供了类似的动画效果,我们只需将原来的动画效果改吧改吧就可以了。
##效果
1.创建Activity(Mistake_Homework_Activity ) 下面是XML布局
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_mistake__homework_"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/white"
tools:context="com.daxiong.fun.function.homework.MistakeHomework.Mistake_Homework_Activity">
<include
android:id="@+id/layout_head"
layout="@layout/main_header_view"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<android.support.v7.widget.RecyclerView
android:background="@color/white"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:layout_marginTop="5dp"
android:layout_below="@+id/layout_head"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/recyclerView"/>
<TextView
android:text="编辑"
android:textSize="16sp"
android:id="@+id/tv_edit"
android:layout_marginRight="5dp"
android:layout_alignParentRight="true"
android:padding="10dp"
android:gravity="center"
android:layout_width="wrap_content"
android:layout_height="50dp" />
</RelativeLayout>
##2.先定义一个回调接口
这个先放着,待会再用。
public interface CallbackItemTouch {
/**
* Called when an item has been dragged
* @param oldPosition start position
* @param newPosition end position
*/
void itemTouchOnMove(RecyclerView.ViewHolder holder, int oldPosition, int newPosition,RecyclerView.ViewHolder target);
}
##3.重写 ItemTouchHelper.Callback的内部类
ItemTouchHelper这个类它继承了 RecyclerView.ItemDecoration。这是一个强大的帮助类。用来配合RecyclerView使用,ItemTouchHelper同一时刻只能支持两种效果:swipe、drag中的一种。分别用来实现RecyclerView里面item侧滑删除(swipe)效果或者item长按拖拽移动(drag)。
/**
* Created by guo on 2018/7/24.
*/
public class MyItemTouchHelperCallback extends ItemTouchHelper.Callback{
CallbackItemTouch callbackItemTouch; // interface
public MyItemTouchHelperCallback(CallbackItemTouch callbackItemTouch){
this.callbackItemTouch = callbackItemTouch;
}
@Override
public boolean isLongPressDragEnabled() {
return true;
}
@Override
public boolean isItemViewSwipeEnabled() {
return false; // swiped disabled
}
@Override
public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
// movements drag 设置可自由拖动的方向
int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN | ItemTouchHelper.LEFT| ItemTouchHelper.RIGHT;
return makeFlag( ItemTouchHelper.ACTION_STATE_DRAG , dragFlags);
}
@Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
//当拖拽时的回调方法, callbackItemTouch就是我们刚刚写的回调接口啦,待会我们会在activity中重写这个子类。
callbackItemTouch.itemTouchOnMove(viewHolder,viewHolder.getAdapterPosition(),target.getAdapterPosition(),target); // information to the interface
return false;
}
@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
// swiped disabled
}
}
##4.Mistake_Homework_Activity 界面
- Activity先实现我们刚刚定义的接口,并初始化RecyclerView就可以实现自由拖拽了。(初始化时得把刚刚重写的MyItemTouchHelperCallback 添加到recyclerView对象中)
public class Mistake_Homework_Activity extends BaseActivity implements CallbackItemTouch {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_mistake__homework_);
//初始化view
initView();
//设置监听器
setListener();
}
// 初始化方法
public void initView() {
mRecyclerView = (RecyclerView) findViewById(R.id.recyclerView);
//设置RecyclerView管理器,这里设置3列
gridLayoutManager = new StaggeredGridLayoutManager(3, StaggeredGridLayoutManager.VERTICAL);
mRecyclerView.setLayoutManager(gridLayoutManager);
//初始化适配器
mAdapter = new MisTakeHomeWorkAdapter(subList);
//设置添加或删除item时的动画,这里使用默认动画
//mRecyclerView.setItemAnimator(new DefaultItemAnimator());
final ItemTouchHelper.Callback callback = new MyItemTouchHelperCallback(this);// create MyItemTouchHelperCallback
ItemTouchHelper touchHelper = new ItemTouchHelper(callback); // Create ItemTouchHelper and pass with parameter the MyItemTouchHelperCallback
touchHelper.attachToRecyclerView(mRecyclerView); // Attach ItemTouchHelper to RecyclerView
mRecyclerView.setAdapter(mAdapter);
}
@Override
public void itemTouchOnMove(RecyclerView.ViewHolder oldViewHolder, int oldP, int newP, RecyclerView.ViewHolder targetViewHolder) {
//TODO 此方法在下面实现
}
}
5.当然写完上面写代码就能够实现自由拖拽的,但是拖拽后放开后,item的镜像会返回到原地。所以啊,我们还要在拖拽过程中记录当前你把item拖拽到了那个view了,记录下position。当放开时执行我们的逻辑代码,判断是否将这个item放入到目标item中。
在activity中这个回调方法中,记录item镜像移动过程中的 起始position,和最新所在的position
@Override
public void itemTouchOnMove(RecyclerView.ViewHolder oldViewHolder, int oldP, int newP, RecyclerView.ViewHolder targetViewHolder) {
//移动到新的item上方时,给它设置选中颜色
setChooseColor(newP);
}
//记录 position
this.oldPosition = oldP;
this.newPosition = newP;
}
/**
newP是手指所在的item position
*/
private void setChooseColor(int newP){
int count=mRecyclerView.getChildCount();
for (int i=0;i<count;i++){
View itemView = mRecyclerView.getChildAt(i);
int position = (int) itemView.getTag();
if(newP==position){
itemView.setBackgroundColor(Color.parseColor("#55cccccc"));
}else {
itemView.setBackgroundColor(Color.parseColor("#00ffffff"));
}
}
}
好!既然记录了起始和目标的position了,那我们是不是监听RecyclerView的 setOnTouchListener方法就可以了呢。
mRecyclerView.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
int type = event.getAction();
switch (type) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE:
break;
case MotionEvent.ACTION_UP:
int x = (int) event.getX();
int y = (int) event.getY();
try {
//判断是否移动
if (newPosition != -1 && oldPosition != subList.size() - 1) {
//根据x y 坐标获取当前手指所在item的position,这个方法在下面贴出代码
int position = pointToPosition(x, y);
//既然获取了position了,那就好办了,根据position,判断目标position的类型后执行相应的逻辑代码。
// TODO ...
break;
}
}
}
//根据坐标,获取所在item 的position值,返回-1则获取失败,可能是在RecyclerView之外,不做处理。
public int pointToPosition(int x, int y) throws Exception {
Rect frame = new Rect();
final int count = mRecyclerView.getChildCount(); //显示的子item 数
for (int i = count - 1; i >= 0; i--) { // 遍历判断当前xy值所在的position
final View child = mRecyclerView.getChildAt(i);
int position = (int) child.getTag();
if (oldPosition != position) { //忽略移动view的坐标
if (child.getVisibility() != View.GONE) {
child.getHitRect(frame);
if (frame.contains(x, y)) {
int[] mFirstVisibleItems = null;
mFirstVisibleItems = gridLayoutManager.findFirstVisibleItemPositions(mFirstVisibleItems);
if (mFirstVisibleItems != null) {
int mFirstPosition = mFirstVisibleItems[0];
return mFirstPosition + i;
}
}
}
}
}
return -1;
}
好了,将上面几个方法添加到activity中就可能实现自由拖拽啦,其它的也可以自己优化。