文章目录
- 一、导包
- 二、基本使用
- 三、设置分割线
- 四、自定义点击事件
- 五、实现 GridView
- 六、实现瀑布流
- 七、更多效果
- 八、RecyclerView 常见问题
- 九、RecyclerView 和 ScrollView 嵌套的问题
一、导包
implementation 'androidx.recyclerview:recyclerview:1.1.0'
二、基本使用
2.1、首先是两个布局文件
Activity 的布局文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"><androidx.recyclerview.widget.RecyclerViewandroid:id="@+id/recyclerView"android:layout_width="match_parent"android:layout_height="wrap_content" />
</LinearLayout>
item 的布局文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="vertical"><TextViewandroid:id="@+id/tvItem"android:layout_width="match_parent"android:layout_height="50dp"android:gravity="center" /></LinearLayout>
2.2、Adapter 代码
public class HomeAdapter extends RecyclerView.Adapter<HomeAdapter.MyViewHolder> {private List<String> mList;private Context mContext;public HomeAdapter(Context mContext, List<String> mList) {this.mContext = mContext;this.mList = mList;}public void removeData(int position) {mList.remove(position);notifyDataSetChanged();}@NonNull@Overridepublic MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {View itemView = LayoutInflater.from(mContext).inflate(R.layout.item_recycler, parent, false);MyViewHolder holder = new MyViewHolder(itemView);return holder;}@Overridepublic void onBindViewHolder(@NonNull MyViewHolder holder, int position) {holder.tv.setText(mList.get(position));}@Overridepublic int getItemCount() {return mList.size();}class MyViewHolder extends RecyclerView.ViewHolder {private TextView tv;public MyViewHolder(@NonNull View itemView) {super(itemView);tv = itemView.findViewById(R.id.tvItem);}}
}
2.3、Activity 中的代码
public class MainActivity extends AppCompatActivity {private RecyclerView mRecyclerView;private HomeAdapter mHomeAdapter;private List<String> mList;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);mRecyclerView = this.findViewById(R.id.recyclerView);// 设置布局管理器LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);linearLayoutManager.setOrientation(RecyclerView.VERTICAL);mRecyclerView.setLayoutManager(linearLayoutManager);// 设置 item 增加和删除时的动画mRecyclerView.setItemAnimator(new DefaultItemAnimator());mList = getList();mHomeAdapter = new HomeAdapter(this, mList);mRecyclerView.setAdapter(mHomeAdapter);}private List<String> getList() {List<String> list = new ArrayList<>();for (int i = 0; i < 100; i++) {list.add(i + "");}return list;}
}
2.4、排列方向
垂直排列(默认): linearLayoutManager.setOrientation(RecyclerView.VERTICAL);
水平排列: linearLayoutManager.setOrientation(RecyclerView.HORIZONTAL);
运行效果

三、设置分割线
参考博客:
RecyclerView之ItemDecoration由浅入深
RecyclerView 之 ItemDecoration 讲解及高级特性实践
正确使用RecyclerView分割线
3.1、默认分割线。
mRecyclerView.addItemDecoration(new DividerItemDecoration(this, RecyclerView.VERTICAL));
效果图:

