RecyclerView 是 Android 提供的一個強大的列表控件,用來顯示大量數據。
RecyclerView 的主要特點
1. 高性能的視圖復用機制
Recycle就是循環的意思,那么recycleview的特點也很鮮明了,它只會創建出在屏幕內和一定緩存的itemview,當view滑出屏幕時,不會銷毀重建,而是復用舊的view,可以極高的提高性能;
2. 高度可定制
可以添加分割線、動畫、拖拽、滑動刪除;
3. 支持多種布局
支持線性、網格、瀑布流等布局實現;
子項的點擊事件和長按點擊事件
總體思路:因為我們在適配器的viewholder
中可以拿到每個子項的view
的,此時可以設置監聽調用Onclick
方法或者OnLongClick
方法,然后通過接口回調的方式在RecycleView
中設置回調的邏輯處理;
那么為什么要通過接口回調的方式呢?是因為適配器的任務就是綁定數據后顯示UI
,它不應該決定點擊事件后的邏輯處理,所以在適配器中實現子項的監聽,但是真正的實現我們放到Activity
或者Fragment
中,這樣有利于解耦;
接下來看看具體的實現:
在適配器中:
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);
}@Overridepublic void onClick(View view) {if(onItemClickListener!= null){onItemClickListener.onItemClick(cityadapt.this,getAdapterPosition());}}@Overridepublic boolean onLongClick(View view) {return true;}
}
在實現類中
cityadapt1.setOnItemClickListener(new cityadapt.OnItemClickListener() {@Overridepublic void onItemClick(cityadapt cityadapt, int position) {viewPager2.setCurrentItem(position,true);}@Overridepublic void onItemLongClick(cityadapt cityadapt, int position) {}
});
在進行子項的刪除和拖動的學習之前,得先了解一個類:ItemTouchHelper
ItemTouchHelper
-
ItemTouchHelper
是 Android 官方提供的一個 RecyclerView 輔助類。 -
它通過 監聽手勢事件,幫助我們輕松實現:
Item 拖動(上下或左右移動)
Item 滑動(左滑/右滑刪除,或執行其他操作) -
不需要自己去寫復雜的手勢檢測邏輯。
我理解的本質也是接口回調,我們只定義接口的實現,然后作為構造方法的參數傳入ItemTouchHelper
的實例中,最后與RecycleView綁定;
在這里我們定義回調后的實現,比如允許滑動的方向等等;
ItemTouchHelper.Callback callback = new ItemTouchHelper.Callback() {
}
然后通過ItemTouchHelper
的實例傳入,使得后續做出自定義的邏輯判斷和反應等;
ItemTouchHelper itemTouchHelper = new ItemTouchHelper(callback);
最后通過attachToRecyclerView
與recycleview綁定,binding.recyCity
是recycleview的實例;
itemTouchHelper.attachToRecyclerView(binding.recyCity);
在ItemTouchHelper.Callback
中有必須實現的三個抽象方法:
-
getMovementFlags()
:用來指定支持的拖拽和滑動方向 -
onMove()
:拖拽時回調(交換兩個 item 的位置) -
onSwiped()
:側滑時回調(比如刪除 item)
在本模塊中介紹一下`getMovementFlags()
//規定可以滑動的方向
@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)
,參數有兩個,第一個是關于允許上下滑動或拖拽的,第二個是關于上下的;
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
的基礎上,我們還需重寫一個方法:
//是否允許滑動移除@Overridepublic boolean isItemViewSwipeEnabled() {return true;}
然后在ItemTouchHelper
中的onSwiped
方法內規定邏輯;
//規定左滑后的邏輯@Overridepublic 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)
:通過接口回調的方式,自定義實現刪除的邏輯;
當然我們也可以定義刪除的動畫
子項刪除事件的動畫效果
代碼如下:
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代表設置整張圖片),以及你要放置的位置,設置透明度等;
子項的拖動事件
重寫此方法:
//是否允許長按拖拽
@Override
public boolean isLongPressDragEnabled() {return true;
}
此處跟刪除的邏輯差不多,解釋放在注釋中,可自行觀看;
@Overridepublic 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;}
這樣我們就可以實現子項的點擊事件,刪除,拖動啦;
–未完待續–