在开发中不管是LitView还Recycleview都需要设置item,但是很多时候item不是固定,而是根据服务器的返回的数据动态配置的在一个Recycleview中肯能有多种形式,例如正文形式,图集形式,短视频形式,文本形式,以及广告插入,专题、通栏、banner图、图布局和根布局等多种形式。有的还需要下拉刷新、上拉加载等定义的形式等等。本篇文章将围绕着这些问题而展开,封装一个强大的Adapter来适配Adapter的各种配置,支持下拉刷新、上拉加载,支持加载中布局,加载更多布局,加载失败布局,加载空数据布局。
理念
不管是头布局,脚布局,图文布局,还是轮播图等,把每一种类型的布局都看作一个Item的类型,这样,每次添加一个内容的时候都可以把它当作Recycleview的一个Item,只需要给数据集合中添加一个Item,然后刷新数据,这个添加根据服务器返回数据是动态添加,因此可以动态适配各种布局类型。
分析
在Adapter中有三大核心类:
- getItemViewType : 根据position返回的一个int值,代表该position下的ViewHolder类型
- onCreateViewHolder :创建对应ViewType的viewholder
- onBinderViewHolder :绑定数据到对应的ViewHolder
Item中方法抽取
/**
* item中每个条目的必要方法
*/
public interface Item {
/**
* 获取viewType
* @return
*/
public int getItemType();
/**
* 创建ViewHolder
* @param parent
* @param viewType
* @return
*/
public BaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType);
/**
* 数据绑定
* @param holder
* @param position
*/
public void onBindViewHolder(BaseViewHolder holder, int position);
}
创建基类BaseItem
基类中主要是为了让其他类进行继承实现一些基本的共有操作,需要实现Item接口。这里主要有两点:1、构造方法中传递数据参数,有参构造表示服务器返回数据集合,提供给子类绑定数据使用,无参构造表示该item类型布局中不需要参数,即静态布局。
2、创建onCreateViewHolder中的布局,交个给子类加载每种布局。
public abstract class BaseItem<T> implements Item {
public T data;
public BaseItem(T data) {
this.data = data;
}
public BaseItem() {
}
@Override
public BaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(inflateViewId(), parent, false);
return new BaseViewHolder(view);
}
@Override
public void onBindViewHolder(BaseViewHolder holder, int position) {
}
/**
* 创建viewholder的View
*/
public abstract int inflateViewId();
}
3、子类实现
如果子类为静态固定布局,则使用无参构造,否则使用有参构造。如下静态布局和动态布局:
public class FooterItem extends BaseItem<DataBean.ResultBean.ContentBean>{
@Override
public int getItemType() {
return HolderType.FooterItem.getValue();
}
@Override
public void onBindViewHolder(BaseViewHolder holder, int position) {
}
@Override
public int inflateViewId() {
return R.layout.item_list_footer;
}
}
public class TextItem extends BaseItem<DataBean.ResultBean.ContentBean> {
public TextItem(DataBean.ResultBean.ContentBean data) {
super(data);
}
@Override
public int getItemType() {
return HolderType.TextItem.getValue();
}
@Override
public void onBindViewHolder(BaseViewHolder holder, int position) {
holder.setText(R.id.text_content2,data.getText());
}
@Override
public int inflateViewId() {
return R.layout.item_list_content2;
}
}
BaseViewHolder封装
ViewHolder主要用来承载每个item布局中的控件的绑定和数据的设置。这里进行了基本的常用数据绑定的封装,如:获取布局,获取图片,设置文字等,设置图片统一使用Picasso进行设置,便于管理。
1、构造方法
public BaseViewHolder(View itemView) {
super(itemView);
views = new SparseArray<>();
mItemView = itemView;
}
2、View获取
public View getItemView() {
return mItemView;
}
3、获取控件
public View getView(int resId) {
return retrieveView(resId);
}
private <T extends View> T retrieveView(int viewId) {
View view = views.get(viewId);
if (view == null) {
view = mItemView.findViewById(viewId);
views.put(viewId, view);
}
return (T) view;
}
4、图片设置
public void setImageUrl(int resId, String url) {
ImageView imageView = getImageView(resId);
if (imageView == null) {
throw new ExceptionInInitializerError("ImageView id is not exist");
}
Picasso.with(mItemView.getContext()).load(url).into(imageView);
}
5、文字设置
public void setText(int resId, CharSequence text) {
getTextView(resId).setText(text);
}
BaseAdapter
BaseAdapter继承自RecyclerView.Adapter<BaseViewHolder>,重写了onCreateViewHolder、onBindViewHolder、getItemCount、getItemViewType四个方法,提供了数据设置的方法和集合操作的方法,包括设置数据、获取数据、添加集合、移除集合等等。
- onCreateViewHolder:返回每个item的ViewHolder
@Override
public BaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
for (int i = 0; i < getItemCount(); i++) {
if (viewType == mData.get(i).getItemType()) {
return mData.get(i).onCreateViewHolder(parent, viewType);
}
}
throw new RuntimeException("onCreateViewHolder viewType is wrong");
}
- onBindViewHolder:对每个Item进行数据的绑定
@Override
public void onBindViewHolder(BaseViewHolder holder, int position) {
mData.get(position).onBindViewHolder(holder, position);
}
- getItemViewType:返回对应的Item的数据类型
@Override
public int getItemViewType(int position) {
return mData.get(position).getItemType();
}
SimpleAdapter
以上内容基本上封装了Adapter的操作,能够适应各种item的要求,为了更加完美,加上加载中,数据为空,数据错误,加载更多四中item,都继承自BaseItem。SimpleAdapter继承自BaseAdapter,在构造方法中初始化了四个布局,提供了一系列显示和隐藏的方法,详情看源码,在使用的时候继承自他就可以。
下拉刷新
下拉刷新使用Google原生控件SwipeRefreshLayout,需要Recycleview的父控件设置为SwipeRefreshLayout,就可以实现下拉刷新效果。
1、进度条的颜色设置
setColorSchemeResources(@ColorRes int... colorResIds)
2、下拉监听
mRefreshlayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
//停止刷新进度
mRefreshlayout.setRefreshing(false);
//执行下拉刷新操作
}
});
上拉加载
上拉加载用于实现分页的功能,数据的加载不是一次性的,而是分条加载的,当Recycleview下拉到底侧的时候,就会显示加载更多再次请求数据用于显示。在RecyclerView中,提供了一个监听接口:RecyclerView.OnScrollListener我们可以通过这个接口来创建我们上拉加载更多的功能。他提供了两个方法:onScrolled,onScrollStateChanged。
1、onScrolled
当Recycleview滑动的时候调用,dx表示x轴滑动的距离,dy表示y轴滑动的距离,可以根据dy判断是否是底部向上拉取操作。
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
//手指正向上滚动
if (dy > 0) {
isSlidingToLast = true;
} else {
isSlidingToLast = false;
}
}
2、onScrollStateChanged
Recycleview状态变化时候回调,一共三个状态:
- SCROLL_STATE_IDLE:当前的recycleView不滑动(滑动已经停止时)
- SCROLL_STATE_DRAGGING: 当前的recycleView被拖动滑动
- SCROLL_STATE_SETTLING:当前的recycleView滚动到某个位置的过程,但没有被触摸滚动
(1)获取最后一个可见的item位置
int lastVisibleItem =mLayoutManager().findLastVisibleItemPosition();
(2)获取Recycleview所有的item个数
int itemCount = mLayoutManager.getItemCount();
(3)条件判断
最后一个可见的item位置 = 总item数 -1
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
//如果为null,第一次运行,确定布局类型
int mLastVisiblePosition = -1;
if (layoutManager instanceof LinearLayoutManager) {
mLastVisiblePosition = ((LinearLayoutManager) layoutManager).findLastVisibleItemPosition();
} else if (layoutManager instanceof GridLayoutManager) {
mLastVisiblePosition = ((GridLayoutManager) layoutManager).findLastVisibleItemPosition();
} else if (layoutManager instanceof StaggeredGridLayoutManager) {
StaggeredGridLayoutManager staggeredGridLayoutManager = (StaggeredGridLayoutManager) layoutManager;
int[] lastPositions = new int[staggeredGridLayoutManager.getSpanCount()];
staggeredGridLayoutManager.findLastVisibleItemPositions(lastPositions);
mLastVisiblePosition = findMax(lastPositions);
} else {
throw new RuntimeException("LayoutManager should be init");
}
RecyclerView.LayoutManager manager = recyclerView.getLayoutManager();
int itemCount = manager.getItemCount();
if (newState == RecyclerView.SCROLL_STATE_SETTLING && mLastVisiblePosition == itemCount - 1
&& isSlidingToLast && canShowLoadMore()) {
//展示加载更多进度条
showLoadMore();
//进行加载更多分页数据请求
onLoadingMore();
}
}
BaseFragment
BaseFragment主要对fragment进行封装,包含了下拉刷新上拉加载,数据处理的逻辑,使用起来更加方便,子类子需要继承他,重写他的抽象方法就可以了,不需要额外重写其他生命周期的方法。
public class MyFragment extends BaseFragment<DataBean.ResultBean.ContentBean> {
/**
* 下拉刷新逻辑处理
*/
@Override
protected void onPullRefresh() {
}
/**
* 上拉加载逻辑处理
*/
@Override
protected void onLoadingMore() {
}
/**
* 初始化数据,访问网络,网络请求
*/
@Override
protected void initData() {
}
/**
* 对网络请求的数据进行处理,返回Adapter所有要传递参数的集合
* @param list
* @return
*/
@Override
protected List<Item> getItem(List<DataBean.ResultBean.ContentBean> list) {
return null;
}
}
源码和demo地址:https://github.com/yoonerloop/ConfigItemAdapter点击打开链接
测试服务端json数据:
{
"code": "200",
"msg": "success",
"result": {
"banner": {
"info": "年底大促销",
"url": [
"https://www.baidu.com/img/bd_logo1.png",
"https://www.baidu.com/img/bd_logo1.png",
"https://www.baidu.com/img/bd_logo1.png"
]
},
"content": [
{
"des": "景网,全球最大的创意图片库,提供大量免费促销活动图片供用户下载使用,是图片分享和图片交易的专业图片网站。",
"image": "http://img.taopic.com/uploads/allimg/120727/201995-120HG1030762.jpg",
"type": "1"
},
{
"des": "景网,全球最大的创意图片库,提供大量免费促销活动图片供用户下载使用,是图片分享和图片交易的专业图片网站。",
"image": "http://img02.tooopen.com/images/20150507/tooopen_sy_122395947985.jpg",
"type": "1"
},
{
"text": "亚瑟从出生开始就被尤瑟王托付给魔法师梅林抚养,梅林便偷偷将襁褓中的亚瑟带离廷塔杰尔城堡,来到一个隐秘的地方将亚瑟抚养成人,亚瑟王与梅林关系十分良好,传说中梅林可以随意改变自己容貌的年轻或年老,不过他大部分时间都以年迈的老者示人。",
"type": "2"
},
{
"des": "景网,全球最大的创意图片库,提供大量免费促销活动图片供用户下载使用,是图片分享和图片交易的专业图片网站。",
"image": "http://img07.tooopen.com/images/20170316/tooopen_sy_201956178977.jpg",
"type": "1"
},
{
"type": "3",
"url": [
"https://www.baidu.com/img/bd_logo1.png",
"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1514463320357&di=49be8eb330e4cafb461b337cecba1e2c&imgtype=0&src=http%3A%2F%2Fpic21.nipic.com%2F20120525%2F7512072_140351788322_2.jpg",
"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1514463320352&di=671e9cbfeeffcb20af986fe7dee91af5&imgtype=0&src=http%3A%2F%2Fdown1.sucaitianxia.com%2Fpsd02%2Fpsd181%2Fpsds40246.jpg",
"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1514463320352&di=671e9cbfeeffcb20af986fe7dee91af5&imgtype=0&src=http%3A%2F%2Fdown1.sucaitianxia.com%2Fpsd02%2Fpsd181%2Fpsds40246.jpg"
]
}
]
}
}