一、框架引入
- 先在项目的 build.gradle(Project:XXXX) 的 repositories 添加:
allprojects {
repositories {
...
maven { url "https://jitpack.io" }
}
}
- 然后在Module的 build.gradle(Module:app) 的 dependencies 添加:
dependencies {
......
compile 'com.github.CymChad:BaseRecyclerViewAdapterHelper:2.9.22'
}
2.2 最基本使用示例Demo
-
要是刚接触Android的朋友要是还是不知道怎么用,那我这里提供一个很简单的demo:
-
比如说我想要实现的效果大致如下:
-
第一步:在布局文件中引入RecyclerView
activity_main.xml -
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <android.support.v7.widget.RecyclerView android:id="@+id/recycler_view" android:layout_width="match_parent" android:layout_height="wrap_content" /> </RelativeLayout>
- 第二步:编写条目布局文件
item_rv.xml(省略)
- 第三步:编写数据实体类型
Model.java
public class Model {
private String title;
private String content;
private String imgUrl;
//生成set、get方法
......
}
- 第四步:编写适配器
MyAdapter.java
public class MyAdapter extends BaseQuickAdapter<Model, BaseViewHolder> {
public MyAdapter(@LayoutRes int layoutResId, @Nullable List<Model> data) {
super(layoutResId, data);
}
@Override
protected void convert(BaseViewHolder helper, Model item) {
//可链式调用赋值
helper.setText(R.id.tv_title, item.getTitle())
.setText(R.id.tv_content, item.getContent())
.setImageResource(R.id.iv_img, R.mipmap.ic_launcher);
//获取当前条目position
//int position = helper.getLayoutPosition();
}
}
- 最后一步:在Activity中使用该适配器
MainActivity.java
public class MainActivity extends AppCompatActivity {
private RecyclerView recyclerView;
private List<Model> datas;
private MyAdapter adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//初始化RecyclerView
recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
//模拟的数据(实际开发中一般是从网络获取的)
datas = new ArrayList<>();
Model model;
for (int i = 0; i < 15; i++) {
model = new Model();
model.setTitle("我是第" + i + "条标题");
model.setContent("第" + i + "条内容");
datas.add(model);
}
//创建布局管理
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
layoutManager.setOrientation(LinearLayoutManager.VERTICAL);
recyclerView.setLayoutManager(layoutManager);
//创建适配器
adapter = new MyAdapter(R.layout.item_rv, datas);
//给RecyclerView设置适配器
recyclerView.setAdapter(adapter);
}
}
- OK,运行,得到的效果就是上述的效果。
三、点击事件
-
上文中描述的就是使用BaseRecyclerViewAdapterHelper最基本的用法,因为怕刚接触Android的兄弟们不明朗或不相信这么简单的用法,所以做了上节简单的demo示例用法。
-
那么使用列表当然少不了点击事件,不论是整个条目的点击事件还是条目中子控件的点击事件,该适配器对点击事件也是做了及简化的处理:
3.1 条目事件
Item的点击事件
//条目点击事件
adapter.setOnItemClickListener(new BaseQuickAdapter.OnItemClickListener() {
@Override
public void onItemClick(BaseQuickAdapter adapter, View view, int position) {
Toast.makeText(MainActivity.this, "点击了第" + (position + 1) + "条条目", Toast.LENGTH_SHORT).show();
}
});
Item的长按事件
//条目长按事件
adapter.setOnItemLongClickListener(new BaseQuickAdapter.OnItemLongClickListener() {
@Override
public boolean onItemLongClick(BaseQuickAdapter adapter, View view, int position) {
Toast.makeText(MainActivity.this, "长按了第" + (position + 1) + "条条目", Toast.LENGTH_SHORT).show();
return false;
}
});
注意事项
- 在嵌套recycleView的情况下需要使用你使用 adapter. setOnItemClickListener 来设置点击事件,如果使用recycleView.addOnItemTouchListener会累计添加的。
3.2 条目子控件事件
Item子控件的点击事件
- 首先:在adapter的convert方法里面通过 helper.addOnClickListener 绑定一下子控件的控件id
@Override
protected void convert(BaseViewHolder helper, Model item) {
//可链式调用赋值
helper.setText(R.id.tv_title, item.getTitle())
.setText(R.id.tv_content, item.getContent())
.addOnClickListener(R.id.iv_img) //给图标添加点击事件
.setImageResource(R.id.iv_img, R.mipmap.ic_launcher);
//获取当前条目position
//int position = helper.getLayoutPosition();
}
- 然后:我们设置
//条目子控件点击事件
adapter.setOnItemChildClickListener(new BaseQuickAdapter.OnItemChildClickListener() {
@Override
public void onItemChildClick(BaseQuickAdapter adapter, View view, int position) {
Toast.makeText(MainActivity.this, "点击了第" + (position + 1) + "条条目的图片", Toast.LENGTH_SHORT).show();
}
});
Item子控件的长按事件
-
这里和子控件点击事件是一样的,只是将 点击 变成 长按 就可以了
-
首先:在adapter的convert方法里面通过 helper.addOnLongClickListener 绑定一下子控件的控件id
@Override
protected void convert(BaseViewHolder helper, Model item) {
//可链式调用赋值
helper.setText(R.id.tv_title, item.getTitle())
.setText(R.id.tv_content, item.getContent())
//.addOnClickListener(R.id.iv_img) //给图标添加点击事件
.addOnLongClickListener(R.id.iv_img)//给图片添加长按事件
.setImageResource(R.id.iv_img, R.mipmap.ic_launcher);
//获取当前条目position
//int position = helper.getLayoutPosition();
}
- 然后:我们设置
//条目子控件长按事件
adapter.setOnItemChildLongClickListener(new BaseQuickAdapter.OnItemChildLongClickListener() {
@Override
public boolean onItemChildLongClick(BaseQuickAdapter adapter, View view, int position) {
Toast.makeText(MainActivity.this, "长按了第" + (position + 1) + "条条目的图片", Toast.LENGTH_SHORT).show();
return false;
}
});
注意事项
- 设置子控件的事件,如果不在adapter中绑定,点击事件无法生效,因为无法找到你需要设置的控件。
多个Item子控件事件
-
官方文档上没说,但是 其实这里可以用很常规的方法处理,就是通过判断ID来判定是否是我要的控件?,从而处理不同的事件
-
比如我这里给 图片和标题 都加点击事件处理不同的逻辑
-
首先:当然是在适配器中给图片和标题都添加点击事件
@Override
protected void convert(BaseViewHolder helper, Model item) {
//可链式调用赋值
helper.setText(R.id.tv_title, item.getTitle())
.setText(R.id.tv_content, item.getContent())
.addOnClickListener(R.id.iv_img) //给图标添加 点击事件
.addOnClickListener(R.id.tv_title) //给标题也添加 点击事件
.setImageResource(R.id.iv_img, R.mipmap.ic_launcher);
//获取当前条目position
//int position = helper.getLayoutPosition();
}
- 其次:
//条目子控件点击事件
adapter.setOnItemChildClickListener(new BaseQuickAdapter.OnItemChildClickListener() {
@Override
public void onItemChildClick(BaseQuickAdapter adapter, View view, int position) {
//判断id
if (view.getId() == R.id.iv_img) {
Log.i("tag", "点击了第" + position + "条条目的 图片");
} else if (view.getId() == R.id.tv_title) {
Log.i("tag", "点击了第" + position + "条条目的 标题");
}
}
});
- 那么如果是长按事件呢?当然也是相同的处理方法。
如果需要在子控件事件中获取其他子控件可以使用:
getViewByPosition(RecyclerView recyclerView, int position, @IdRes int viewId)
比如:
//条目子控件点击事件
adapter.setOnItemChildClickListener(new BaseQuickAdapter.OnItemChildClickListener() {
@Override
public void onItemChildClick(BaseQuickAdapter adapter, View view, int position) {
Toast.makeText(MainActivity.this, "点击了第" + (position + 1) + "条条目的图片", Toast.LENGTH_SHORT).show();
TextView tv_title = (TextView) adapter.getViewByPosition(recyclerView, position, R.id.tv_title);
Log.i("tag", "当前图片对应的 title=" + tv_title.getText());
}
});
//条目子控件长按事件
adapter.setOnItemChildLongClickListener(new BaseQuickAdapter.OnItemChildLongClickListener() {
@Override
public boolean onItemChildLongClick(BaseQuickAdapter adapter, View view, int position) {
Toast.makeText(MainActivity.this, "长按了第" + position + "条条目的图片", Toast.LENGTH_SHORT).show();
TextView tv_title = (TextView) adapter.getViewByPosition(recyclerView, position, R.id.tv_title);
Log.i("tag", "长按的图片对应的title=" + tv_title.getText());
return false;
}
});
注意:如果有header的话需要处理一下position加上 headerlayoutcount。
- 最后,到这里,点击事件基本就完了
五、添加头部、尾部
- 添加
(可以添加多个头部或尾部)
mQuickAdapter.addHeaderView(headerView);
mQuickAdapter.addFooterView(footerView);
- 删除指定view
mQuickAdapter.removeHeaderView(headerView);
mQuickAdapter.removeFooterView(footerView);
- 删除所有
mQuickAdapter.removeAllHeaderView();
mQuickAdapter.removeAllFooterView();
- 默认出现了头部就不会显示Empty,和尾部,配置以下方法也支持同时显示:
mQuickAdapter.setHeaderAndEmpty()
mQuickAdapter.setHeaderFooterEmpty();
- 默认头部尾部都是占满一行,如果需要不占满可以配置:
mQuickAdapter.setHeaderViewAsFlow();
mQuickAdapter.setFooterViewAsFlow();
六、上拉加载
-
没错,该适配器居然还实现了加载更多的功能,真心佩服作者
-
设置上拉加载
// 滑动最后一个Item的时候回调onLoadMoreRequested方法
setOnLoadMoreListener(RequestLoadMoreListener);
- 默认第一次加载会进入回调,如果不需要可以配置:
mQuickAdapter.disableLoadMoreIfNotFullPage();
- 回调处理代码
//上拉加载(设置这个监听就表示有上拉加载功能了)
mQuickAdapter.setOnLoadMoreListener(new BaseQuickAdapter.RequestLoadMoreListener() {
@Override public void onLoadMoreRequested() {
mRecyclerView.postDelayed(new Runnable() {
@Override
public void run() {
if (mCurrentCounter >= TOTAL_COUNTER) {
//数据全部加载完毕
mQuickAdapter.loadMoreEnd();
} else {
if (isErr) {
//成功获取更多数据(可以直接往适配器添加数据)
mQuickAdapter.addData(DataServer.getSampleData(PAGE_SIZE));
mCurrentCounter = mQuickAdapter.getData().size();
//主动调用加载完成,停止加载
mQuickAdapter.loadMoreComplete();
} else {
//获取更多数据失败
isErr = true;
Toast.makeText(PullToRefreshUseActivity.this, R.string.network_err, Toast.LENGTH_LONG).show();
//同理,加载失败也要主动调用加载失败来停止加载(而且该方法会提示加载失败)
mQuickAdapter.loadMoreFail();
}
}
}
}, delayMillis);
}
}, mReyclerView);
- 加载完成(注意不是加载结束,而是本次数据加载结束并且还有下页数据)
mQuickAdapter.loadMoreComplete();
- 加载失败
mQuickAdapter.loadMoreFail();
- 加载结束
mQuickAdapter.loadMoreEnd();
注意:如果上拉结束后,下拉刷新需要再次开启上拉监听,需要使用setNewData方法填充数据。
- 打开或关闭加载(一般用于下拉的时候做处理,因为上拉下拉不能同时操作)
mQuickAdapter.setEnableLoadMore(boolean);
- 预加载(这个功能屌炸天)
// 当列表滑动到倒数第N个Item的时候(默认是1)回调onLoadMoreRequested方法
mQuickAdapter.setPreLoadNumber(int);
设置自定义加载布局
mQuickAdapter.setLoadMoreView(new CustomLoadMoreView());
public final class CustomLoadMoreView extends LoadMoreView {
@Override
public int getLayoutId() {
return R.layout.view_load_more;
}
/**
* 如果返回true,数据全部加载完毕后会隐藏加载更多
* 如果返回false,数据全部加载完毕后会显示getLoadEndViewId()布局
*/
@Override
public boolean isLoadEndGone() {
return true;
}
@Override
protected int getLoadingViewId() {
return R.id.load_more_loading_view;
}
@Override
protected int getLoadFailViewId() {
return R.id.load_more_load_fail_view;
}
/**
* isLoadEndGone()为true,可以返回0
* isLoadEndGone()为false,不能返回0
*/
@Override
protected int getLoadEndViewId() {
return 0;
}
}
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="@dimen/dp_40">
<LinearLayout
android:id="@+id/load_more_loading_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="horizontal">
<ProgressBar
android:id="@+id/loading_progress"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="?android:attr/progressBarStyleSmall"
android:layout_marginRight="@dimen/dp_4"
android:indeterminateDrawable="@drawable/sample_footer_loading_progress"/>
<TextView
android:id="@+id/loading_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/dp_4"
android:text="@string/loading"
android:textColor="#0dddb8"
android:textSize="@dimen/sp_14"/>
</LinearLayout>
<FrameLayout
android:id="@+id/load_more_load_fail_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone">
<TextView
android:id="@+id/tv_prompt"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:textColor="#0dddb8"
android:text="@string/load_failed"/>
</FrameLayout>
</FrameLayout>
九、多布局
-
多种类型条目的列表是我们日常开发中非常常见的功能,当然该适配器也给我们提供了相应的方法。
-
使用该适配器设置不同类型条目有两种方式,一种 耦合了实体类,一种是设置代理,这里两种方式我都会演示一遍。
Demo实例演示
- 从上述文档中我们是可以看出来两点
1.我们的数据不直接传给适配器,而是通过MultiItemEntity来传递
2.适配器构造里必须绑定type和layout的关系
- 废话不多说,演示一个小demo(这里Item布局我就不贴出来了)
第一步:创建MultiItemEntity类
public class MyMultipleItem implements MultiItemEntity {
public static final int FIRST_TYPE = 1;
public static final int SECOND_TYPE = 2;
public static final int NORMAL_TYPE = 3;
private int itemType;
private Model data;
public MyMultipleItem(int itemType, Model data) {
this.itemType = itemType;
this.data = data;
}
@Override
public int getItemType() {
return itemType;
}
public Model getData(){
return data;
}
}
第二步:创建适配器
public class MultipleItemAdapter extends BaseMultiItemQuickAdapter<MyMultipleItem, BaseViewHolder> {
public MultipleItemAdapter(List data) {
super(data);
//必须绑定type和layout的关系
addItemType(MyMultipleItem.FIRST_TYPE, R.layout.first_type_layout);
addItemType(MyMultipleItem.SECOND_TYPE, R.layout.second_type_layout);
addItemType(MyMultipleItem.NORMAL_TYPE, R.layout.item_rv);
}
@Override
protected void convert(BaseViewHolder helper, MyMultipleItem item) {
switch (helper.getItemViewType()) {
case MyMultipleItem.FIRST_TYPE:
Log.i("tag","FIRST_TYPE==============="+helper.getLayoutPosition());
break;
case MyMultipleItem.SECOND_TYPE:
Log.i("tag","SECOND_TYPE==============="+helper.getLayoutPosition());
break;
case MyMultipleItem.NORMAL_TYPE:
helper.setImageResource(R.id.iv_img, R.mipmap.ic_launcher)
.setText(R.id.tv_title, item.getData().getTitle())
.setText(R.id.tv_content, item.getData().getContent());
break;
}
}
}
第三步:最后当然是在我们的Activity里编写代码
public class MainActivity extends AppCompatActivity {
private RecyclerView recyclerView;
private List<Model> datas01;
private List<MyMultipleItem> datas02;
private MultipleItemAdapter adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//初始化RecyclerView
recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
//模拟的假数据(实际开发中当然是从网络获取数据)
datas01 = new ArrayList<>();
Model model;
for (int i = 0; i < 30; i++) {
model = new Model();
model.setTitle("我是第" + i + "条标题");
model.setContent("第" + i + "条内容");
datas01.add(model);
}
datas02 = new ArrayList<>();
//这里我是随机给某一条目加载不同的布局
for (int i = 0; i < 30; i++) {
if (i % 3 == 0) {
datas02.add(new MyMultipleItem(MyMultipleItem.FIRST_TYPE, null));
} else if (i % 7 == 0) {
datas02.add(new MyMultipleItem(MyMultipleItem.SECOND_TYPE, null));
} else {
datas02.add(new MyMultipleItem(MyMultipleItem.NORMAL_TYPE, datas01.get(i)));
}
}
//创建布局管理
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
layoutManager.setOrientation(LinearLayoutManager.VERTICAL);
recyclerView.setLayoutManager(layoutManager);
//创建适配器
adapter = new MultipleItemAdapter(datas02);
//给RecyclerView设置适配器
recyclerView.setAdapter(adapter);
}
}
作者:猜火车_
链接:https://www.jianshu.com/p/1e20f301272e
來源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。
抄录整理:鹿人_甲