前言
不知道你们有没有遇到过这样一个需求,轮播图上有好几种布局,反正我遇到了。刚听见这个需求的时候就想,多布局轮播图,那不是可以用fragment实现么,但又一想,那不得写好几个fragment,多麻烦啊,一点都不优雅,不符合咱的身份。然后回去好好想了想,多布局,RecyclerView的Adpater不是可以根据viewType的不同而加载不同的布局,实现多布局功能吗,那轮播图的PageAdapter是不是也可以呢?说干就干。
效果
实现
PageApdater是在instantiateItem()方法中通过inflate()获取到布局,然后设置到父布局中显示,那么我们可以仿照RecyclerView.Adapter,对每个item设置不同的type,然后在inflate()的时候根据type不同区获取对应的布局,然后添加到父布局中。
先定义接口,子类要实现的方法
/**
* 获取item的type
*/
protected abstract int getItemViewType(int position);
/**
* 获取item的布局
*/
protected abstract int getLayoutId(int position, int itemViewType);
/**
* 对布局子view进行操作
*/
protected abstract void handleView(int position, int itemViewType, View parent);
然后在instantiateItem()方法中
@NonNull
@Override
public Object instantiateItem(@NonNull ViewGroup container, int position) {
int itemViewType = getItemViewType(position);
//获取视图布局id
int layoutId = getLayoutId(position, itemViewType);
//得到要对应显示的视图对象
View convertView = LayoutInflater.from(container.getContext()).inflate(layoutId, null);
//获取到type对应布局后对子view进行操作
handleView(position, itemViewType, convertView);
container.addView(convertView);
return convertView;
}
然后在子类中,只需要实现父类定义的几个接口
@Override
protected int getItemViewType(int position) {
return list.get(position).getItemType();
}
@Override
protected int getLayoutId(int position, int itemViewType) {
switch (itemViewType) {
case ScenicRotationInfo.ADVERT:
return R.layout.item_shuffling;
case ScenicRotationInfo.OTHER:
return R.layout.item_channel;
default:
return 0;
}
}
@Override
protected void handleView(int position, int itemViewType, View parent) {
ScenicRotationInfo info = list.get(position);
switch (itemViewType) {
case ScenicRotationInfo.ADVERT:
ImageView imageView = parent.findViewById(R.id.imageView_shuffling);
Glide.with(mContext).load(info.url).into(imageView);
break;
case ScenicRotationInfo.OTHER:
TextView textView = parent.findViewById(R.id.tvChannel);
textView.setText(info.title);
break;
}
}
怎么样,是不是很简单,是不是很像我们写的RecyclerView的Adpater,不不不,还差一点,那就是对View进行复用,减少布局的加载和findViewById()。毕竟没有性能优化的Adpater是没有灵魂的。
首先要缓存布局
/**
* 缓存的视图
*/
private SparseArray<ArrayList<View>> itemViewCache = new SparseArray<>();
//修改convertView 获取方式,先从缓存集合中去取,集合内不存在再去通过inflate()进行加载
ArrayList<View> convertViews = itemViewCache.get(itemViewType);
if (convertViews == null || convertViews.isEmpty()) {
//获取视图布局id
int layoutId = getLayoutId(position, itemViewType);
//得到我们要返回出去的视图对象可以是一个容器 也可以是一个view
convertView = LayoutInflater.from(container.getContext()).inflate(layoutId, null);
} else {
// 复用视图
// 取出移除时缓存的视图
// 通过remove得到,避免集合重复添加长度过长
convertView = convertViews.remove(convertViews.size() - 1);
}
@Override
public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
View view = (View) object;
container.removeView(view);
//缓存视图以重用
//当该视图移除时,添加到缓存集合中
int itemViewType = getItemViewType(position);
ArrayList<View> convertViews = itemViewCache.get(itemViewType);
if (convertViews == null) {
convertViews = new ArrayList<>();
itemViewCache.put(itemViewType, convertViews);
}
convertViews.add(view);
}
然后通过ViewHolder复用View减少findViewById(),修改instantiateItem()方法中代码
@NonNull
@Override
public Object instantiateItem(@NonNull ViewGroup container, int position) {
View convertView;
BaseViewHolder viewHolder;
int itemViewType = getItemViewType(position);
ArrayList<View> convertViews = itemViewCache.get(itemViewType);
if (convertViews == null || convertViews.isEmpty()) {
//获取视图布局id
int layoutId = getLayoutId(position, itemViewType);
//得到我们要返回出去的视图对象可以是一个容器 也可以是一个view
convertView = LayoutInflater.from(container.getContext()).inflate(layoutId, null);
if (convertView != null) {
viewHolder = new BaseViewHolder(convertView);
//将视图和viewHolder绑定
convertView.setTag(viewHolder);
} else {
throw new NullPointerException("inflate layout(id:" + layoutId + ")");
}
} else {
// 复用视图
// 取出移除时缓存的视图
// 通过remove得到,避免集合重复添加长度过长
convertView = convertViews.remove(convertViews.size() - 1);
viewHolder = (BaseViewHolder) convertView.getTag();
}
handleView(viewHolder, position, convertView);
container.addView(convertView);
return convertView;
}
以及接口handleView()。
protected abstract void handleView(BaseViewHolder viewHolder, int position, int itemViewType);
@Override
protected void handleView(BaseViewHolder viewHolder, int position, int itemViewType) {
ScenicRotationInfo info = list.get(position);
switch (itemViewType) {
case ScenicRotationInfo.ADVERT:
ImageView imageView = viewHolder.getView(R.id.imageView_shuffling);
Glide.with(mContext).load(info.url).into(imageView);
break;
case ScenicRotationInfo.OTHER:
viewHolder.setText(R.id.tvChannel, info.title);
break;
}
}
我这里的BaseViewHolder是项目中引用了 BaseRecyclerViewAdapterHelper 这个库,里面提供了这个类,很棒的一个Powerful and flexible RecyclerAdapte。如果你不想引用的话,自己写一个简单的ViewHolder也可以。
public class BaseViewHolder extends ViewHolder {
/**
* Views indexed with their IDs
*/
private SparseArray<View> views;
/**
* use itemView instead
*/
public View convertView;
public BaseViewHolder(@NonNull View itemView) {
super(itemView);
this.views = new SparseArray<>();
convertView = itemView;
}
public <T extends View> T getView(@IdRes int viewId) {
View view = views.get(viewId);
if (view == null) {
view = itemView.findViewById(viewId);
views.put(viewId, view);
}
return (T) view;
}
}
最后优化一下代码,这样比较优雅。
/**
* Created by yangqc on 2020/5/18
* 多布局的PageAdapter
*
* @Author yangqc
*/
public abstract class BaseMultiItemPageAdapter extends PagerAdapter {
protected Context mContext;
protected LayoutInflater inflater;
/**
* 获取item的type
*/
protected abstract int getItemViewType(int position);
/**
* 获取item的布局
*/
protected abstract int getLayoutId(int position, int itemViewType);
/**
* 对布局子view进行操作
*/
protected abstract void handleView(BaseViewHolder viewHolder, int position, int itemViewType);
/**
* 缓存的视图
*/
private SparseArray<ArrayList<View>> itemViewCache = new SparseArray<>();
@Override
public boolean isViewFromObject(@NonNull View view, @NonNull Object object) {
return view == object;
}
@NonNull
@Override
public Object instantiateItem(@NonNull ViewGroup container, int position) {
if (mContext == null)
mContext = container.getContext();
if (inflater == null)
inflater = LayoutInflater.from(mContext);
View convertView;
BaseViewHolder viewHolder;
int itemViewType = getItemViewType(position);
ArrayList<View> convertViews = itemViewCache.get(itemViewType);
if (convertViews == null || convertViews.isEmpty()) {
//获取视图布局id
int layoutId = getLayoutId(position, itemViewType);
//得到我们要返回出去的视图对象可以是一个容器 也可以是一个view
convertView = inflater.inflate(layoutId, null);
if (convertView != null) {
viewHolder = new BaseViewHolder(convertView);
//将视图和viewHolder绑定
convertView.setTag(viewHolder);
} else {
throw new NullPointerException("inflate layout(id:" + layoutId + ")");
}
} else {
// 复用视图
// 取出移除时缓存的视图
// 通过remove得到,避免集合重复添加长度过长
convertView = convertViews.remove(convertViews.size() - 1);
viewHolder = (BaseViewHolder) convertView.getTag();
}
handleView(viewHolder, position, itemViewType);
container.addView(convertView);
return convertView;
}
@Override
public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
View view = (View) object;
container.removeView(view);
//缓存视图以重用
//当该视图移除时,添加到缓存集合中
int itemViewType = getItemViewType(position);
ArrayList<View> convertViews = itemViewCache.get(itemViewType);
if (convertViews == null) {
convertViews = new ArrayList<>();
itemViewCache.put(itemViewType, convertViews);
}
convertViews.add(view);
}
}