目录
一、配置RecyclerView
二、使用RecyclerView
三、RecyclerView的分割线
四、RecyclerView的Adapter
五、RecyclerView的点击事件
5.1、在Adapter中实现
5.2、在Activity(Fragment)中实现
六、RecyclerView实现GridView
七、RecyclerView实现瀑布流
八、RecyclerView设置头视图、足视图、空视图
一、配置RecyclerView
首先要导入support-v7包,在app的build.gradle中加入如下代码导包,然后async工程一下:
dependencies {
...
implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'com.android.support:recyclerview-v7:28.0.0'
}
二、使用RecyclerView
在XML中
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler"
android:layout_width="match_parent"
android:layout_height="match_parent" />
在Activity中或者Fragment中代码
private void initView() {
RecyclerView mRecyclerView = (RecyclerView) findViewById(R.id.recycler);
LinearLayoutManager manager = new LinearLayoutManager(mContext);
//布局管理器设置排列样式
// manager.setOrientation(LinearLayoutManager.HORIZONTAL);
manager.setOrientation(LinearLayoutManager.VERTICAL);
mRecyclerView.setLayoutManager(manager);
//设置item增加和删除时的动画
mRecyclerView.setItemAnimator(new DefaultItemAnimator());
//设置分割线
mRecyclerView.addItemDecoration(new DividerItemDecoration(mContext,DividerItemDecoration.VERTICAL_LIST));
mAdapter = new RecyclerViewAdapter(mContext,mListData);
mRecyclerView.setAdapter(mAdapter);
}
三、RecyclerView的分割线
//设置分割线
mRecyclerView.addItemDecoration(new DividerItemDecoration(mContext,DividerItemDecoration.VERTICAL_LIST));
RecyclerView自身属性没有默认的分割线,需要我们继承RecyclerView.ItemDecoration来自定义分割线:
public class DividerItemDecoration extends RecyclerView.ItemDecoration {
private static final int[] ATTRS = new int[]{
android.R.attr.listDivider
};
public static final int HORIZONTAL_LIST = LinearLayoutManager.HORIZONTAL;
public static final int VERTICAL_LIST = LinearLayoutManager.VERTICAL;
private Drawable mDivider;
private int mOrientation;
public DividerItemDecoration(Context context, int orientation) {
final TypedArray a = context.obtainStyledAttributes(ATTRS);
mDivider = a.getDrawable(0);
a.recycle();
setOrientation(orientation);
}
public void setOrientation(int orientation) {
if (orientation != HORIZONTAL_LIST && orientation != VERTICAL_LIST) {
throw new IllegalArgumentException("invalid orientation");
}
mOrientation = orientation;
}
@Override
public void onDraw(Canvas c, RecyclerView parent) {
if (mOrientation == VERTICAL_LIST) {
drawVertical(c, parent);
} else {
drawHorizontal(c, parent);
}
}
public void drawVertical(Canvas c, RecyclerView parent) {
final int left = parent.getPaddingLeft();
final int right = parent.getWidth() - parent.getPaddingRight();
final int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
final View child = parent.getChildAt(i);
RecyclerView v = new RecyclerView(parent.getContext());
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
.getLayoutParams();
final int top = child.getBottom() + params.bottomMargin;
final int bottom = top + mDivider.getIntrinsicHeight();
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(c);
}
}
public void drawHorizontal(Canvas c, RecyclerView parent) {
final int top = parent.getPaddingTop();
final int bottom = parent.getHeight() - parent.getPaddingBottom();
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 left = child.getRight() + params.rightMargin;
final int right = left + mDivider.getIntrinsicHeight();
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(c);
}
}
@Override
public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) {
if (mOrientation == VERTICAL_LIST) {
outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());
} else {
outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0);
}
}
}
这里核心方法时OnDraw,它根据传进来的orientation来判断绘制横向item或者纵向item的分割线,drawVertical()用于绘制纵向item的分割线, drawHorizontal()用于绘制横向item的分割线, getItemOffsets()用于设置item的padding属性。
但是重点在于:设置分割线,一定,一定在set Adapter之前加入,根据不同的Recycler View有不同的分割线,在下面的Recycler View实现中会有讲解。
四、RecyclerView的Adapter
RecyclerView的Adapter最大的改进时增加了ViewType,会根据不同的View Type加载不同的item布局。使用Adapter可以分为四步吧(对于小菜鸟的写法,大神封装宁说)。
1、写一个Adapter类去继承RecyclerView.Adapter<>
public class RecyclerViewAdapter extends RecyclerView.Adapter<T>
2、写一个ViewHolder内部类去继承RecyclerView.ViewHolder,在内部类中声明控件,并将这个类传入Adapter类的泛型中
static class ViewHolder extends RecyclerView.ViewHolder {
TextView mItemIndexTv;
public ViewHolder(@NonNull View itemView) {
super(itemView);
mItemIndexTv = (TextView) itemView.findViewById(R.id.item_tv);
}
}
3、重写onCreateViewHolder()、onBindViewHolder()、getItemCount()方法
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
if (mContext == null) {
mContext = viewGroup.getContext();
}
View view = LayoutInflater.from(mContext).inflate(R.layout.item_recycler, viewGroup, false);
ViewHolder holder = new ViewHolder(view);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull final ViewHolder viewHolder, final int position) {
viewHolder.mItemIndexTv.setText(mListData.get(position));
if(mOnItemClickListener != null){
viewHolder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
int pos = viewHolder.getLayoutPosition();
mOnItemClickListener.OnItemClick(viewHolder.itemView,pos);
}
});
viewHolder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View view) {
int pos = viewHolder.getLayoutPosition();
mOnItemClickListener.OnItemLongClick(viewHolder.itemView,pos);
return false;
}
});
}
}
@Override
public int getItemCount() {
return mListData != null ? mListData.size() : 0;
}
4、构造函数和自己定义方法。
public RecyclerViewAdapter(Context mContext, List<String> mListData) {
this.mContext = mContext;
this.mListData = mListData;
}
/**
* 数据更新
*/
public void upData(List<String> listData) {
if (listData != null) {
mListData.addAll(listData);
}
notifyDataSetChanged();
}
/**
* 数据清除
*/
public void cleanData() {
if (mListData != null && mListData.size() > 0) {
mListData.clear();
notifyDataSetChanged();
}
}
/**
* 增加对应位置的数据
*/
public void addData(int position, List<String> listData) {
if (listData != null && listData.size() > 0) {
mListData.addAll(position, listData);
notifyDataSetChanged();
}
}
/**
* 删除指定位置的数据
*/
public void removeData(int position){
mListData.remove(position);
notifyDataSetChanged();
}
完整的Adapter类如下:
public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.ViewHolder> {
private Context mContext;
private List<String> mListData;
public RecyclerViewAdapter(Context mContext, List<String> mListData) {
this.mContext = mContext;
this.mListData = mListData;
}
/**
* 数据更新
*/
public void upData(List<String> listData) {
if (listData != null) {
mListData.addAll(listData);
}
notifyDataSetChanged();
}
/**
* 数据清除
*/
public void cleanData() {
if (mListData != null && mListData.size() > 0) {
mListData.clear();
notifyDataSetChanged();
}
}
/**
* 增加对应位置的数据
*/
public void addData(int position, List<String> listData) {
if (listData != null && listData.size() > 0) {
mListData.addAll(position, listData);
notifyDataSetChanged();
}
}
/**
* 删除指定位置的数据
*/
public void removeData(int position){
mListData.remove(position);
notifyDataSetChanged();
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
if (mContext == null) {
mContext = viewGroup.getContext();
}
View view = LayoutInflater.from(mContext).inflate(R.layout.item_recycler, viewGroup, false);
ViewHolder holder = new ViewHolder(view);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull final ViewHolder viewHolder, final int position) {
viewHolder.mItemIndexTv.setText(mListData.get(position));
}
@Override
public int getItemCount() {
return mListData != null ? mListData.size() : 0;
}
static class ViewHolder extends RecyclerView.ViewHolder {
TextView mItemIndexTv;
public ViewHolder(@NonNull View itemView) {
super(itemView);
mItemIndexTv = (TextView) itemView.findViewById(R.id.item_tv);
}
}
}
然后在Activity中或者Fragment中声明Adapter和设置Adapter:
RecyclerViewAdapter mAdapter = new RecyclerViewAdapter(mContext,mListData);
mRecyclerView.setAdapter(mAdapter);
五、RecyclerView的点击事件
RecyclerView不能像ListView一样有itemClick的监听,需要我们自己实现。
5.1、在Adapter中实现
在设置Adapter中我们指定了ViewHolder,ViewHolde代表的是RecyclerView的单条,而ViewHolder中有一个itemView,所指的就是RecyclerView的单条,所以我们只需要设置holder.itemView的点击事件,就是RecyclerView的单条点击事件。
@Override
public void onBindViewHolder(@NonNull final ViewHolder viewHolder, final int position) {
viewHolder.mItemIndexTv.setText(mListData.get(position));
//单点击事件
viewHolder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Toast.makeText(mContext,"点击了第"+ position,Toast.LENGTH_SHORT).show();
}
});
//长点击事件
viewHolder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View view) {
Toast.makeText(mContext,"进行长点击、点击了第"+ position,Toast.LENGTH_SHORT).show();
return false;
}
});
}
5.2、在Activity(Fragment)中实现
想要像ListView在Activity(Fragment)中实现RecyclerView的点击事件,需要使用接口回调。
基本步骤有四步:
1、定义接口
public interface OnItemClickListener {
//单点监听
void OnItemClick(View view,int position);
//长点监听
void OnItemLongClick(View view,int position);
}
2、在Adapter中声明接口并设置监听
private OnItemClickListener mOnItemClickListener;
public void setOnItemClickListener(OnItemClickListener mOnItemClickListener) {
this.mOnItemClickListener = mOnItemClickListener;
}
3、对item进行点击事件监听并回调给我么自定义的监听
@Override
public void onBindViewHolder(@NonNull final ViewHolder viewHolder, final int position) {
viewHolder.mItemIndexTv.setText(mListData.get(position));
if(mOnItemClickListener != null){
viewHolder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
int pos = viewHolder.getLayoutPosition();
mOnItemClickListener.OnItemClick(viewHolder.itemView,pos);
}
});
viewHolder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View view) {
int pos = viewHolder.getLayoutPosition();
mOnItemClickListener.OnItemLongClick(viewHolder.itemView,pos);
return false;
}
});
}
}
4、在Activity(Fragment)中实现点击逻辑
mAdapter.setOnItemClickListener(new OnItemClickListener() {
@Override
public void OnItemClick(View view, int position) {
Toast.makeText(RecyclerViewActivity.this,"点击了第"+ position + "条",Toast.LENGTH_SHORT).show();
}
@Override
public void OnItemLongClick(View view, final int position) {
new AlertDialog.Builder(RecyclerViewActivity.this)
.setTitle("确认删除")
.setNegativeButton("取消",null)
.setPositiveButton("确定", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
mAdapter.removeData(position);
}
})
.show();
}
});
六、RecyclerView实现GridView
mRecyclerView.addItemDecoration(new DividerGridItemDecoration(this));
GridLayoutManager manager = new GridLayoutManager(this,3);
mRecyclerView.setLayoutManager(manager);
设置Manager为GridLayoutManager,第一个参数为上下文,第二个参数为GridView列数,这里使用的分割线的类会在DEMO代码中给出,欢迎下载
实现效果:
七、RecyclerView实现瀑布流
指定RecyclerView的Manager为StaggeredGridLayoutManager,第一个参数为瀑布流的列数,第二个参数为瀑布流的方向。
StaggeredGridLayoutManager manager = new StaggeredGridLayoutManager(3,StaggeredGridLayoutManager.VERTICAL);
mRecyclerView.setLayoutManager(manager);
八、RecyclerView添加头视图、足视图、空视图
RecyclerView没有像ListView一样给我们提供addHeaderView()和addFooterView()、setEmptyView()的方法,需要需要我们自己进行封装,比较好的是RecyclerView给我们提供了丰富的Adapter可进行拓展,Adapter中可根据不同的View Type去加载不同的视图,这样就给我们暴露出一个接口了
这里找了一个大神的博客,他写的很详细了, 大家可以看看
https://www.jianshu.com/p/0eebc6c2a08d
他的思路也写的比较详细。我们不光会要用,还要知道其中的原理。
这里我只写出如何使用
1、添加依赖:
implementation 'com.xpc:gloriousrecyclerview:0.2.6@aar'
implementation 'com.android.support:recyclerview-v7:24.2.1'
2、xml使用
<com.xpc.gloriousrecyclerview.GloriousRecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:hideNoMoreData="true"
app:loadAllCompleteText="Load Complete"
app:loadMoreBackground="#ccc"
app:loadMoreFailedText="加载失败..."
app:loadMoreIndeterminateDrawable="@drawable/loading_icon_drawable"
app:loadMoreTextColor="#0080ff"
app:loadMoreTextSize="14sp"
app:loadingMoreText="加载中..请稍后"/>
3、在Activity(Fragment)中
public class HeadAndFooterRecyclerViewActivity extends AppCompatActivity {
private GloriousRecyclerView mRecycler;
private List<String> mListData = new ArrayList<>();
private RecyclerViewAdapter mAdapter;
private View mHeadView, mFooterView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_head_and_footer_recycler_view);
initHeadAndFooterView();
initView();
initData();
}
private void initData() {
for (int i = 0; i < 10; i++) {
mListData.add(i + "");
}
mAdapter.upData(mListData);
}
private void initView() {
mRecycler = (GloriousRecyclerView) findViewById(R.id.recycler_view);
LinearLayoutManager manager = new LinearLayoutManager(this);
manager.setOrientation(LinearLayoutManager.VERTICAL);
mRecycler.setLayoutManager(manager);
mAdapter = new RecyclerViewAdapter(this, mListData);
mRecycler.setAdapter(mAdapter);
mRecycler.addHeaderView(mHeadView);
mRecycler.addFooterView(mFooterView);
mRecycler.setEmptyView(emptyView);
}
private void initHeadAndFooterView() {
mHeadView = LayoutInflater.from(this).inflate(R.layout.recycler_head, mRecycler, false);
mFooterView = LayoutInflater.from(this).inflate(R.layout.recycler_footer, mRecycler, false);
emptyView = LayoutInflater.from(this).inflate(R.layout.recycler_empty,mRecycler,false);
}
}
使用效果图:
这就是大概的使用,作者的GitHub中提供的DEMO功能更多,下拉刷新、上拉加载,大家如果需要的话可以自己去学习,这里提供出Git地址:
https://github.com/titanchen2000/GloriousRecyclerView
不过有一些缺陷的是:使用这个控件,只能添加一层的头视图和足视图,无法添加多层,在后续中我会进行修改,欢迎关注。谢谢。
希望这篇博客能帮助到你!