RecyclerView的簡單用法我們已經再熟悉不過了,這裏就不多說了,下面主要說一下RecyclerView比較好用的一些功能,先看下效果圖
下面我們將對這些功能進行逐一實現
一、添加分隔線
我們知道ListView添加分隔線是一件很簡單的事情,我們添加以下代碼就可以:
android:divider="@color/colorAccent"
android:dividerHeight="2dp"
但是我們使用RecyclerView添加這些對我們是一點用都沒有的。然而RecyclerView提供了更加高級的用法:
addItemDecoration()
有了這個方法我們就可以定製自己的分隔線,我們只要實現RecyclerView.ItemDecoration
下的兩個方法
getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state)
和
onDraw(Canvas canvas, RecyclerView parent, RecyclerView.State state)
我們的RecyclerView的ItemView周圍實際上可以用下圖表示
綠色的部分就是我們的itemView,橙色的部分就是我們的outRect,我們給outRect設置left,top,right,bottom實際上就是設置我們的itemView與其他控件的偏移量,我們這裏想設置分隔線,只需要給我們的outRect設置outRect.top=1px,留出1px的間距供我們去繪製分隔線。
最後在onDraw方法中計算出分隔線矩形的left,top,right和bottom繪製出來就可以了
我們這裏同時處理了LinearLayoutManager和GridLayoutManager的分隔線,LinearLayoutManager的分隔線比較簡單,GridLayoutManger我們需要考慮到最後一列不需要繪製右側分隔線,最後一行不需要繪製底部分隔線,我們判斷是否是右側或者左側代碼如下:
/**
* 是否是最後一行
* @param parent
* @param position
* @return
*/
public boolean isLastRow(RecyclerView parent,int position){
int itemCount = parent.getAdapter().getItemCount(); //itemView的總數
GridLayoutManager gridLayoutManager = (GridLayoutManager) parent.getLayoutManager();
int spanCount = gridLayoutManager.getSpanCount(); //gridLayoutManager有多少列
itemCount = itemCount - itemCount % spanCount;
if(position>=itemCount)
return true;
else
return false;
}
/**
* 是否是最後一列
* @param parent
* @param position
* @return
*/
public boolean isLastColunm(RecyclerView parent,int position){
GridLayoutManager gridLayoutManager = (GridLayoutManager) parent.getLayoutManager();
int spanCount = gridLayoutManager.getSpanCount();
if((position+1) % spanCount == 0)
return true;
else
return false;
}
具體的計算和繪製的過程:(其實分隔線就是繪製一個矩形就)
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
super.getItemOffsets(outRect, view, parent, state);
int position = parent.getChildAdapterPosition(view); //根據itemView獲取在RecyclerView中的位置
RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
if (layoutManager instanceof GridLayoutManager) { //網格佈局
if (isLastColunm(parent,position)) {
outRect.bottom = (int) dividerHeight; //是最後一列的話,只繪製底部的分隔線
} else if(isLastRow(parent,position)){
outRect.right = (int) dividerHeight; //是最後一行的話,只繪製右側的分隔線
}else{
outRect.bottom = (int) dividerHeight;
outRect.right = (int) dividerHeight;
}
} else { //線性佈局
if (position != 0) {
outRect.top = (int) dividerHeight;
}
}
}
@Override
public void onDraw(Canvas canvas, RecyclerView parent, RecyclerView.State state) {
super.onDraw(canvas, parent, state);
int childCount = parent.getChildCount();
if (parent.getLayoutManager() instanceof GridLayoutManager) {
for (int i = 0; i < childCount; i++) {
View childView = parent.getChildAt(i);
int left = childView.getLeft();
int top = childView.getTop();
int right = childView.getRight();
int bottom = childView.getBottom();
int childAdapterPosition = parent.getChildAdapterPosition(childView);
if (isLastColunm(parent,childAdapterPosition)) { //最後一列的分隔線
canvas.drawRect(left, bottom, right, bottom+dividerHeight, mPaint);
} else if(isLastRow(parent,childAdapterPosition)){ //最後一行的分隔線
canvas.drawRect(right, top, right+dividerHeight, bottom, mPaint);
}else{
canvas.drawRect(left, bottom, right+dividerHeight, bottom+dividerHeight, mPaint);
canvas.drawRect(right, top, right+dividerHeight, bottom, mPaint);
}
}
} else {
//繪製線性佈局的itemView之間的分隔線
for (int i = 0; i < childCount; i++) {
View itemView = parent.getChildAt(i);
int childAdapterPosition = parent.getChildAdapterPosition(itemView);
if (childAdapterPosition == 0) { //如果是第一條,不繪製分割線
continue;
}
int dividerLeft = parent.getPaddingLeft();
int dividerTop = (int) (itemView.getTop() - dividerHeight);
int dividerRight = parent.getWidth() - parent.getPaddingRight();
int dividerBottom = itemView.getTop();
canvas.drawRect(dividerLeft, dividerTop, dividerRight, dividerBottom, mPaint);
}
}
}
二、添加點擊和長按
RecyclerView沒有提供類似setOnItemCickListener這樣的接口來供我們實現單擊itemView的操作。RecyclerView提供了addOnItemTouchListener,我們可以實現RecyclerView.OnItemTouchListener這個接口中的兩個方法
onInterceptTouchEvent(RecyclerView rv, MotionEvent e)
onTouchEvent(RecyclerView rv, MotionEvent e)
熟悉View事件分發機制的,對這兩個方法應該都不陌生,我們可以攔截我們的View事件,然後交由GestureDetectorCompat手勢識別類進行處理我們的手勢,然後根據單擊和長按分別調用我們定義的接口回調方法:
//RecyclerView條目的單擊
public abstract void onItemClick(RecyclerView.ViewHolder holder);
//RecyclerView的長按
public abstract void onItemLongPress(RecyclerView.ViewHolder holder);
這樣我們就輕鬆的實現了RecyclerView的單擊和長按操作
詳細代碼如下:
@Override
public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
mGestureDetectorCompat.onTouchEvent(e);
return false;
}
@Override
public void onTouchEvent(RecyclerView rv, MotionEvent e) {
mGestureDetectorCompat.onTouchEvent(e);
}
@Override
public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
}
public class OnItemGestureListener extends GestureDetector.SimpleOnGestureListener {
@Override
public boolean onSingleTapUp(MotionEvent e) {
//findChildViewUnder根據點擊的x,y座標找到點擊的位置所屬的view
View childViewUnder = mRecyclerView.findChildViewUnder(e.getX(), e.getY());
if(childViewUnder!=null){
//找到RecyclerView點擊x,y位置的View的ViewHolder
RecyclerView.ViewHolder viewHolder = mRecyclerView.getChildViewHolder(childViewUnder);
onItemClick(viewHolder);
}
return true;
}
@Override
public void onLongPress(MotionEvent e) {
View childViewUnder = mRecyclerView.findChildViewUnder(e.getX(), e.getY());
if(childViewUnder!=null){
RecyclerView.ViewHolder childViewHolder = mRecyclerView.getChildViewHolder(childViewUnder);
onItemLongPress(childViewHolder);
}
}
}
這其中
findChildViewUnder
根據我們按下的座標位置找到我們RecyclerView中的具體某一個itemView
getChildViewHolder
根據我們的ItemView找到相應的ViewHolder三、拖拽移動、側滑刪除(我們這裏同時實現了線性和網格佈局的拖動,線性的側滑刪除)
RecyclerView實現側滑刪除是一件非常容易的事情,ItemTouchHelper就是用來輔助RecyclerView來實現各種刪除側滑操作的。我們的只要繼承ItemTouchHelper.Callback並實現其中的個方法:
(1)拖拽移動需要我們實現下面兩個方法
a:
getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder)
我們在長按ItemView的時候會調用這個方法,我們可以在這個方法裏面定義我們LinearLayoutManager和GridLayoutManager的移動的方法
ItemTouchHelper.UP
ItemTouchHelper.DOWN
ItemTouchHelper.LEFT
ItemTouchHelper.RIGHT
滑動的方向
ItemTouchHelper.START
ItemTouchHelper.END
然後調用makeMovementFlags設置我們移動和滑動的方向
/**
* 當長按itemView移動的時候調用這個方法
* @param recyclerView
* @param viewHolder
* @return
*/
@Override
public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
if (recyclerView.getLayoutManager() instanceof GridLayoutManager) { //因爲GridLayout有上下左右四個方向,所以dragFlags設置爲上下左右
int dragFlags = android.support.v7.widget.helper.ItemTouchHelper.UP | android.support.v7.widget.helper.ItemTouchHelper.DOWN
| android.support.v7.widget.helper.ItemTouchHelper.LEFT | android.support.v7.widget.helper.ItemTouchHelper.RIGHT;
int swipeFlags = 0; //設置爲零表示不能夠側滑刪除
return makeMovementFlags(dragFlags, swipeFlags);
} else {
int dragFlags = android.support.v7.widget.helper.ItemTouchHelper.UP | android.support.v7.widget.helper.ItemTouchHelper.DOWN; //線性佈局只要上下兩個方向
int swipeFlags = ItemTouchHelper.START | ItemTouchHelper.END;
return makeMovementFlags(dragFlags, swipeFlags);
}
}
b:
onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target)
當我們長按移動ItemView的時候會不斷的走這個方法,在這裏我們可以根據長按移動交換itemView的位置,具體實現如下:
/**
* 長按移動itemView的時候調用這個方法
* @param recyclerView
* @param viewHolder
* @param target
* @return
*/
@Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
int fromPosition = viewHolder.getAdapterPosition(); //按下的位置
int targetPosition = target.getAdapterPosition(); //移動到的位置
if(recyclerView.getLayoutManager() instanceof GridLayoutManager){ //如果是GridLayoutManager,在第0位不能拖動和被覆蓋掉
if (targetPosition == 0) {
return false;
}
}
if (fromPosition < targetPosition) {
for (int i = fromPosition; i < targetPosition; i++) {
Collections.swap(mAdapter.getDataList(), i, i + 1);
}
} else {
for (int i = fromPosition; i > targetPosition; i--) {
Collections.swap(mAdapter.getDataList(), i, i - 1);
}
}
mAdapter.notifyItemMoved(fromPosition, targetPosition);
return true;
}
(2)側滑刪除需要我們實現下面這個方法
onSwiped(RecyclerView.ViewHolder viewHolder, int direction)
具體實現
/**
* 側滑刪除的時候調用這個方法
* @param viewHolder
* @param direction
*/
@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
int adapterPosition = viewHolder.getAdapterPosition();
mAdapter.notifyItemRemoved(adapterPosition);
mAdapter.getDataList().remove(adapterPosition);
}
參考鏈接:
http://www.10tiao.com/html/227/201705/2650239745/1.html
https://mp.weixin.qq.com/s/LsxYOGnp6Yq_9rFHUDAL9A