RecyclerView 是 Android 提供的一个强大的列表控件,用来显示大量数据。
RecyclerView 的主要特点
1. 高性能的视图复用机制
Recycle就是循环的意思,那么recycleview的特点也很鲜明了,它只会创建出在屏幕内和一定缓存的itemview,当view滑出屏幕时,不会销毁重建,而是复用旧的view,可以极高的提高性能;
2. 高度可定制
可以添加分割线、动画、拖拽、滑动删除;
3. 支持多种布局
支持线性、网格、瀑布流等布局实现;
子项的点击事件和长按点击事件
总体思路:因为我们在适配器的viewholder
中可以拿到每个子项的view
的,此时可以设置监听调用Onclick
方法或者OnLongClick
方法,然后通过接口回调的方式在RecycleView
中设置回调的逻辑处理;
那么为什么要通过接口回调的方式呢?是因为适配器的任务就是绑定数据后显示UI
,它不应该决定点击事件后的逻辑处理,所以在适配器中实现子项的监听,但是真正的实现我们放到Activity
或者Fragment
中,这样有利于解耦;
接下来看看具体的实现:
在适配器中:
Android
private OnItemClickListener onItemClickListener;
public interface OnItemClickListener{
void onItemClick(cityadapt cityadapt,int position);
void onItemLongClick(cityadapt cityadapt,int position);
}
public void setOnItemClickListener(OnItemClickListener onItemClickListener){
this.onItemClickListener = onItemClickListener;
}
//实现接口,使得调用适配器中的Onclick的方法,然后再进行接口回调
public class cityviewholder extends RecyclerView.ViewHolder implements View.OnClickListener, View.OnLongClickListener {
public cityviewholder(@NonNull View itemView) {
super(itemView);
itemView.setOnClickListener(this);
}
@Override
public void onClick(View view) {
if(onItemClickListener!= null){
onItemClickListener.onItemClick(cityadapt.this,getAdapterPosition());
}
}
@Override
public boolean onLongClick(View view) {
return true;
}
}
在实现类中
Android
cityadapt1.setOnItemClickListener(new cityadapt.OnItemClickListener() {
@Override
public void onItemClick(cityadapt cityadapt, int position) {
viewPager2.setCurrentItem(position,true);
}
@Override
public void onItemLongClick(cityadapt cityadapt, int position) {
}
});
在进行子项的删除和拖动的学习之前,得先了解一个类:ItemTouchHelper
ItemTouchHelper
-
ItemTouchHelper
是 Android 官方提供的一个 RecyclerView 辅助类。 -
它通过 监听手势事件 ,帮助我们轻松实现:
Item 拖动(上下或左右移动)
Item 滑动(左滑/右滑删除,或执行其他操作)
-
不需要自己去写复杂的手势检测逻辑。
我理解的本质也是接口回调,我们只定义接口的实现,然后作为构造方法的参数传入ItemTouchHelper
的实例中,最后与RecycleView绑定;
在这里我们定义回调后的实现,比如允许滑动的方向等等;
Android
ItemTouchHelper.Callback callback = new ItemTouchHelper.Callback() {
}
然后通过ItemTouchHelper
的实例传入,使得后续做出自定义的逻辑判断和反应等;
Android
ItemTouchHelper itemTouchHelper = new ItemTouchHelper(callback);
最后通过attachToRecyclerView
与recycleview绑定,binding.recyCity
是recycleview的实例;
Android
itemTouchHelper.attachToRecyclerView(binding.recyCity);
在ItemTouchHelper.Callback
中有必须实现的三个抽象方法:
-
getMovementFlags()
:用来指定支持的拖拽和滑动方向 -
onMove()
:拖拽时回调(交换两个 item 的位置) -
onSwiped()
:侧滑时回调(比如删除 item)
在本模块中介绍一下`getMovementFlags()
Android
//规定可以滑动的方向
@Override
public int getMovementFlags(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) {
int position = viewHolder.getAdapterPosition();
if(position == 0){
return 0;
}
int ver = 0;
int hor = ItemTouchHelper.LEFT;
return makeMovementFlags(ver,hor);
}
- 最后返回一个方法
makeMovementFlags(ver,hor)
,参数有两个,第一个是关于允许上下滑动或拖拽的,第二个是关于上下的;
Android
int position = viewHolder.getAdapterPosition();
if(position == 0){
return 0;
}
这里这么写得到当前滑动或者拖动的位置,如果这个是recycleview中第一个的话,就返回0,意味着不允许滑动或者拖拽;
ItemTouchHelper.LEFT
:ItemTouchHelper.LEFT
是ItemTouchHelper
里定义的一个常量,代表滑动(swipe)时允许的方向之一;
剩余的其他:
ItemTouchHelper.UP // 向上拖动
ItemTouchHelper.DOWN // 向下拖动
ItemTouchHelper.LEFT // 向左滑动
ItemTouchHelper.RIGHT // 向右滑动
子项的删除事件
在ItemTouchHelper
的基础上,我们还需重写一个方法:
Android
//是否允许滑动移除
@Override
public boolean isItemViewSwipeEnabled() {
return true;
}
然后在ItemTouchHelper
中的onSwiped
方法内规定逻辑;
Android
//规定左滑后的逻辑
@Override
public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) {
int i = viewHolder.getAdapterPosition();
if(i != 0){
cityadapt1.getData().remove(i);
cityadapt1.notifyItemRemoved(i);
// MainActivity.deleteFragment(i);
addtomainweather.deleteFragment(i);
}
}
里面有三个方法:
-
cityadapt1.getData().remove(i)
: 把适配器中持有的数据源里对应的 item 移除; -
cityadapt1.notifyItemRemoved(i)
:触发删除的动画; -
addtomainweather.deleteFragment(i)
:通过接口回调的方式,自定义实现删除的逻辑;
当然我们也可以定义删除的动画
子项删除事件的动画效果
代码如下:
Android
public void onChildDraw(@NonNull Canvas c, @NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
if(actionState == ItemTouchHelper.ACTION_STATE_SWIPE){
View view = viewHolder.itemView;
Paint paint = new Paint();
paint.setColor(Color.WHITE);
if(dX < 0){
c.drawRect((float) view.getRight()+dX,(float) view.getTop(),(float) view.getRight(),(float) view.getBottom(),paint);
Bitmap icon = BitmapFactory.decodeResource(recyclerView.getContext().getResources(),R.drawable.deletecity);
int iconmargin =( view.getHeight() - icon.getHeight())/2;
int top = view.getTop()+iconmargin;
int bottom = top + icon.getHeight();
int left = view.getRight() - iconmargin - icon.getWidth();
int right = view.getRight() - iconmargin;
c.drawBitmap(icon,null,new Rect(left,top,right,bottom),null);
}
}
}
这个方法是 ItemTouchHelper.Callback
提供的 onChildDraw ,用来在 拖动/滑动时绘制额外的 UI 效果(比如背景、按钮、图标;
先看看这个方法对应的参数:
@NonNull Canvas c
, // 画布
@NonNull RecyclerView recyclerView
, // 当前的 RecyclerView
@NonNull RecyclerView.ViewHolder viewHolder
, // 当前正在交互的 item
float dX,
// X 方向的位移
float dY,
// Y 方向的位移
int actionState,
// 当前手势状态(滑动/拖动)
boolean isCurrentlyActive
// 手指是否还在按住 item
代码中的部分说明:
actionState == ItemTouchHelper.ACTION_STATE_SWIPE
是如果手势是滑动的话;
其他的滑动:
-
ItemTouchHelper.ACTION_STATE_IDLE
→ 没有操作 -
ItemTouchHelper.ACTION_STATE_DRAG
→ 拖动中
View view = viewHolder.itemView
;获得当前的item;
c.drawRect((float) view.getRight()+dX,(float) view.getTop(),(float) view.getRight(),(float) view.getBottom(),paint);
:里面有五个参数,分别是规定滑动后矩形的左边界,矩形的上边界,矩形的右边界,矩形的下边界,以及绘制的颜色/样式;
Bitmap icon = BitmapFactory.decodeResource(recyclerView.getContext().getResources(),R.drawable.deletecity);``recyclerView.getContext().getResources()
,得到文件资源管理器,得到对应的图片等; BitmapFactory.decodeResource(Resources res, int id)
,BitmapFactory
是一个解码工具类,这个方法会读取资源文件,并转成内存中的 Bitmap 对象;
c.drawBitmap(icon,null,new Rect(left,top,right,bottom),null);
四个参数分别代表:设置到画布上的bitmap对象,源区域(null代表设置整张图片),以及你要放置的位置,设置透明度等;
子项的拖动事件
重写此方法:
Android
//是否允许长按拖拽
@Override
public boolean isLongPressDragEnabled() {
return true;
}
此处跟删除的逻辑差不多,解释放在注释中,可自行观看;
Android
@Override
public boolean onMove(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, @NonNull RecyclerView.ViewHolder target) {
//返回当前正在拖动的适配器中的数据索引
int i = viewHolder.getAdapterPosition();
//返回目标适配器中的数据索引
int j = target.getAdapterPosition();
//把适配器中持有的数据源里对应的 item 交换;
Collections.swap(cityadapt1.getData(),i,j);
//刷新item移动动画
cityadapt1.notifyItemMoved(i,j);
addtomainweather.swapFragment(i,j);
return true;
}
这样我们就可以实现子项的点击事件,删除,拖动啦; --未完待续--