之前我们也说过Android-RecyclerView通用适配器BaseAdapter-多绘制类型-开始篇,通用适配器有两个重要的点:
1. 适配器的创建
2.数据的绑定
所以我们的BaseAdapter相当于需要进行
1. 封装好的BaseViewHolder的创建
2. 封装好的BaseDataModel提供的onBindViewHolder的方法(相当于onBindData方法)
3. 再说一点资源释放的地方onViewDetachedFromWindow方法中调用BaseDataModel提供的releaseResource方法。释放除了资源,还有设置的监听事件都可以释放(这些都是内存的相关优化的点)
4. 另外Context建议采用弱引用,避免持有上下文导致的内存泄漏
这里小白做个简单记录,封装也不是很好,另外可能还得逐渐深入和学习...
所以我们的BaseAdapter可以如下:
import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.ViewGroup;
import java.lang.ref.WeakReference;
import java.util.List;
/*
*@Description: 基础适配器
*@Author: hl
*@Time: 2018/11/1 11:42
*/
public class BaseAdapter extends RecyclerView.Adapter<BaseViewHolder> {
private List<BaseDataModel> baseMulDataModelList = null;
private WeakReference<Context> contextWeakReference = null;
private BaseViewHolder.HandleView handleView = null;
public BaseAdapter(Context context,
List<BaseDataModel> baseMulDataModelList,
BaseViewHolder.HandleView handleView){
this.contextWeakReference = new WeakReference<>(context);
this.baseMulDataModelList = baseMulDataModelList;
this.handleView = handleView;
}
public BaseAdapter(Context context,
List<BaseDataModel> baseMulDataModelList){
this.contextWeakReference = new WeakReference<>(context);
this.baseMulDataModelList = baseMulDataModelList;
}
@Override
public BaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return new BaseViewHolder(contextWeakReference.get(),
DrawTypeManager.onCreateViewHolder(parent, viewType),
handleView);
}
@Override
public void onBindViewHolder(BaseViewHolder holder, int position) {
baseMulDataModelList.get(position).onBindViewHolder(holder, handleView, position);
}
@Override
public int getItemCount() {
return (null == baseMulDataModelList) ? 0 : baseMulDataModelList.size();
}
@Override
public int getItemViewType(int position) {
return baseMulDataModelList.get(position).getDrawType();
}
/**
* Item资源释放
* @param holder
*/
@Override
public void onViewDetachedFromWindow(BaseViewHolder holder) {
super.onViewDetachedFromWindow(holder);
///< 释放资源
int position = holder.getAdapterPosition();
///< 越界检查
if(position < 0 || position >= baseMulDataModelList.size()){
return;
}
baseMulDataModelList.get(position).releaseResource();
}
/**
* 适配器资源释放
* @param recyclerView
*/
@Override
public void onDetachedFromRecyclerView(RecyclerView recyclerView) {
super.onDetachedFromRecyclerView(recyclerView);
if (null != contextWeakReference){
contextWeakReference.clear();
contextWeakReference = null;
}
}
}
而我们的BaseViewHolder可以干什么呢?既然是布局创建容器,自然需要提供控件的查找(findViewById), 另外还得提供监听事件,同时我们自定义了一些回调(比如重新动态设置控件的宽高、文本高亮等方法).
import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.util.SparseArray;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
/*
*@Description: 基础ViewHolder
*@Author: hl
*@Time: 2018/11/1 17:21
*/
public class BaseViewHolder<T extends BaseDataModel> extends RecyclerView.ViewHolder{
private Context context;
private SparseArray<View> views;
private View itemView;
private HandleView handleView;
public BaseViewHolder(Context context, View itemView, HandleView handleView) {
super(itemView);
this.context = context;
this.views = new SparseArray<>();
this.itemView = itemView;
this.handleView = handleView;
}
public View getItemView() {
return itemView;
}
public View getView(int resId) {
return retrieveView(resId);
}
public TextView getTextView(int resId){
return retrieveView(resId);
}
public ImageView getImageView(int resId){
return retrieveView(resId);
}
public Button getButton(int resId){
return retrieveView(resId);
}
protected <V extends View> V retrieveView(int viewId){
View view = views.get(viewId);
if(view == null){
view = itemView.findViewById(viewId);
views.put(viewId, view);
///< 动态对控件进行设置
if (null != handleView){
handleView.readjust(view);
}
}
return (V) view;
}
public Context getContext() {
return context;
}
/**
* 处理View相关
*/
public interface HandleView<T extends BaseDataModel>{
/**
* 重新调整控件信息
*/
public void readjust(View view);
/**
* 关键字高亮
*/
public void highLight(View view, String text);
/**
* 点击事件
*/
public void onclick(View view, int postion, T baseDataModel);
}
}
由于是多布局类型,所以我们增加了一个DrawTypeManager作为布局类型的管理。小白看的有些网友是没有将这个单独提出来做,个人是想单独提出来进行管理。所以就由DrawTypeManager提供视图的创建并返回该布局创建的视图:
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.lieyunwang.finance.R;
import java.util.HashMap;
/*
*@Description: 绘制类型管理器
*@Author: hl
*@Time: 2018/10/31 13:22
*/
public class DrawTypeManager extends BaseDrawType{
public static final int INFO_NEWS_BANNER_TYPE = 1;
public static final int INFO_NEWS_CONTENT_TYPE = 2;
public static final int INFO_LIEZUAN_CONTENT_TYPE = 3;
///< RV渲染类型对应的布局ID
private static final HashMap<Integer, Integer> type2layoutid = new HashMap<Integer, Integer>(){
{
put(INFO_NEWS_BANNER_TYPE, R.layout.fragment_info_news_banner_adapter);
put(INFO_NEWS_CONTENT_TYPE, R.layout.fragment_info_news_adapter);
put(INFO_LIEZUAN_CONTENT_TYPE, R.layout.info_liezuan_info_adapter);
}
};
/**
* 获取布局视图对象
* @param parent
* @param viewType
* @return
*/
public static View onCreateViewHolder(ViewGroup parent, int viewType){
return LayoutInflater.from(parent.getContext())
.inflate(getLayoutId(viewType),
parent, false);
}
/**
* 根据viewType获取布局id
* @param viewType
* @return
*/
private static int getLayoutId(int viewType) {
if (type2layoutid.containsKey(viewType)){
return type2layoutid.get(viewType);
}
throw new RuntimeException("未定义布局!");
}
}
当然不一定好,后面慢慢完善,说不定还是别的人比较好呢。
基本上创建ViewHolder就差不多可以了。然后就是数据的定义,我们的数据肯定是需要提供绘制类型drawType,然后提供绑定的方法onBindViewHolder, 还得提供释放资源的方法releaseResource。根据具体还可进行扩展:
/*
*@Description: 基础数据标准
*@Author: hl
*@Time: 2018/11/5 16:12
*/
public abstract class BaseDataModel {
protected int drawType = -1;
public int getDrawType() {
return drawType;
}
public void setDrawType(int drawType) {
this.drawType = drawType;
}
public abstract void onBindViewHolder(BaseViewHolder holder, BaseViewHolder.HandleView handleView, int position);
public abstract void releaseResource();
}
这里我做成抽象类,所以的具体实体类都需要实现绑定和释放的两个方法,然后drawType提供一个设置和返回的方法,这个地方其实也可以做成抽象类的。只是小白想绘制类型由父类进行管理!!!当然这个还得再考虑考虑...
还有一点就是DrawTypeManager继承了BaseDrawType,BaseDrawType中默认设置了几个类型:
/*
*@Description: 绘制类型(布局统一配置)
*@Author: hl
*@Time: 2018/10/31 13:17
*/
public class BaseDrawType {
public static final int ERROR_TYPE = Integer.MAX_VALUE - 1;
public static final int EMPTY_TYPE = Integer.MAX_VALUE - 2;
public static final int LOADING_TYPE = Integer.MAX_VALUE - 3;
public static final int LOAD_MORE_TYPE = Integer.MAX_VALUE - 4;
public static final int LOAD_NOMORE_TYPE = Integer.MAX_VALUE - 5;
}
这个后面做显示错误,空数据,加载中,加载更多会用到。
到这里基本上初步封装就算完事了。为了验证下,我们可以搞点测试数据..
简单测试数据
TestModel.java - 这是我工程的的测试数据,基本上就是这样的结构,重点就实现绑定和释放就行。看具体实际需要进行扩展。
public class TestModel extends BaseDataModel {
/**
* 基本属性相关 - 如果可以往父类搁置尽量搁置在父类
*/
private int id;
private String post_title;
private String time_ago;
private String poster;
private int type = 0;
///< type == -1, Banner资源相关 - 有则显示,无则不管
private List<String> titleList = null;
private List<String> imageList = null;
/**
* 控件相关
*/
private Banner banner = null;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getPost_title() {
return post_title;
}
public void setPost_title(String post_title) {
this.post_title = post_title;
}
public String getTime_ago() {
return time_ago;
}
public void setTime_ago(String time_ago) {
this.time_ago = time_ago;
}
public String getPoster() {
return poster;
}
public void setPoster(String poster) {
this.poster = poster;
}
public int getType() {
return type;
}
public void setType(int type) {
this.type = type;
}
public List<String> getTitleList() {
return titleList;
}
public void setTitleList(List<String> titleList) {
this.titleList = titleList;
}
public List<String> getImageList() {
return imageList;
}
public void setImageList(List<String> imageList) {
this.imageList = imageList;
}
public void setDrawType(int drawType) {
super.setDrawType(drawType);
}
public int getDrawType() {
int drawType = super.getDrawType();
if (-1 == drawType){
if (-1 == type){ ///< -1 - banner
super.setDrawType(DrawTypeManager.INFO_NEWS_BANNER_TYPE);
}else{ ///< 0 - normalItem 1 - 推广 2 - 专题
super.setDrawType(DrawTypeManager.INFO_NEWS_CONTENT_TYPE);
}
}
return super.getDrawType();
}
public String getTitle() {
return post_title;
}
public String getTime() {
return time_ago;
}
public String getImage() {
return poster;
}
public BaseDataModel getBaseDataModel(){
return this;
}
@Override
public void onBindViewHolder(final BaseViewHolder holder,
final BaseViewHolder.HandleView handleView,
final int position) {
///< Bannner轮播数据绑定
if (-1 == type){ ///< -1 - banner
banner = (Banner) holder.getView(R.id.finba_banner);
banner.setBannerTitles(getTitleList());
banner.setImages(getImageList())
.setDelayTime(3000)
.isAutoPlay(true)
.start();
///< 轮播点击事件
banner.setOnBannerListener(new OnBannerListener() {
@Override
public void OnBannerClick(int position) {
handleView.onclick(banner, position, getBaseDataModel());
}
});
}
///< 正常列表、推广、专题数据绑定
else{ ///< 0 - normalItem 1 - 推广 2 - 专题
ImageView posterIv = holder.getImageView(R.id.fina_posterIv);
GlideApp.with(holder.getContext())
.load(getPoster())
.placeholder(R.drawable.pic_default_02)
.into(posterIv );
}
///< 点击事件
holder.getItemView().setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
handleView.onclick(holder.getItemView(), position, getBaseDataModel());
}
});
}
}
@Override
public void releaseResource() {
///< TODO 资源释放可以在这里做:停止动画、还原某些动作呀
if (null != banner){
banner.stopAutoPlay();
banner.releaseBanner();
banner = null;
}
}
}
至于适配器的使用,其实如果你数据只是单纯展示,完全不用继承实现,直接可以使用BaseAdapter就行。
这里我们要做点事情,所以就继承扩展一下:
/**
* 测试数据适配器
*/
public class TestAdapter extends BaseAdapter {
public TestAdapter(Context context, List<BaseDataModel> baseMulDataModelList){
this(context, baseMulDataModelList, new BaseViewHolder.HandleView<TestModel>() {
@Override
public void readjust(View view) {
if (null != view){
if (view.getId() == R.id.fina_posterIv){ ///< 正常Item背景图片大小
///< @HC 设置详情预览图片大小
HC_Screen.setConstraintLayoutWH(view, 360, 240);
}
else if (view.getId() == R.id.fina_posterBigIv){ ///< 广告大图大小
///< 屏幕宽高固定比例
HC_Screen.setConstraintLayoutWHNoRatio(view,
HC_Screen.SCREEN_WIDTH - DensityUtil.dip2px(15),
(1 * (HC_Screen.SCREEN_WIDTH - DensityUtil.dip2px(15))) / 4);
}else if (view.getId() == R.id.finba_banner){ ///< 轮播图大小
Banner banner = (Banner) view;
banner.setBannerStyle(BannerConfig.CIRCLE_INDICATOR_TITLE);
banner.setImageLoader(new GlideImageLoader());
}
}
}
@Override
public void highLight(View view, String text) {
}
@Override
public void onclick(View view, int postion, TestModel baseDataModel) {
Log.e("test", "viewId=" + view.getId() + " postion=" + postion + " id=" + baseDataModel.getId());
}
});
}
public TestAdapter(Context context, List<BaseDataModel> baseMulDataModelList, BaseViewHolder.HandleView handleView) {
super(context, baseMulDataModelList, handleView);
}
}
只是看个样子,需要可以自己参考修改就行。。 总得来说使用还是相对以前简单,而且很多情况下都直接使用BaseAdapter,只需要修改下datamodel就行了。目前来看简单多了一些。
具体直接RecycleView就可以进行使用了,搞个简单布局就OK! 使用之前也说过。
如果在把封装性和扩展做得更好更简洁的话,应该还是比较不错的。另外后面会去参考一些大神的封装,借鉴一下宝贵经验,提升记几个!!!
Ok,到这里小白的封装之路踏出了一步两步好几步....只能加油了....别人的路也是这样走出来的!
网友评论