主要声明三点
第一:
关于本文的BaseRecyclerViewAdapterHelper用法,自然是转载的官方原文。或许有的人会很疑惑原文写的那么清除那么完善,为什么我还要写一篇几乎一模一样的?是为了蹭热度让更多的人关注?有更多的点击量?当然不是,我对这么虚伪的东西是非常不齿的。那么自然我有我的原因:
1、我自己其实是个小白,刚入这行不久,由于接触这行时间也不长,加上对这行脑袋瓜比较笨,所以学起来特别吃力,很多东西明明刚用过的,转过头又忘了怎么回事,所以也是特别痛苦;
2、正如第一条所说的,由于太笨,所以想着记录一些笔记来帮助日后的开发,所以…..对,严格意义上来说这只是我的个人学习笔记;
3、当然,由于毕竟是在开源的环境下,相信有的同行搜索相关文章时是会搜到我的文章,所以我写的还是相对比较正规的,如果只是我自己看,我是不会这样记笔记的,以防不幸看到我的文章的童鞋看不懂而吐槽。
1、本文其实只是按照自己的排版习惯重新排版而已;
2、在重写排版的基础上,增加了一些简单的Demo,以及我在写Demo过程中碰上的少量的问题,当然这些问题仅仅是我个人使用的时候碰上的,正如第一条所说的,我本身也是小白,也是在学习阶段,对很多东西也是理解的不够,所以会导致或多或少的问题,所以我像我其他文章一样,我还是强烈建议不幸看到本文的同行去看原文学习使用:BRVAH官方使用指南(持续更新)http://www.jianshu.com/p/b343fcff51b0
第三:
该适配器虽号称万能适配器,但并非万能,有很多功能本身是实现不了的,还会和其他开源的冲突,比如不能和XRecyclerView一起使用(至少我用的时候是真的冲突)。
第四:
关于文章中所提到的 “bug”,我相信都不存在,只是由于时间关系,无心再去研究,但是我也坚信你继续看本文,还是能够知道该适配器的使用方式的。
allprojects {
repositories {
...
maven { url "https://jitpack.io" }
}
}
dependencies {
......
compile 'com.github.CymChad:BaseRecyclerViewAdapterHelper:2.9.22'
}
配置没配置好,有几种情况:
1. 只配置了 dependencies
2. 配置 repositories,但是位置错了,build.gradle(Project:XXXX) 文件下的repositories有两个,一个是buildscript下面的,一个是allprojects下面的,要配置到allprojects下面才是对的。
3. 版本号前面多一个v,这个是我的锅,在2.1.2版本之前都是带v的,之后(包含2.1.2)都不需要带v。
和原生的adapter相比,减少70%的代码量。
首先看一段使用示例代码:
public class QuickAdapter extends BaseQuickAdapter<Status, BaseViewHolder> {
public QuickAdapter() {
super(R.layout.tweet, DataServer.getSampleData());
}
@Override
protected void convert(BaseViewHolder viewHolder, Status item) {
viewHolder.setText(R.id.tweetName, item.getUserName())
.setText(R.id.tweetText, item.getText())
.setText(R.id.tweetDate, item.getCreatedAt())
.setVisible(R.id.tweetRT, item.isRetweet())
.linkify(R.id.tweetText);
Glide.with(mContext).load(item.getUserAvatar()).crossFade().into((ImageView) viewHolder.getView(R.id.iv));
}
}
1、使用: 首先需要继承BaseQuickAdapter,然后BaseQuickAdapter
第一个泛型Status是数据实体类型,第二个BaseViewHolder是ViewHolder其目的是为了支持扩展ViewHolder。
2、赋值:可以直接使用viewHolder对象点相关方法通过传入viewId和数据进行,方法支持链式调用。如果是加载网络图片或自定义view可以通过viewHolder.getView(viewId)获取该控件。
viewHolder.getLayoutPosition() 获取当前item的position
要是刚接触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>
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="wrap_content"
android:padding="5dp">
<ImageView
android:id="@+id/iv_img"
android:layout_width="150dp"
android:layout_height="80dp" />
<TextView
android:id="@+id/tv_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginLeft="10dp"
android:layout_marginTop="10dp"
android:layout_toRightOf="@+id/iv_img"
android:text="我是标题"
android:textColor="#f00"
android:textSize="20sp" />
<TextView
android:id="@+id/tv_content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/tv_title"
android:layout_marginLeft="10dp"
android:layout_marginTop="10dp"
android:layout_toRightOf="@id/iv_img"
android:text="我是描述" />
RelativeLayout>
public class Model {
private String title;
private String content;
private String imgUrl;
//生成set、get方法
......
}
public class MyAdapter extends BaseQuickAdapter<Model, BaseViewHolder> {
public MyAdapter(@LayoutRes int layoutResId, @Nullable List 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();
}
}
public class MainActivity extends AppCompatActivity {
private RecyclerView recyclerView;
private List 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);
}
}
上文中描述的就是使用BaseRecyclerViewAdapterHelper最基本的用法,因为怕刚接触Android的兄弟们不明朗或不相信这么简单的用法,所以做了上节简单的demo示例用法。
那么使用列表当然少不了点击事件,不论是整个条目的点击事件还是条目中子控件的点击事件,该适配器对点击事件也是做了及简化的处理:
//条目点击事件
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();
}
});
//条目长按事件
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;
}
});
@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();
}
});
这里和子控件点击事件是一样的,只是将 点击 变成 长按 就可以了
首先:在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;
}
});
官方文档上没说,但是 其实这里可以用很常规的方法处理,就是通过判断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。
给条目添加动画也是比较常见的需求
开启动画(默认为渐显效果)
//开启动画(默认为渐显效果)
adapter.openLoadAnimation();
public static final int ALPHAIN = 0x00000001;
/**
* Use with {@link #openLoadAnimation}
*/
public static final int SCALEIN = 0x00000002;
/**
* Use with {@link #openLoadAnimation}
*/
public static final int SLIDEIN_BOTTOM = 0x00000003;
/**
* Use with {@link #openLoadAnimation}
*/
public static final int SLIDEIN_LEFT = 0x00000004;
/**
* Use with {@link #openLoadAnimation}
*/
public static final int SLIDEIN_RIGHT = 0x00000005;
//使用缩放动画
adapter.openLoadAnimation(BaseQuickAdapter.SCALEIN);
//自定义动画效果
adapter.openLoadAnimation(new BaseAnimation() {
@Override
public Animator[] getAnimators(View view) {
return new Animator[]{
ObjectAnimator.ofFloat(view, "scaleY", 1, 0.5f, 1),
ObjectAnimator.ofFloat(view, "scaleX", 1, 0.5f, 1)
};
}
});
//设置重复执行动画
adapter.isFirstOnly(false);
adapter.setNotDoAnimationCount(count);
由于进入界面的item都是很多的速度进来的所以不会出现滑动显示的依次执行动画效果,这个时候会一起执行动画,如果觉得这样的效果不好可以使用setNotDoAnimationCount设置第一屏item不执行动画,但是如果需要依次执行动画可以重写startAnim让第一个屏幕的item动画延迟执行即可。
@Override
protected void startAnim(Animator anim, int index) {
super.startAnim(anim, index);
if (index < count)
anim.setStartDelay(index * 150);
}
mQuickAdapter.addHeaderView(headerView);
mQuickAdapter.addFooterView(footerView);
mQuickAdapter.removeHeaderView(headerView);
mQuickAdapter.removeFooterView(footerView);
mQuickAdapter.removeAllHeaderView();
mQuickAdapter.removeAllFooterView();
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>
1、这里的下拉加载我个人觉得不好用,并不像我们常用的那种下拉加载(当然有可能是我没有合理使用)
2、所以如果是需要下拉刷新的功能,我个人会使用SwipeRefreshLayout
mAdapter.setUpFetchEnable(true);
mAdapter.setUpFetchListener(new BaseQuickAdapter.UpFetchListener() {
@Override
public void onUpFetch() {
startUpFetch();
}
});
private void startUpFetch() {
count++;
/**
* set fetching on when start network request.
*/
mAdapter.setUpFetching(true);
/**
* get data from internet.
*/
mRecyclerView.postDelayed(new Runnable() {
@Override
public void run() {
mAdapter.addData(0, genData());
/**
* set fetching off when network request ends.
*/
mAdapter.setUpFetching(false);
/**
* set fetch enable false when you don't need anymore.
*/
if (count > 5) {
mAdapter.setUpFetchEnable(false);
}
}
}, 300);
}
mAdapter.setStartUpFetchPosition(2);
由于我还没碰到过这样的需求,所以对 分组布局 这个概念还不是很了解;
当然,我对概念都不知道,所以也不知道是什么样的效果,当然也就没有写demo(主要是时间问题)。
以下是正文
实体类必须继承SectionEntity:
public class MySection extends SectionEntity<Video> {
private boolean isMore;
public MySection(boolean isHeader, String header) {
super(isHeader, header);
}
public MySection(Video t) {
super(t);
}
}
public class SectionAdapter extends BaseSectionQuickAdapter<MySection> {
public SectionAdapter(int layoutResId, int sectionHeadResId, List data) {
super(layoutResId, sectionHeadResId, data);
}
@Override
protected void convert(BaseViewHolder helper, MySection item) {
helper.setImageUrl(R.id.iv, (String) item.t);
}
@Override
protected void convertHead(BaseViewHolder helper,final MySection item) {
helper.setText(R.id.header, item.header);
helper.setOnClickListener(R.id.more, new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(context,item.header+"more..",Toast.LENGTH_LONG).show();
}
});
}
多种类型条目的列表是我们日常开发中非常常见的功能,当然该适配器也给我们提供了相应的方法。
使用该适配器设置不同类型条目有两种方式,一种 耦合了实体类,一种是设置代理,这里两种方式我都会演示一遍。
public class MultipleItem implements MultiItemEntity {
public static final int TEXT = 1;
public static final int IMG = 2;
private int itemType;
public MultipleItem(int itemType) {
this.itemType = itemType;
}
@Override
public int getItemType() {
return itemType;
}
}
public class MultipleItemQuickAdapter extends BaseMultiItemQuickAdapter<MultipleItem, BaseViewHolder> {
public MultipleItemQuickAdapter(List data) {
super(data);
addItemType(MultipleItem.TEXT, R.layout.text_view);
addItemType(MultipleItem.IMG, R.layout.image_view);
}
@Override
protected void convert(BaseViewHolder helper, MultipleItem item) {
switch (helper.getItemViewType()) {
case MultipleItem.TEXT:
helper.setImageUrl(R.id.tv, item.getContent());
break;
case MultipleItem.IMG:
helper.setImageUrl(R.id.iv, item.getContent());
break;
}
}
}
multipleItemAdapter.setSpanSizeLookup(new BaseQuickAdapter.SpanSizeLookup() {
@Override
public int getSpanSize(GridLayoutManager gridLayoutManager, int position) {
return data.get(position).getSpanSize();
}
});
如果使用多布局出现这个NotFoundException异常,有可能是addItemType()两个参数写反了。
以上就是官方文档给出的多布局设置方式的介绍,可能对于像我这样Android小白会看的一脸懵逼,所以我写了一个很简单的Demo方便理解。
1.我们的数据不直接传给适配器,而是通过MultiItemEntity来传递
2.适配器构造里必须绑定type和layout的关系
第一步:创建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 datas01;
private List 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);
}
}
最后:已近完事了,可以运行了,
从上文中,我们可以知道,其实多布局的本质还是要用某一个变量来区分,上述的方法是使用专门提供得MultiItemEntity接口,让我们实现,从而进行区分;
既然是某一个变量来区分,那我们能不能不实现MultiItemEntity接口,直接在适配器里进行区分呢?
官方还给出了一种方式,给BaseQuickAdapter 设置代理
首先我们先看一下官方给出的文档(分三步)
public class MultiDelegateAdapter extends BaseQuickAdapter<Entity, BaseViewHolder> {
public MultiDelegateAdapter() {
super(null);
//Step.1
setMultiTypeDelegate(new MultiTypeDelegate() {
@Override
protected int getItemType(Entity entity) {
//根据你的实体类来判断布局类型
return entity.type;
}
});
//Step.2
getMultiTypeDelegate()
.registerItemType(Entity.TEXT, R.layout.item_text_view)
.registerItemType(Entity.IMG, R.layout.item_image_view);
}
@Override
protected void convert(BaseViewHolder helper, Entity entity) {
//Step.3
switch (helper.getItemViewType()) {
case Entity.TEXT:
// do something
break;
case Entity.IMG:
// do something
break;
}
}
}
首先:我给Model类添加一个变量来区分类型
public class Model {
public static final int FIRST_TYPE = 1;
public static final int SECOND_TYPE = 2;
public static final int NORMAL_TYPE = 3;
//添加类型变量
public int type;
private String title;
private String content;
private String imgUrl;
//set get方法
......
}
其次:编写适配器
public class MultiDelegateAdapter extends BaseQuickAdapter<Model, BaseViewHolder> {
public MultiDelegateAdapter(@Nullable List data) {
super(data);
setMultiTypeDelegate(new MultiTypeDelegate() {
@Override
protected int getItemType(Model entity) {
//根据你的实体类来判断布局类型
return entity.type;
}
});
getMultiTypeDelegate()
.registerItemType(Model.FIRST_TYPE, R.layout.first_type_layout)
.registerItemType(Model.SECOND_TYPE, R.layout.second_type_layout)
.registerItemType(Model.NORMAL_TYPE, R.layout.item_rv);
}
@Override
protected void convert(BaseViewHolder helper, Model item) {
switch (helper.getItemViewType()) {
case Model.FIRST_TYPE:
Log.i("tag", "FIRST_TYPE===============" + helper.getLayoutPosition());
break;
case Model.SECOND_TYPE:
Log.i("tag", "SECOND_TYPE===============" + helper.getLayoutPosition());
break;
case Model.NORMAL_TYPE:
helper.setImageResource(R.id.iv_img, R.mipmap.ic_launcher)
.setText(R.id.tv_title, item.getTitle())
.setText(R.id.tv_content, item.getContent());
break;
}
}
}
最后:在Activity中使用该适配器
public class MainActivity extends AppCompatActivity {
private RecyclerView recyclerView;
private List datas;
private MultiDelegateAdapter 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 < 30; i++) {
model = new Model();
model.setTitle("我是第" + i + "条标题");
model.setContent("第" + i + "条内容");
//这里随机给某一条目设置不同布局类型
if (i == 3 || i == 10 || i == 13) {
model.setType(Model.FIRST_TYPE);
} else if (i == 5 || i == 8 || i == 20) {
model.setType(Model.SECOND_TYPE);
} else {
model.setType(Model.NORMAL_TYPE);
}
datas.add(model);
}
//创建布局管理
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
layoutManager.setOrientation(LinearLayoutManager.VERTICAL);
recyclerView.setLayoutManager(layoutManager);
//创建适配器
adapter = new MultiDelegateAdapter(datas);
//给RecyclerView设置适配器
recyclerView.setAdapter(adapter);
}
}
// 没有数据的时候默认显示该布局
mQuickAdapter.setEmptyView(getView());
1、在我写demo的时候,设置空布局时就碰上一个bug,即使我所有的布局中的相关控件高度都设置成填充父窗体,但是显示时并没有填充整个界面,而仅仅是包裹内容;
2、但是当我在我现在开发的项目中设置空布局时又确实是填充整个父窗体,这就让我很迷惑了,狂躁了好久,看了好多次demo代码也没找到原因,最后还是没有找到原因…….
3、当然,因为我也是一个Android新人,有很多地方我都需要继续努力学习,或许只是一个很简单、很低级的错误导致没有填充父窗体,如果看到这篇文章的朋友你也遇到过相似的问题,而且又正好知道原因,还希望你能多多指教。
本人测试了一下,拖拽交换位置效果不错,但是滑动删除效果我测试的是有问题的,条目显示会错乱,被删除的条目还会出现空白条目现象…….(疑惑)不知道是不是我打开方式不对???
拖拽和滑动删除的回调方法:
OnItemDragListener onItemDragListener = new OnItemDragListener() {
@Override
public void onItemDragStart(RecyclerView.ViewHolder viewHolder, int pos){}
@Override
public void onItemDragMoving(RecyclerView.ViewHolder source, int from, RecyclerView.ViewHolder target, int to) {}
@Override
public void onItemDragEnd(RecyclerView.ViewHolder viewHolder, int pos) {}
}
OnItemSwipeListener onItemSwipeListener = new OnItemSwipeListener() {
@Override
public void onItemSwipeStart(RecyclerView.ViewHolder viewHolder, int pos) {}
@Override
public void clearView(RecyclerView.ViewHolder viewHolder, int pos) {}
@Override
public void onItemSwiped(RecyclerView.ViewHolder viewHolder, int pos) {}
};
public class ItemDragAdapter extends BaseItemDraggableAdapter<String, BaseViewHolder> {
public ItemDragAdapter(List data) {
super(R.layout.item_draggable_view, data);
}
@Override
protected void convert(BaseViewHolder helper, String item) {
helper.setText(R.id.tv, item);
}
}
mAdapter = new ItemDragAdapter(mData);
ItemDragAndSwipeCallback itemDragAndSwipeCallback = new ItemDragAndSwipeCallback(mAdapter);
ItemTouchHelper itemTouchHelper = new ItemTouchHelper(itemDragAndSwipeCallback);
itemTouchHelper.attachToRecyclerView(mRecyclerView);
// 开启拖拽
mAdapter.enableDragItem(itemTouchHelper, R.id.textView, true);
mAdapter.setOnItemDragListener(onItemDragListener);
// 开启滑动删除
mAdapter.enableSwipeItem();
mAdapter.setOnItemSwipeListener(onItemSwipeListener);
重写 ItemDragAndSwipeCallback 里的onMove()方法,return true即可
本人表示不想贴出demo了,因为我没尝试着写demo试试,看文档说明感觉和多布局貌似是一个套路….(感觉是…)
例子:三级菜单
// if you don't want to extent a class, you can also use the interface IExpandable.
// AbstractExpandableItem is just a helper class.
public class Level0Item extends AbstractExpandableItem<Level1Item> {...}
public class Level1Item extends AbstractExpandableItem<Person> {...}
public class Person {...}
public class ExpandableItemAdapter extends BaseMultiItemQuickAdapter<MultiItemEntity, BaseViewHolder> {
public ExpandableItemAdapter(List data) {
super(data);
addItemType(TYPE_LEVEL_0, R.layout.item_expandable_lv0);
addItemType(TYPE_LEVEL_1, R.layout.item_expandable_lv1);
addItemType(TYPE_PERSON, R.layout.item_text_view);
}
@Override
protected void convert(final BaseViewHolder holder, final MultiItemEntity item) {
switch (holder.getItemViewType()) {
case TYPE_LEVEL_0:
....
//set view content
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int pos = holder.getAdapterPosition();
if (lv0.isExpanded()) {
collapse(pos);
} else {
expand(pos);
}
}});
break;
case TYPE_LEVEL_1:
// similar with level 0
break;
case TYPE_PERSON:
//just set the content
break;
}
}
adapter.expandAll();
// 获取当前父级位置
int cp = getParentPosition(person);
// 通过父级位置找到当前list,删除指定下级
((Level1Item)getData().get(cp)).removeSubItem(person);
// 列表层删除相关位置的数据
getData().remove(holder.getLayoutPosition());
// 更新视图
notifyDataSetChanged();
public class MovieViewHolder extends BaseViewHolder
public class DataBindingUseAdapter extends BaseQuickAdapter<Movie, DataBindingUseAdapter.MovieViewHolder>
由于adapter本身能力有限,我们又不想耦合view层所以有些需求是现实不了,于是合作了一些优秀开源库,为开发者提供更多可能性。以下扩展框架都是有结合BRVAH的demo。
PinnedSectionItemDecoration:一个强大的粘性标签库
EasyRefreshLayout:这个库让你轻松实现下拉刷新和上拉更多
EasySwipeMenuLayout:独立的侧滑删除
转载:https://www.jianshu.com/p/1e20f301272e