3.2、复制 DividerItemDecoration 的源码进行修改。参考 5.2 中示例
我们可以根据默认分割线的源码,继承 RecyclerView.ItemDecoration 来自定义分割线。里面核心的方法就是 onDraw 方法,它根据传进来的 orientation 来判断是绘制横向 item 的分割线还是纵向 item 的分割线。getItemOffsets 方法则用于设置 item 的 padding 属性。
四、自定义点击事件
1、自定义接口并提供回调方法
private OnItemClickListener mOnItemClickListener;public interface OnItemClickListener {void onItemClick(View view, int position);void onItemLongClick(View view, int position);}public void setOnItemClickListener(OnItemClickListener onItemClickListener) {this.mOnItemClickListener = onItemClickListener;}
2、Adapter 继承 View.OnClickListener 和 View.OnLongClickListener 并实现其中的方法。
3、OnBindViewHolder 方法中给 item 设置 tag。
@Overridepublic void onBindViewHolder(@NonNull MyViewHolder holder, int position) {holder.itemView.setTag(position);holder.tv.setText(mList.get(position));}
4、监听 item 的点击事件并回调给我们自定义的监听。
@Overridepublic MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {View itemView = LayoutInflater.from(mContext).inflate(R.layout.item_recycler, parent, false);MyViewHolder holder = new MyViewHolder(itemView);itemView.setOnClickListener(this);itemView.setOnLongClickListener(this);return holder;}
@Overridepublic void onClick(View v) {if (mOnItemClickListener != null) {mOnItemClickListener.onItemClick(v, (int) v.getTag());}}@Overridepublic boolean onLongClick(View v) {if (mOnItemClickListener != null) {mOnItemClickListener.onItemLongClick(v, (int) v.getTag());}return false;}
5、最后在 Activity 中进行监听
mHomeAdapter.setOnItemClickListener(new HomeAdapter.OnItemClickListener() {@Overridepublic void onItemClick(View view, int position) {Toast.makeText(MainActivity.this, "点击了第" + (position + 1) + "条", Toast.LENGTH_SHORT).show();}@Overridepublic void onItemLongClick(View view, final int position) {new AlertDialog.Builder(MainActivity.this).setTitle("确认删除吗?").setNegativeButton("取消", null).setPositiveButton("确定", new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialog, int which) {mHomeAdapter.removeData(position);}}).show();}});
长按时会弹出对话框,效果如下。

五、实现 GridView
5.1、布局管理器
这里设置 4 列
GridLayoutManager gridLayoutManager = new GridLayoutManager(this,4);mRecyclerView.setLayoutManager(gridLayoutManager);
或者用瀑布流布局管理器
StaggeredGridLayoutManager staggeredGridLayoutManager = new StaggeredGridLayoutManager(4, StaggeredGridLayoutManager.HORIZONTAL);mRecyclerView.setLayoutManager(staggeredGridLayoutManager);
注意:StaggeredGridLayoutManager.VERTICAL 的情况下,4 是列数,表示只有 4 列。改为 StaggeredGridLayoutManager.HORIZONTAL 方向时,4 是行数,表示只有 4 行。
5.2、分割线
没有默认的网格布局分割线,这里我们通过修改 DividerItemDecoration 的源码自定义一个分割线。代码如下:
public class DividerGridItemDecoration extends RecyclerView.ItemDecoration {public static final int HORIZONTAL = LinearLayout.HORIZONTAL;public static final int VERTICAL = LinearLayout.VERTICAL;private static final String TAG = "DividerItem";private static final int[] ATTRS = new int[]{android.R.attr.listDivider};private Drawable mDivider;private int mOrientation;private final Rect mBounds = new Rect();public DividerGridItemDecoration(Context context) {final TypedArray a = context.obtainStyledAttributes(ATTRS);mDivider = a.getDrawable(0);if (mDivider == null) {Log.w(TAG, "@android:attr/listDivider was not set in the theme used for this "+ "DividerItemDecoration. Please set that attribute all call setDrawable()");}a.recycle();}public void setDrawable(@NonNull Drawable drawable) {if (drawable == null) {throw new IllegalArgumentException("Drawable cannot be null.");}mDivider = drawable;}@Nullablepublic Drawable getDrawable() {return mDivider;}@Overridepublic void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {if (parent.getLayoutManager() == null || mDivider == null) {return;}drawVertical(c, parent);drawHorizontal(c, parent);}private void drawVertical(Canvas canvas, RecyclerView parent) {canvas.save();final int left;final int right;//noinspection AndroidLintNewApi - NewApi lint fails to handle overrides.if (parent.getClipToPadding()) {left = parent.getPaddingLeft();right = parent.getWidth() - parent.getPaddingRight();canvas.clipRect(left, parent.getPaddingTop(), right,parent.getHeight() - parent.getPaddingBottom());} else {left = 0;right = parent.getWidth();}final int childCount = parent.getChildCount();for (int i = 0; i < childCount; i++) {final View child = parent.getChildAt(i);parent.getDecoratedBoundsWithMargins(child, mBounds);final int bottom = mBounds.bottom + Math.round(child.getTranslationY());final int top = bottom - mDivider.getIntrinsicHeight();mDivider.setBounds(left, top, right, bottom);mDivider.draw(canvas);}canvas.restore();}private void drawHorizontal(Canvas canvas, RecyclerView parent) {canvas.save();final int top;final int bottom;//noinspection AndroidLintNewApi - NewApi lint fails to handle overrides.if (parent.getClipToPadding()) {top = parent.getPaddingTop();bottom = parent.getHeight() - parent.getPaddingBottom();canvas.clipRect(parent.getPaddingLeft(), top,parent.getWidth() - parent.getPaddingRight(), bottom);} else {top = 0;bottom = parent.getHeight();}final int childCount = parent.getChildCount();for (int i = 0; i < childCount; i++) {final View child = parent.getChildAt(i);parent.getLayoutManager().getDecoratedBoundsWithMargins(child, mBounds);final int right = mBounds.right + Math.round(child.getTranslationX());final int left = right - mDivider.getIntrinsicWidth();mDivider.setBounds(left, top, right, bottom);mDivider.draw(canvas);}canvas.restore();}@Overridepublic void getItemOffsets(Rect outRect, View view, RecyclerView parent,RecyclerView.State state) {if (mDivider == null) {outRect.set(0, 0, 0, 0);return;}if (mOrientation == VERTICAL) {outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());} else {outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0);}}
}
添加分割线
DividerGridItemDecoration gridItemDecoration=new DividerGridItemDecoration(this);mRecyclerView.addItemDecoration(gridItemDecoration);
运行效果如下

5.3、每个item占用的格数
使用setSpanSizeLookup函数,其中传入一个GridLayoutManager.SpanSizeLookup对象,其内部有一个抽象函数getSpanSize(),你可以设置返回的数值,让当前的item占据几个位置,当然返回的int型数值只能小于等于GridLayoutManager设置span的个数,比如每行item的个数为4个,然后你设置返回5,就会报错。
首先我们不能再以上面的方式添加分割线了,而是通过设置item的样式来设置分割线,为了更加直观添加了一个颜色。
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"><strokeandroid:width="1dp"android:color="#000000" /><solid android:color="@color/colorAccent" />
</shape>
设置每个数据占用的格数。
GridLayoutManager gridLayoutManager = new GridLayoutManager(this, 4);gridLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {@Overridepublic int getSpanSize(int position) {if (position == 0) {return 4;} else if (position == 2) {return 3;} else {return 1;}}});
这里设置第1个数据占用4格,第3个数据占用3格,其余的都只占用一格。效果图如下。

