recyclerview这个控件太强大了,自带各种特效,很多高逼格效果需要我们自己简单组合一下就呈现出来,比如拖动排序!它对拓展简直是非常open(OCP原则),废话不扯了,先看效果:
说明:
1.实现这个效果的核心类是:ItemTouchHelper和ItemTouchHelper.Callbck.
2.mainActivity的布局就是一个recyclerview,item的布局cardview套着三个控件。
AAA: 首先看MAinActivity的代码吧
public class MainActivity extends AppCompatActivity {
List<String> list;
private MyAdapter adapter;
RecyclerView recyView;
//初始化集合数据
{
list = new ArrayList<>();
for (int i = 0; i < 10; i++) {
list.add("我的序号是:" + i);
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
recyView = (RecyclerView) findViewById(R.id.recyclerview);
adapter = new MyAdapter();
recyView.setLayoutManager(new LinearLayoutManager(this));
recyView.setAdapter(adapter);
//1.创建item helper
ItemTouchHelper itemTouchHelper = new ItemTouchHelper(callback);
//2.绑定到recyclerview上面去
itemTouchHelper.attachToRecyclerView(recyView);
//3.在ItemHelper的接口回调中过滤开启长按拖动,拓展其他操作
}
BBB: 下面是adapter的代码
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> {
List<String> list;
public MyAdapter(List<String> list) {
this.list=list;
}
@Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.item, parent, false);
MyViewHolder holder = new MyViewHolder(itemView);
return holder;
}
@Override
public int getItemCount() {
return list == null ? 0 : list.size();
}
@Override
public void onBindViewHolder(MyViewHolder holder, int position) {
holder.textNumber.setText(list.get(position));
}
class MyViewHolder extends RecyclerView.ViewHolder {
TextView textNumber;
public MyViewHolder(View itemView) {
super(itemView);
textNumber = (TextView) itemView.findViewById(R.id.textNumber);
}
}
}
CCC : 下面是itemTouchHelper的重写代码,并附有大量的注释,我是翻看了源码和谷歌官方文档,写得应该还是比较清楚的了,这个写好的itemTouchHelper代码直接复制过去是可以用的。
注意:目前实现的是item的长按拖动啊!!!
//itemHelper的回调
ItemTouchHelper.Callback callback = new ItemTouchHelper.Callback() {
/**
* 官方文档的说明如下:
* o control which actions user can take on each view, you should override getMovementFlags(RecyclerView, ViewHolder)
* and return appropriate set of direction flags. (LEFT, RIGHT, START, END, UP, DOWN).
* 返回我们要监控的方向,上下左右,我们做的是上下拖动,要返回都是UP和DOWN
* 关键坑爹的是下面方法返回值只有1个,也就是说只能监控一个方向。
* 不过点入到源码里面有惊喜。源码标记方向如下:
* public static final int UP = 1 0001
* public static final int DOWN = 1 << 1; (位运算:值其实就是2)0010
* public static final int LEFT = 1 << 2 左 值是3
* public static final int RIGHT = 1 << 3 右 值是8
*/
@Override
public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
//也就是说返回值是组合式的
//makeMovementFlags (int dragFlags, int swipeFlags),看下面的解释说明
int swipFlag=0;
//如果也监控左右方向的话,swipFlag=ItemTouchHelper.LEFT|ItemTouchHelper.RIGHT;
int dragflag = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
//等价于:0001&0010;多点触控标记触屏手指的顺序和个数也是这样标记哦
return makeMovementFlags(dragflag,swipFlag);
/**
* 备注:由getMovementFlags可以联想到setMovementFlags,不过文档么有这个方法,但是:
* 有 makeMovementFlags (int dragFlags, int swipeFlags)
* Convenience method to create movement flags.便捷方法创建moveMentFlag
* For instance, if you want to let your items be drag & dropped vertically and swiped left to be dismissed,
* you can call this method with: makeMovementFlags(UP | DOWN, LEFT);
* 这个recyclerview的文档写的简直完美,示例代码都弄好了!!!
* 如果你想让item上下拖动和左边滑动删除,应该这样用: makeMovementFlags(UP | DOWN, LEFT)
*/
//拓展一下:如果只想上下的话:makeMovementFlags(UP | DOWN, 0),标记方向的最小值1
}
/**
* 官方文档的说明如下
* If user drags an item, ItemTouchHelper will call onMove(recyclerView, dragged, target). Upon receiving this callback,
* you should move the item from the old position (dragged.getAdapterPosition()) to new position (target.getAdapterPosition())
* in your adapter and also call notifyItemMoved(int, int).
* 拖动某个item的回调,在return前要更新item位置,调用notifyItemMoved(draggedPosition,targetPosition)
* viewHolde:正在拖动item
* target:要拖到的目标
* @return true 表示消费事件
*/
@Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
//直接按照文档来操作啊,这文档写得太给力了,简直完美!
adapter.notifyItemMoved(viewHolder.getAdapterPosition(), target.getAdapterPosition());
//注意这里有个坑的,itemView 都移动了,对应的数据也要移动
Collections.swap(list, viewHolder.getAdapterPosition(), target.getAdapterPosition());
return true;
}
/**
* 谷歌官方文档说明如下:
* 这个看了一下主要是做左右拖动的回调
* When a View is swiped, ItemTouchHelper animates it until it goes out of bounds, then calls onSwiped(ViewHolder, int).
* At this point, you should update your adapter (e.g. remove the item) and call related Adapter#notify event.
* @param viewHolder
* @param direction
*/
@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
//暂不处理
}
/**
* 官方文档如下:返回true 当前tiem可以被拖动到目标位置后,直接”落“在target上,其他的上面的tiem跟着“落”,
* 所以要重写这个方法,不然只是拖动的tiem在动,target tiem不动,静止的
* Return true if the current ViewHolder can be dropped over the the target ViewHolder.
* @param recyclerView
* @param current
* @param target
* @return
*/
@Override
public boolean canDropOver(RecyclerView recyclerView, RecyclerView.ViewHolder current, RecyclerView.ViewHolder target) {
return true;
}
/**
* 官方文档说明如下:
* Returns whether ItemTouchHelper should start a drag and drop operation if an item is long pressed.
* 是否开启长按 拖动
* @return
*/
@Override
public boolean isLongPressDragEnabled() {
//return true后,可以实现长按拖动排序和拖动动画了
return true;
}
};
======================================================================================
上面实现item的长按拖动,有很多需求可能触摸item的某个控件就进行拖动。
先看改进后的效果图如下:
这个时候去找官方文档了。recyclerview真强大,要啥有啥。
itemTouchHelper有个方法 如下:
//谷歌官方文档如下:
//开启拖动我们给他的holder,但是默认
// 是长按拖动,直接代码调用拖动要先禁止长按拖动
//Starts dragging the provided ViewHolder. By default,
// ItemTouchHelper starts a drag when a View is long pressed.
// You can disable that behavior by overriding isLongPressDragEnabled().
itemTouchHelper.startDrag(holder);
由于adapter和activity是分离的两个类,所以用了接口回调,更新了adatper的代码和mainActivity的代码。
DDD: 接口的代码如下:
public interface startDragListener {
//触摸imageview,开启拖动的接口
void startDragItem(RecyclerView.ViewHolder holder);
}
EEE: 更新后的adapter的代码如下:
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> {
startDragListener draglistener;
//set接口回调
public void setDraglistener(startDragListener draglistener) {
this.draglistener = draglistener;
}
List<String> list;
public MyAdapter(List<String> list) {
this.list=list;
}
@Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.item, parent, false);
MyViewHolder holder = new MyViewHolder(itemView);
return holder;
}
@Override
public int getItemCount() {
return list == null ? 0 : list.size();
}
@Override
public void onBindViewHolder(final MyViewHolder holder, int position) {
holder.textNumber.setText(list.get(position));
holder.image.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
//注意:这里down和up都会回调该方法
if (event.getAction()== MotionEvent.ACTION_DOWN) {
draglistener.startDragItem(holder);
}
return false;
}
});
}
class MyViewHolder extends RecyclerView.ViewHolder {
TextView textNumber;
ImageView image;
public MyViewHolder(View itemView) {
super(itemView);
textNumber = (TextView) itemView.findViewById(R.id.textNumber);
image= (ImageView) itemView.findViewById(R.id.image);
}
}
}
FFF:更新后的mainactivity的代码如下:
public class MainActivity extends AppCompatActivity implements startDragListener{
List<String> list;
private MyAdapter adapter;
RecyclerView recyView;
private ItemTouchHelper itemTouchHelper;
//初始化集合数据
{
list = new ArrayList<>();
for (int i = 0; i < 10; i++) {
list.add("我的序号是:" + i);
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
recyView = (RecyclerView) findViewById(R.id.recyclerview);
adapter = new MyAdapter(list);
adapter.setDraglistener(this);
recyView.setLayoutManager(new LinearLayoutManager(this));
recyView.setAdapter(adapter);
//1.创建item helper
itemTouchHelper = new ItemTouchHelper(callback);
//2.绑定到recyclerview上面去
itemTouchHelper.attachToRecyclerView(recyView);
//3.在ItemHelper的接口回调中过滤开启长按拖动,拓展其他操作
}
//itemHelper的回调
ItemTouchHelper.Callback callback = new ItemTouchHelper.Callback() {
/**
* 官方文档的说明如下:
* o control which actions user can take on each view, you should override getMovementFlags(RecyclerView, ViewHolder)
* and return appropriate set of direction flags. (LEFT, RIGHT, START, END, UP, DOWN).
* 返回我们要监控的方向,上下左右,我们做的是上下拖动,要返回都是UP和DOWN
* 关键坑爹的是下面方法返回值只有1个,也就是说只能监控一个方向。
* 不过点入到源码里面有惊喜。源码标记方向如下:
* public static final int UP = 1 0001
* public static final int DOWN = 1 << 1; (位运算:值其实就是2)0010
* public static final int LEFT = 1 << 2 左 值是3
* public static final int RIGHT = 1 << 3 右 值是8
*/
@Override
public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
//也就是说返回值是组合式的
//makeMovementFlags (int dragFlags, int swipeFlags),看下面的解释说明
int swipFlag=0;
//如果也监控左右方向的话,swipFlag=ItemTouchHelper.LEFT|ItemTouchHelper.RIGHT;
int dragflag = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
//等价于:0001&0010;多点触控标记触屏手指的顺序和个数也是这样标记哦
return makeMovementFlags(dragflag,swipFlag);
/**
* 备注:由getMovementFlags可以联想到setMovementFlags,不过文档么有这个方法,但是:
* 有 makeMovementFlags (int dragFlags, int swipeFlags)
* Convenience method to create movement flags.便捷方法创建moveMentFlag
* For instance, if you want to let your items be drag & dropped vertically and swiped left to be dismissed,
* you can call this method with: makeMovementFlags(UP | DOWN, LEFT);
* 这个recyclerview的文档写的简直完美,示例代码都弄好了!!!
* 如果你想让item上下拖动和左边滑动删除,应该这样用: makeMovementFlags(UP | DOWN, LEFT)
*/
//拓展一下:如果只想上下的话:makeMovementFlags(UP | DOWN, 0),标记方向的最小值1
}
/**
* 官方文档的说明如下
* If user drags an item, ItemTouchHelper will call onMove(recyclerView, dragged, target). Upon receiving this callback,
* you should move the item from the old position (dragged.getAdapterPosition()) to new position (target.getAdapterPosition())
* in your adapter and also call notifyItemMoved(int, int).
* 拖动某个item的回调,在return前要更新item位置,调用notifyItemMoved(draggedPosition,targetPosition)
* viewHolde:正在拖动item
* target:要拖到的目标
* @return true 表示消费事件
*/
@Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
//直接按照文档来操作啊,这文档写得太给力了,简直完美!
adapter.notifyItemMoved(viewHolder.getAdapterPosition(), target.getAdapterPosition());
//注意这里有个坑的,itemView 都移动了,对应的数据也要移动
Collections.swap(list, viewHolder.getAdapterPosition(), target.getAdapterPosition());
return true;
}
/**
* 谷歌官方文档说明如下:
* 这个看了一下主要是做左右拖动的回调
* When a View is swiped, ItemTouchHelper animates it until it goes out of bounds, then calls onSwiped(ViewHolder, int).
* At this point, you should update your adapter (e.g. remove the item) and call related Adapter#notify event.
* @param viewHolder
* @param direction
*/
@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
//暂不处理
}
/**
* 官方文档如下:返回true 当前tiem可以被拖动到目标位置后,直接”落“在target上,其他的上面的tiem跟着“落”,
* 所以要重写这个方法,不然只是拖动的tiem在动,target tiem不动,静止的
* Return true if the current ViewHolder can be dropped over the the target ViewHolder.
* @param recyclerView
* @param current
* @param target
* @return
*/
@Override
public boolean canDropOver(RecyclerView recyclerView, RecyclerView.ViewHolder current, RecyclerView.ViewHolder target) {
return true;
}
/**
* 官方文档说明如下:
* Returns whether ItemTouchHelper should start a drag and drop operation if an item is long pressed.
* 是否开启长按 拖动
* @return
*/
@Override
public boolean isLongPressDragEnabled() {
//return true后,可以实现长按拖动排序和拖动动画了
return false;
}
};
@Override
public void startDragItem(RecyclerView.ViewHolder holder) {
//谷歌官方文档如下:
//开启拖动我们给他的holder,但是默认
// 是长按拖动,直接代码调用拖动要先禁止长按拖动
//Starts dragging the provided ViewHolder. By default,
// ItemTouchHelper starts a drag when a View is long pressed.
// You can disable that behavior by overriding isLongPressDragEnabled().
itemTouchHelper.startDrag(holder);
}
}
备注:1.看完这篇文章就可以重写侧滑删除,并有删除动画功能
2.写博客简直太耗费时间了
3.demo源码的下载链接