一、添加依賴
app.gradle裏添加依賴:
compile 'com.android.support:recyclerview-v7:26.+'
如果使用的是androidx:
implementation 'androidx.recyclerview:recyclerview:1.1.0'
二、基本使用方法
1、activity的xml佈局
就是寫一個RecyclerView的控件:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler_view"
android:scrollbars="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
2、列表的item佈局
隨意寫了一下,此處純展示功能,所以就寫了個TextView:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:background="#d4d2d2"
android:layout_height="wrap_content">
<TextView
android:id="@+id/tv_item"
android:layout_width="match_parent"
android:background="#FFFFFF"
android:layout_height="200dp"
android:layout_margin="10dp"
android:gravity="center"
android:textSize="22sp"
android:text="item"/>
</LinearLayout>
3、Adapter
public class InfoRecyclerViewAdapter extends RecyclerView.Adapter<InfoRecyclerViewAdapter.ViewHolder> {
private ArrayList<String> mData;
private Context mContext;//記錄一個Context,如果以後需要彈出個提示框這種的,也好用
/**
* 事件回調監聽
*/
private InfoRecyclerViewAdapter.OnItemClickListener onItemClickListener;
public InfoRecyclerViewAdapter(Context context,ArrayList<String> data) {
this.mContext = context;
this.mData = data;
}
public InfoRecyclerViewAdapter(Context context) {
this.mContext = context;
}
/**
* 新增數據:得到此次選擇的數據後,更新數據
*/
public void initData(ArrayList<String> data,int pageIndex) {
if (mData == null) {
mData = new ArrayList<>();
}
if(pageIndex == 0){//現在增加的是第一頁數據,則清除之前的數據
mData.clear();
}
if (data != null) {
int start = mData.size();
mData.addAll(data);
int count = mData.size() - start;
notifyItemRangeChanged(start, count);
}
}
/**
* 提供給外部獲取數據源的方法
*/
public List<String> getDatas() {
List<String> list = new ArrayList<>(mData);
return list;
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view;
// 實例化展示的view
view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_list_vertical, parent, false);
// 實例化viewholder
ViewHolder viewHolder = new ViewHolder(view);
return viewHolder;
}
@Override
public void onBindViewHolder(final ViewHolder holder, int position) {
// 綁定數據
holder.tvItem.setText(mData.get(position));
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(final View v) {
if(onItemClickListener != null) {
int pos = holder.getLayoutPosition();
onItemClickListener.onItemClick(holder.itemView, pos);
}
}
});
holder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
if(onItemClickListener != null) {
int pos = holder.getLayoutPosition();
onItemClickListener.onItemLongClick(holder.itemView, pos);
}
//表示此事件已經消費,不會觸發單擊事件
return true;
}
});
}
@Override
public int getItemViewType(int position) {
if(position == getItemCount() - 1){
return 1;
}else {
return 0;
}
}
@Override
public int getItemCount() {
return mData == null ? 0 : mData.size();
}
/**
* 往列表第一行新增一個item
*/
public void addNewItem() {
if(mData == null) {
mData = new ArrayList<>();
}
mData.add(0, "new Item");
notifyItemInserted(0);
}
/**
* 刪除item
*/
public void deleteItem(int position) {
if(mData == null || mData.isEmpty()) {
return;
}
if(position >= 0 && position < mData.size()){
mData.remove(position);
notifyItemRemoved(position);
}
}
/**
* 設置回調監聽
* @param listener
*/
public void setOnItemClickListener(InfoRecyclerViewAdapter.OnItemClickListener listener) {
this.onItemClickListener = listener;
}
public interface OnItemClickListener {
void onItemClick(View view, int position);
void onItemLongClick(View view, int position);
}
static class ViewHolder extends RecyclerView.ViewHolder {
@BindView(R.id.tv_item)
TextView tvItem;
ViewHolder(View view) {
super( view);
ButterKnife.bind(this, view);
}
}
}
activity往列表中新增數據:
ArrayList<String> mDataList= new ArrayList<>();//activity裏的數據源,獲取到數據後就傳到adapter裏更新列表
獲取到數據後就傳到adapter裏更新列表:
infoRecyclerViewAdapter.initData(mDataList);
// 由於Adapter內部是直接在首個Item位置做增加操作,增加完畢後列表移動到首個Item位置
infoLayoutManager.scrollToPosition(0);
刪除某個位置的item:
infoRecyclerViewAdapter.deleteItem(2);//比如我要刪除位置爲第三的item
// 刪除完畢後列表移動到Item位置
infoLayoutManager.scrollToPosition(2);
4、activity
//activity裏的數據源,獲取到數據後就傳到adapter裏更新列表
ArrayList<String> mDataList= new ArrayList<>();
網格列表(像GridView):
//豎直滑動,每行只顯示四個
GridLayoutManager infoLayoutManager = new GridLayoutManager( this,4);
//如果在初始化adapter的時候就已經獲取到了數據源可以在初始化adapter的時候就傳入數據源
InfoRecyclerViewAdapter infoRecyclerViewAdapter = new InfoRecyclerViewAdapter(this,mDataList);
//如果像是異步網絡請求數據後再刷新列表的話:
InfoRecyclerViewAdapter infoRecyclerViewAdapter = new InfoRecyclerViewAdapter(this);
//獲取到數據後就傳到adapter裏更新列表
infoRecyclerViewAdapter.initData(mDataList,0);
// 設置佈局管理器
infoRecyclerView.setLayoutManager(infoLayoutManager);
// 設置adapter
infoRecyclerView.setAdapter(infoRecyclerViewAdapter);
//設置間隔樣式
infoRecyclerView.addItemDecoration(new DividerGridItemDecoration(this));
垂直列表(像ListView):數據更新方式同上
infoLayoutManager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);
// 設置佈局管理器
infoRecyclerView.setLayoutManager(infoLayoutManager);
// 設置adapter
infoRecyclerViewAdapter = new InfoRecyclerViewAdapter(mDataList);
//item的點擊事件
infoRecyclerViewAdapter.setOnItemClickListener(new InfoRecyclerViewAdapter.OnItemClickListener() {
@Override
public void onItemClick(View view, int position) {
Toast.makeText(ActVerticalRecycler.this,"click " + position + " item", Toast.LENGTH_SHORT).show();
}
@Override
public void onItemLongClick(View view, int position) {
Toast.makeText(ActVerticalRecycler.this,"long click " + position + " item", Toast.LENGTH_SHORT).show();
}
});
infoRecyclerView.setAdapter(infoRecyclerViewAdapter);
//設置間隔樣式
// infoRecyclerView.addItemDecoration(new MyDividerItemDecoration(this, LinearLayoutManager.VERTICAL));
infoRecyclerView.addItemDecoration(new DividerGridItemDecoration(this));
// 設置Item添加和移除的動畫
infoRecyclerView.setItemAnimator(new DefaultItemAnimator());
水平列表:
// 設置佈局管理器
infoLayoutManager = new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false);
infoRecyclerView.setLayoutManager(infoLayoutManager);
// 設置adapter
infoRecyclerViewAdapter = new InfoRecyclerViewAdapter( mDataList );
infoRecyclerView.setAdapter(infoRecyclerViewAdapter);
//設置間隔樣式
// infoRecyclerView.addItemDecoration(new MyDividerItemDecoration(this, LinearLayoutManager.HORIZONTAL));
infoRecyclerView.addItemDecoration(new DividerGridItemDecoration(this));
設置列表item之間的間隔
//設置列表item之間的間隔
recycleView.addItemDecoration(new RecyclerView.ItemDecoration() {
@Override
public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
outRect.left = 2;
outRect.right = 2;
outRect.top = 2;
outRect.bottom = 2;
// super.getItemOffsets(outRect, view, parent, state);
}
});
三、 長按拖動
/**
* 長按拖拽
*/
public class ItemDragHelperCallBack extends ItemTouchHelper.Callback {
private OnItemDragListener onItemDragListener;
public ItemDragHelperCallBack(OnItemDragListener onItemDragListener) {
this.onItemDragListener = onItemDragListener;
}
/**
* 返回可以滑動的方向
*
* @param recyclerView
* @param viewHolder
* @return
*/
@Override
public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
RecyclerView.LayoutManager manager = recyclerView.getLayoutManager();
int dragFlags;
if (manager instanceof GridLayoutManager || manager instanceof StaggeredGridLayoutManager) {
//網格佈局管理器允許上下左右拖動
dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN | ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;
} else {
//其他佈局管理器允許上下拖動
dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
}
return makeMovementFlags(dragFlags, 0);
}
/**
* 拖拽到新位置時候的回調方法
*
* @param recyclerView
* @param viewHolder
* @param target
* @return
*/
@Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
//不同Type之間不允許移動
if (viewHolder.getItemViewType() != target.getItemViewType()) {
return false;
}
if (onItemDragListener != null) {
onItemDragListener.onItemMove(viewHolder.getAdapterPosition(), target.getAdapterPosition());
}
return true;
}
/**
* 當用戶左右滑動的時候執行的方法
*
* @param viewHolder
* @param direction
*/
@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
}
/**
* 重寫拖拽不可用
* @return
*/
@Override
public boolean isLongPressDragEnabled() {
return false;
}
public void setOnItemDragListeber(OnItemDragListener onItemDragListener) {
this.onItemDragListener = onItemDragListener;
}
public interface OnItemDragListener {
void onItemMove(int startPos,int endPos);
}
}
activity中使用:
mItemTouchHelper = new ItemTouchHelper(new ItemDragHelperCallBack(new ItemDragHelperCallBack.OnItemDragListener() {
@Override
public void onItemMove(int startPos, int endPos) {
//交換變換位置的集合數據並刷新
Collections.swap(mAdapter.getDatas(), startPos, endPos);
mAdapter.notifyItemMoved(startPos, endPos);
}
}));
//關聯RecyclerView
mItemTouchHelper.attachToRecyclerView(recyclerView);
四、滑動衝突
1、ScrollView嵌套RecycleView
xml佈局:
<ScrollView
android:layout_below="@+id/title_bar"
android:scrollbars="none"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:paddingTop="10dp"
android:paddingBottom="10dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_marginTop="20dp"
android:scrollbars="none"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
</ScrollView>
代碼設置:
GridLayoutManager gridLayoutManager = new GridLayoutManager(mContext,4){
@Override
public boolean canScrollVertically() {
//解決ScrollView裏存在多個RecyclerView時滑動卡頓的問題
return false;
}
};
recyclerView.setLayoutManager(gridLayoutManager);
//解決數據加載不完的問題
recyclerView.setNestedScrollingEnabled(false);
recyclerView.setHasFixedSize(true);
//解決數據加載完成後, 沒有停留在頂部的問題
recyclerView.setFocusable(false);
Android在開發中的使用技巧之解決ScrollView嵌套RecyclerView出現的系列問題 - 簡書
五、recyclerview的item位序
1、Recyclerview.getLayoutPosition()問題
使用Recyclerview 時,如果要添加item的點擊監聽等功能,可以在Recyclerview.Adapter的onBindViewHolder中設置:
@Override
public void onBindViewHolder(final MyViewHolder holder, int position) {
holder.tv.setHeight(150*(1+position%4));
holder.tv.setWidth(150*(1+position%4));
holder.tv.setText(data.get(position));
if(mOnItemClickListener!=null){
holder.tv.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int pos=holder.getLayoutPosition();
mOnItemClickListener.onItemClick(v,pos);
}
});
holder.tv.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
int pos=holder.getLayoutPosition();
mOnItemClickListener.onItemLongClick(v,pos);
return false;
}
});
}
}
注意這裏使用了ViewHolder的getLayoutPosition方法,此方法返回的pos值與onBindViewHolder方法傳入的position值有可能不同。
根據SDK中的解釋,在Recyclerview 進行添加、移除item等操作時,position位置可能會變化,而所有的adapter的刷新並不總是及時的,只有這個方法返回的纔是當前item經過一些變換後所處的真正位置。
getLayoutPosition:返回佈局中最新的計算位置,和用戶所見到的位置一致,當做用戶輸入(例如點擊事件)的時候考慮使用
getAdapterPosition:返回數據在Adapter中的位置(也許位置的變化還未來得及刷新到佈局中),當使用Adapter的時候(例如調用Adapter的notify相關方法時)考慮使用
六、分割線
1、ItemDecoration樣式drawable
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle" >
<gradient
android:centerColor="#ff00ff00"
android:endColor="#ff0000ff"
android:startColor="#ffff0000"
android:type="linear" />
<size android:height="4dp"/>
</shape>
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
<item name= "android:listDivider">@drawable/bg_divider </item >
</style>
2、ItemDecoration
public class DividerGridItemDecoration extends RecyclerView.ItemDecoration {
private static final int[] ATTRS = new int[]{android.R.attr.listDivider};
private Drawable mDivider;
public DividerGridItemDecoration(Context context) {
final TypedArray a = context.obtainStyledAttributes(ATTRS);
mDivider = a.getDrawable(0);
a.recycle();
}
@Override
public void onDraw(Canvas c, RecyclerView parent, State state) {
drawHorizontal(c, parent);
drawVertical(c, parent);
}
/**
* 繪製水平分割線
*/
public void drawHorizontal(Canvas c, RecyclerView parent) {
int childCount = parent.getChildCount();//item總個數
for (int i = 0; i < childCount; i++) {
final View child = parent.getChildAt(i);
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
final int left = child.getLeft() - params.leftMargin;
final int right = child.getRight() + params.rightMargin
+ mDivider.getIntrinsicWidth();
final int top = child.getBottom() + params.bottomMargin;
final int bottom = top + mDivider.getIntrinsicHeight();
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(c);
}
}
/**
* 繪製垂直分割線
*/
public void drawVertical(Canvas c, RecyclerView parent) {
final int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
final View child = parent.getChildAt(i);
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
final int top = child.getTop() - params.topMargin;
final int bottom = child.getBottom() + params.bottomMargin;
final int left = child.getRight() + params.rightMargin;
final int right = left + mDivider.getIntrinsicWidth();
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(c);
}
}
/**
* 列數
*/
private int getSpanCount(RecyclerView parent) {
int spanCount = -1;
LayoutManager layoutManager = parent.getLayoutManager();
if (layoutManager instanceof GridLayoutManager) {
spanCount = ((GridLayoutManager) layoutManager).getSpanCount();
} else if (layoutManager instanceof StaggeredGridLayoutManager) {
spanCount = ((StaggeredGridLayoutManager) layoutManager).getSpanCount();
}
return spanCount;
}
/**
* 是否是最後一列
*/
private boolean isLastColum(RecyclerView parent, int pos, int spanCount,
int childCount) {
LayoutManager layoutManager = parent.getLayoutManager();
if (layoutManager instanceof GridLayoutManager) {
if ((pos + 1) % spanCount == 0) // 如果是最後一列,則不需要繪製右邊
{
return true;
}
} else if (layoutManager instanceof StaggeredGridLayoutManager) {
int orientation = ((StaggeredGridLayoutManager) layoutManager)
.getOrientation();
if (orientation == StaggeredGridLayoutManager.VERTICAL) {
if ((pos + 1) % spanCount == 0) // 如果是最後一列,則不需要繪製右邊
{
return true;
}
} else {
childCount = childCount - childCount % spanCount;
if (pos >= childCount) // 如果是最後一列,則不需要繪製右邊
return true;
}
}
return false;
}
/**
* 是否是最後一行
* @param parent
* @param pos 當前item的位序
* @param spanCount 列數
* @param childCount item總個數
* @return
*/
private boolean isLastRaw(RecyclerView parent, int pos, int spanCount,
int childCount) {
LayoutManager layoutManager = parent.getLayoutManager();
if (layoutManager instanceof GridLayoutManager) {
childCount = childCount - childCount % spanCount;
// 如果是最後一行,則不需要繪製底部
if (pos >= childCount){
return true;
}
} else if (layoutManager instanceof StaggeredGridLayoutManager) {
int orientation = ((StaggeredGridLayoutManager) layoutManager)
.getOrientation();
// StaggeredGridLayoutManager 且縱向滾動
if (orientation == StaggeredGridLayoutManager.VERTICAL) {
childCount = childCount - childCount % spanCount;
// 如果是最後一行,則不需要繪製底部
if (pos >= childCount){
return true;
}
} else {
// StaggeredGridLayoutManager 且橫向滾動
// 如果是最後一行,則不需要繪製底部
if ((pos + 1) % spanCount == 0) {
return true;
}
}
}
return false;
}
@Override
public void getItemOffsets(Rect outRect, int itemPosition,
RecyclerView parent) {
int spanCount = getSpanCount(parent);//列數
int childCount = parent.getAdapter().getItemCount();//item總個數
if (isLastRaw(parent, itemPosition, spanCount, childCount))// 如果是最後一行,則不需要繪製底部
{
outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0);
} else if (isLastColum(parent, itemPosition, spanCount, childCount))// 如果是最後一列,則不需要繪製右邊
{
outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());
} else {
outRect.set(0, 0, mDivider.getIntrinsicWidth(),
mDivider.getIntrinsicHeight());
}
}
}