六、实现瀑布流
6.1、修改 item 布局
为了实现方便,瀑布流不用分割线,我们定义 item 的分割距离为 2dp,为了更加直观,我们还给 item 添加了一个颜色。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_margin="2dp"android:background="@color/colorPrimary"android:orientation="vertical"><TextViewandroid:id="@+id/tvItem"android:layout_width="match_parent"android:layout_height="50dp"android:gravity="center" /></LinearLayout>
6.2、每个 item 的高度
通常这个高度是由服务器返回的数据高度来控制的,在这里我们写一个随机高度来控制 item。
mHeights = new ArrayList<>();for (int i = 0; i < mList.size(); i++) {mHeights.add((int) (100 + Math.random() * 300));}
设置 item 的高度
@Overridepublic void onBindViewHolder(@NonNull MyViewHolder holder, int position) {holder.itemView.setTag(position);holder.tv.setText(mList.get(position));ViewGroup.LayoutParams lp = holder.tv.getLayoutParams();lp.height = mHeights.get(position);holder.tv.setLayoutParams(lp);}
运行效果如下

七、更多效果
Recyclerview实现滑动放大ItemView
八、RecyclerView 常见问题
- 给 RecyclerView 设置 padding 时,滚动内容时与屏幕边缘存在一个间隙
给 RecyclerView 设置布局属性:android:clipToPadding=“false”
九、RecyclerView 和 ScrollView 嵌套的问题
- 滑动不流畅
代码中:RecyclerView 使用 setNestedScrollingEnabled(false) 方法禁止嵌套滑动。
或者布局中:android:nestedScrollingEnabled=“false” - 首次进入会占用焦点,导致 ScrollView 不能显示最上方
RecyclerView 设置 setFocusable(false) 。 - 显示不全
外层嵌套布局并使用 android:descendantFocusability=“blocksDescendants” 属性。
<RelativeLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:descendantFocusability="blocksDescendants"><android.support.v7.widget.RecyclerViewandroid:id="@+id/recyclerview"android:layout_width="match_parent"android:layout_height="wrap_content" /></RelativeLayout>

















