代码实现
RecyclerViewActivity.java
public class RecyclerViewActivity extends BaseActivity {
private RecyclerView recyclerView;
private MyRecyclerViewAdapter adapter;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_recyclerview);
recyclerView = findViewById(R.id.recyclerview);
initRecyclerView();
}
/**
* 初始化 RecyclerView
*/
private void initRecyclerView() {
initLayoutManager();
initItemDivider();
initAdapter();
initItemClickListener();
}
/**
* 设置布局管理器
*/
private void initLayoutManager() {
/**
* 用于实现列表布局
*/
LinearLayoutManager manager = new LinearLayoutManager(this);
manager.setOrientation(LinearLayoutManager.VERTICAL);
recyclerView.setLayoutManager(manager);
/**
* 用于实现网格布局
*/
// GridLayoutManager manager = new GridLayoutManager(this, 3);
// manager.setOrientation(GridLayoutManager.VERTICAL);
// recyclerView.setLayoutManager(manager);
/**
* 用于实现瀑布流
* 第一个参数表示布局的列数/行数
* 第二个参数表示布局的方向
* StaggeredGridLayoutManager.VERTICAL -- 表示布局纵向排列
* StaggeredGridLayoutManager.HORIZONTAL -- 表示布局横向排列
*/
// StaggeredGridLayoutManager manager = new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL);
// manager.setOrientation(StaggeredGridLayoutManager.HORIZONTAL);
// recyclerView.setLayoutManager(manager);
}
/**
* 设置 adapter
*/
private void initAdapter() {
adapter = new MyRecyclerViewAdapter(this, initBookList());
recyclerView.setAdapter(adapter);
}
/**
* 设置 点击或长按事件监听
*/
private void initItemClickListener() {
OnRecyclerViewItemClickListener itemClickListener = new OnRecyclerViewItemClickListener() {
/**
* item 点击事件
*
* @param position
*/
@Override
public void onItemClick(int position) {
Toast.makeText(RecyclerViewActivity.this, position + "被点击", Toast.LENGTH_SHORT).show();
}
/**
* item 长按事件
* @param position
*/
@Override
public boolean onItemLongClick(int position) {
Toast.makeText(RecyclerViewActivity.this, position + "被长按", Toast.LENGTH_SHORT).show();
return true;
}
};
adapter.setOnRecyclerViewItemClickListener(itemClickListener);
}
/**
* 给 item 添加分割线
* 注意:只适用于列表布局(LinearLayout)的 RecyclerView
* 网格布局(GridLayout)和流式布局(StaggeredGridLayout)的 RecyclerView 就不适用了。
*/
private void initItemDivider() {
//添加 Android 自带的分割线
// DividerItemDecoration divider = new DividerItemDecoration(this, DividerItemDecoration.VERTICAL);
//添加 分割线,但最后一个 item 不添加分割线的效果
CustomDividerItemDecoration divider = new CustomDividerItemDecoration(this, DividerItemDecoration.VERTICAL);
//添加 自定义图片/ 图形 的分割线
divider.setDrawable(getResources().getDrawable(R.drawable.line_gradient));
recyclerView.addItemDecoration(divider);
}
/**
* 初始化 集合 数据
*
* @return mBookList
*/
private List<Book> initBookList() {
List<Book> list = new ArrayList<>();
for (int i = 0; i < 10000; i++) {
list.add(new Book("book" + i, "author" + i));
}
return list;
}
public class MyRecyclerViewAdapter extends RecyclerView.Adapter<BookViewHolder> implements View.OnClickListener, View.OnLongClickListener {
Context context;
List<Book> list;
private OnRecyclerViewItemClickListener itemClickListener = null;
/**
* 设置 item 点击监听器
* @param itemClickListener
*/
public void setOnRecyclerViewItemClickListener(OnRecyclerViewItemClickListener itemClickListener) {
this.itemClickListener = itemClickListener;
}
public MyRecyclerViewAdapter(Context context, List<Book> list) {
this.context = context;
this.list = list;
}
@Override
public BookViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(context).inflate(R.layout.viewholder_recyclerview, parent, false);
BookViewHolder viewHolder = new BookViewHolder(view);
view.setOnClickListener(this);
view.setOnLongClickListener(this);
return viewHolder;
}
@Override
public void onBindViewHolder(BookViewHolder holder, int position) {
holder.textview.setText(list.get(position).toString());
if (itemClickListener == null) {
return;
}
//被点击的 item 设置 tag 标签,用于 item 被点击时获取 position 索引值
holder.itemView.setTag(position);
}
@Override
public int getItemCount() {
return list.size();
}
/**
* 当 item 被点击时,调用 item 点击监听,执行点击回调
* @param v
*/
@Override
public void onClick(View v) {
if (itemClickListener == null) {
return;
}
//被点击的 item 获取 tag 标签,即 position 索引值
itemClickListener.onItemClick((Integer) v.getTag());
}
@Override
public boolean onLongClick(View v) {
return itemClickListener.onItemLongClick((Integer) v.getTag());
}
}
/**
* RecyclerView 的 ViewHolder 实现类
*/
public class BookViewHolder extends RecyclerView.ViewHolder {
TextView textview;
public BookViewHolder(View itemView) {
super(itemView);
textview = itemView.findViewById(R.id.textview);
}
}
}
参照 DividerItemDecoration 自定义 item 的分割线
/**
* 最后一个 item 不添加分割线
* 只能用于 LinearLayoutManager 列表布局,不能用于网格布局(GridLayoutManager)和瀑布流(StaggeredGridLayoutManager)
*/
public class CustomDividerItemDecoration extends RecyclerView.ItemDecoration {
public static final int HORIZONTAL = LinearLayout.HORIZONTAL;
public static final int VERTICAL = LinearLayout.VERTICAL;
private static final String TAG = "CustomDividerItem";
private static final int[] ATTRS = new int[]{ android.R.attr.listDivider };
private Drawable mDivider;
/**
* Current orientation. Either {@link #HORIZONTAL} or {@link #VERTICAL}.
*/
private int mOrientation;
private final Rect mBounds = new Rect();
/**
* 只能用于 LinearLayoutManager 列表布局,不能用于网格布局(GridLayoutManager)和瀑布流(StaggeredGridLayoutManager)
*
* @param context Current context, it will be used to access resources.
* @param orientation Divider orientation. Should be {@link #HORIZONTAL} or {@link #VERTICAL}.
*/
public CustomDividerItemDecoration(Context context, int orientation) {
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();
setOrientation(orientation);
}
/**
* Sets the orientation for this divider. This should be called if
* {@link RecyclerView.LayoutManager} changes orientation.
*
* @param orientation {@link #HORIZONTAL} or {@link #VERTICAL}
*/
public void setOrientation(int orientation) {
if (orientation != HORIZONTAL && orientation != VERTICAL) {
throw new IllegalArgumentException(
"Invalid orientation. It should be either HORIZONTAL or VERTICAL");
}
mOrientation = orientation;
}
/**
* Sets the {@link Drawable} for this divider.
*
* @param drawable Drawable that should be used as a divider.
*/
public void setDrawable(@NonNull Drawable drawable) {
if (drawable == null) {
throw new IllegalArgumentException("Drawable cannot be null.");
}
mDivider = drawable;
}
@Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
if (parent.getLayoutManager() == null || mDivider == null) {
return;
}
if (mOrientation == VERTICAL) {
drawVertical(c, parent);
} else {
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 - 1; 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 - 1; 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();
}
@Override
public 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);
}
}
}
item 点击事件监听
OnRecyclerViewItemClickListener.java
public interface OnRecyclerViewItemClickListener {
/**
* 点击事件
* @param position
*/
void onItemClick(int position);
/**
* 长按事件
* @param position
*/
void onItemLongClick(int position);
}
实体 Bean
Book.java
public class Book {
private String name;
private String author;
public Book(String name, String author){
this.name = name;
this.author = author;
}
@Override
public String toString() {
return "name: " + name + "\n" + "author: " + author;
}
}
布局文件
activity_recyclerview.xml
<?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">
<TextView
android:layout_width="wrap_content"
android:text="recyclervie"
android:layout_marginBottom="15dp"
android:layout_height="wrap_content" />
<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerview"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
viewholder_recyclerview.xml
<?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="horizontal">
<TextView
android:id="@+id/textview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:drawableRight="@drawable/mechanic"
android:padding="15dp"
android:text="item" />
</LinearLayout>
line_gradient.xml
![](/qrcode.jpg)
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<gradient
android:startColor="#FFFFFFFF"
android:centerColor="#FF0000FF"
android:endColor="#FFFFFFFF"
android:type="linear"/>
<!--尝试这个颜色搭配的效果-->
<!--<gradient-->
<!--android:startColor="#FFFF0000"-->
<!--android:centerColor="#FF00FF00"-->
<!--android:endColor="#FF0000FF"-->
<!--android:type="linear" />-->
<!--DividerItemDecoration.HORIZONTAL 时设置-->
<!--<size android:width="3dp" />-->
<!--DividerItemDecoration.VERTICAL 时设置-->
<size android:height="3dp" />
</shape>
RecyclerView 的 item 的点击事件
RecyclerView 竟然找不到 setOnItemClickListener() 方法,网上的写法都是自定义接口来实现
本例中,已经添加了 item 的点击事件和长按事件监听
/**
* item 点击事件监听
*/
public interface OnItemClickListener{
/**
* 点击事件
* @param position
*/
void onItemClick(int position);
/**
* 长按事件
* @param position
*/
void onItemLongClick(int position);
}
这里还是有几点需要注意的地方
1.RecyclerView 需要用到 布局管理器,如未添加,则 item 没有显示出来
/**
* 用于实现列表布局
*/
LinearLayoutManager manager = new LinearLayoutManager(this);
manager.setOrientation(LinearLayoutManager.VERTICAL);
recyclerView.setLayoutManager(manager);
如果需要改成横向的列表,只需改变一行代码即可
manager.setOrientation(LinearLayoutManager.HORIZONTAL);
RecyclerView 除了 LinearLayoutManager(列表布局)之外
还提供了 GridlayoutManager(网格布局) 和 StaggeredGridlayoutManager(瀑布流)
/**
* 用于实现网格布局
*/
GridLayoutManager manager = new GridLayoutManager(this, 3);
manager.setOrientation(GridLayoutManager.VERTICAL);
recyclerView.setLayoutManager(manager);
如果需要改成横向的网格,只需改变一行代码即可
manager.setOrientation(GridLayoutManager.HORIZONTAL);
/**
* 用于实现瀑布流
* 第一个参数表示布局的列数/行数
* 第二个参数表示布局的方向
* StaggeredGridLayoutManager.VERTICAL -- 表示布局纵向排列
* StaggeredGridLayoutManager.HORIZONTAL -- 表示布局横向排列
*/
StaggeredGridLayoutManager manager = new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL);
recyclerView.setLayoutManager(manager);
如果需要改成横向的瀑布流,只需添加/改变一行代码即可
manager.setOrientation(StaggeredGridLayoutManager.HORIZONTAL);
2.RecyclerView 有自己的 Adapter (即,RecyclerView.Adapter)
这里的 Adapter 带了泛型,泛型我们可以写 RecyclerView.ViewHolder,因为 BookViewHolder 就是其子类,但之所以这里没有写 RecyclerView.ViewHolder,是因为这里有个小问题需要注意。
根据快捷键自动实现父类方法,代码如下
存在两个问题
1.创建 RecyclerView.ViewHolder 对象时报错,提示 RecyclerView 里的 ViewHolder 类是抽象类(abstract)
2.在 onBindViewHolder 回调中,找不到我们自己写的 item 布局里的控件
所以,建议在泛型里写我们自己定义的 BookViewHolder 而不是直接写 RecyclerView.ViewHolder,这样在实现父类方法时,直接生成了对应的泛型,而不需要我们在一个一个的去修改(如果忘记修改了,就会需要我们一个一个小问题的去排除),当然只是建议。
public class MyRecyclerViewAdapter extends RecyclerView.Adapter<BookViewHolder> {
Context context;
List<Book> list;
public MyRecyclerViewAdapter(Context context, List<Book> list){
this.context = context;
this.list = list;
}
@Override
public BookViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(context).inflate(R.layout.viewholder_recyclerview, parent, false);
BookViewHolder viewHolder = new BookViewHolder(view);
return viewHolder;
}
@Override
public void onBindViewHolder(BookViewHolder holder, int position) {
holder.textview.setText(list.get(position).toString());
}
@Override
public int getItemCount() {
return list.size();
}
}
3.在 Adapter 的 onCreateViewHolder 方法中,进行加载 item 布局时,建议用下面两种方式
@Override
public BookViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(context).inflate(R.layout.viewholder_recyclerview, parent, false);
BookViewHolder viewHolder = new BookViewHolder(view);
return viewHolder;
}
或
@Override
public BookViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = View.inflate(context, R.layout.viewholder_recyclerview, null);
BookViewHolder viewHolder = new BookViewHolder(view);
return viewHolder;
}
先看一下报错的情况
上面两种情况,会出现 IllegalStateException,并且没有提示错误位置
我们看一下 inflate 的源码
我们可以看到,如果不传 boolean attachToRoot 参数则默认传的是 root != null
而上述两种情况中,ViewGroup 对象并不是 null