您的位置:首页 > 其它

RecyclerView Adapter简单封装

2017-01-02 01:17 351 查看

前言

今天是新年第一天,本来打算在上一年的最后一天写下来的,但是由于玩的时间长了一点,所以今天才写,算是在新的一年开一个好头,新年新气象嘛!

至于为什么要写这个文章呐!由于项目中以前都是用ListView实现的列表,很多东西都已经习惯化了,每次都是由的新的模块或者功能才使用RecyclerView,因此一直都没有完整的总结一遍,这次也是在一个新的项目中,准备完全采用RecyclerView来实现列表,因此初步的封装了一下。

为什么要封装呐?事实上,系统对该控件已经封装的很好了,直接拿过来用就行,使用方式也跟以前差不多,构造一个RecyclerView,再构造一个Adapter,当连续写了2个之后就不再想重复劳动了,因此对Adapter进行的简单的封装,

BaseAdapter

这里就大致罗列一下封装的过程,只是实现了初步的功能,后续的功能可以逐步添加,在最后会给出一个使用样例。

BaseAdapter

既然是对Adapter的简单封装,那第一步就是先来实现基类的Adapter,代码如下:

public class BaseAdapter<T> extends RecyclerView.Adapter<BaseViewHolder> {

/**
* data source
*/
public List<T> dataList;

/**
* onClick onLongClick callback
*/
public onItemClickListener listener;

/**
* constructor view holder delegate
*/
public BaseDelegate delegate;

/**
* constructor
*
* @param dataList
* @param delegate
*/
public BaseAdapter(List<T> dataList, BaseDelegate delegate) {
this(dataList, delegate, null);
}

/**
* constructor
*
* @param dataList
* @param delegate
* @param listener
*/
public BaseAdapter(List<T> dataList, BaseDelegate delegate, onItemClickListener listener) {
checkData(dataList);
this.delegate = delegate;
this.listener = listener;
}

/**
* just is empty
*
* @param dataList
*/
private void checkData(List<T> dataList) {
if (dataList == null) {
dataList = Collections.emptyList();
}
this.dataList = dataList;
}

/**
* set onclick & onLongClick callback
*
* @param listener
*/
public void setOnItemClickListener(onItemClickListener listener) {
this.listener = listener;
}

/**
* create view holder
*
* @param parent
* @param viewType
* @return
*/
@Override
public BaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return delegate.onCreateViewHolder(parent, viewType);
}

/**
* bind view holder
*
* @param holder
* @param position
*/
@Override
public void onBindViewHolder(BaseViewHolder holder, final int position) {
holder.onBindViewHolder(dataList.get(position));
if (listener != null && holder.enable()) {
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
listener.onClick(v, dataList.get(position));
}
});
holder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
return listener.onLongClick(v, dataList.get(position));
}
});
}
}

/**
* get item count
*
* @return
*/
@Override
public int getItemCount() {
return dataList.size();
}

/**
* get item view type
*
* @param position
* @return
*/
@Override
public int getItemViewType(int position) {
return delegate.getItemViewType(dataList.get(position));
}
}


1:这里首先是继承RecyclerView.Adapter,这里指定了一个BaseViewHolder,后续我们就来构建基类的ViewHolder。

2:传入需要操作的数据列表,这里加入了泛型,后续跟据实际使用的数据类进行使用。

3:由于RecyclerView不像ListView一样,默认实现了Item的点击事件,因此这里加入了点击事件的回调。

4:我们可以发现onCreateViewHolder,我们采用了代理来实现,等会我们会实现代理

5:在onBindViewHolder中,我们直接调用了Holder的bind过程,其次实现了点击事件。

BaseViewHolder

前面我们提到了统一实现一个基类ViewHolder,这里我们就看看BaseViewHolder怎么实现的。

public abstract class BaseViewHolder<T> extends RecyclerView.ViewHolder {

/**
* TODO
* single view may be direct construction, eg: TextView view = new TextView(context);
*
* @param parent current no use, may be future use
* @param view
*/
public BaseViewHolder(ViewGroup parent, View view) {
super(view);
findViews();
}

/**
* find all views
*/
public abstract void findViews();

/**
* bind view holder
*
* @param data
*/
public abstract void onBindViewHolder(T data);

/**
* holder click enable
*
* @return
*/
public boolean enable() {
return true;
}

}


1:这里首先还是继承自RecyclerView.ViewHolder

2:在构造函数中加入了控件的查找

3:BaseViewHolder是一个抽象类,有一个抽象方法onBindViewHolder,主要是为什么了实现界面绑定功能

4:最后有一个enable函数,这里主要是item是否可以有点击效果

onItemClickListener

由于RecyclerView默认没有点击事件的回调,因此需要我们自定义来实现该效果。这里我们加入点击事件的回调:

public interface onItemClickListener<T> {
void onClick(View v, T data);

boolean onLongClick(View v, T data);
}


1:功能能很简单,主要是实现了单击和长按的回调。也可以实现更改的回调

BaseDelegate

BaseDelegate也是一个抽象类,他主要是代理了Adapter中的create过程,代码如下:

public abstract class BaseDelegate<T> {

/**
* crate view holder by view type
*
* @param parent
* @param viewType
* @return
*/
public abstract BaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType);

/**
* get view type by data
*
* @param data
* @return
*/
public abstract int getItemViewType(T data);

/**
* get layout id by view type
*
* @param viewType
* @return
*/
public abstract int getLayoutId(int viewType);

/**
* get item view
*
* @param parent
* @param viewType
* @return
*/
public View getItemView(ViewGroup parent, int viewType) {
return LayoutInflater.from(parent.getContext()).inflate(getLayoutId(viewType), parent, false);
}
}


1:这里主要是代理了onCreateViewHolder,返回一个具体的ViewHolder

2:还代理了getItemViewType,具体根据实现返回type类型

使用

上面的代码都很简单,简单的封装了整个使用过程,那在项目中又是怎么来使用的呐?是否使用变动更麻烦了?比如我们要实现如下效果:





界面布局

这里我们首先来创一个Activity,布局文件如下:

<?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:background="#ffffff">

<include
android:id="@+id/my_toolbar"
layout="@layout/toolbar_title_layout"/>

<android.support.v7.widget.RecyclerView
android:id="@+id/setting_info"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@id/my_toolbar">

</android.support.v7.widget.RecyclerView>

</RelativeLayout>


主要包含了一个标题与一个RecyclerView

界面代码

public class MainActivity extends AppCompatActivity {

private RecyclerView settingInfo;

private List<ItemData> datas;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViews();
initTitle();
initData();
initRecyclerView();
}

/**
* find views
*/
private void findViews() {
settingInfo = (RecyclerView) findViewById(R.id.setting_info);
}

/**
* init title bar
*/
private void initTitle() {
Toolbar myToolbar = (Toolbar) findViewById(R.id.my_toolbar);
myToolbar.setNavigationIcon(android.support.v7.appcompat.R.drawable.abc_ic_ab_back_material);
myToolbar.setNavigationOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
setResult(RESULT_CANCELED);
finish();
}
});
ToolbarHelper.layoutTitleCenter(this, myToolbar, getString(R.string.title));
}

private void initData() {
datas = new ArrayList<>(20);
datas.add(new ItemData(0, SettingDelegate.SELF_INFO));
datas.add(new ItemData(0, SettingDelegate.SEPARATE_TYPE));
for (int i = 0; i < 3; ++i) {
datas.add(new ItemData(0, SettingDelegate.ARROW_TYPE, "我是箭头"));
datas.add(new ItemData(0, SettingDelegate.CHECK_TYPE, "选中我"));
datas.add(new ItemData(0, SettingDelegate.TOGGLE_TYPE, "开启"));
datas.add(new ItemData(0, SettingDelegate.SEPARATE_TYPE));
}
datas.add(new ItemData(0, SettingDelegate.LOGOUT_TYPE));
}

/**
* init
*/
private void initRecyclerView() {
settingInfo.setHasFixedSize(true);
DividerItemDecoration decor = new DividerItemDecoration(this, DividerItemDecoration.VERTICAL);
settingInfo.addItemDecoration(decor);
settingInfo.setLayoutManager(new LinearLayoutManager(this));
settingInfo.setAdapter(new BaseAdapter(datas, new SettingDelegate(), new onItemClickListener() {
@Override
public void onClick(View v, Object data) {
ArrowActivity.start(MainActivity.this);
}

@Override
public boolean onLongClick(View v, Object data) {
return false;
}
}));
}
}


1:查找了页面控件,并且设置了标题,构造了数据源

2:对RecyclerView设置了分割线,同时设置了布局管理器,这些都是基本功能,最后设置了Adapter,这里主要指定了数据源,同时设置了代理, 最后传入了点击事件的回调。

SettingDelegate

前面传入了SettingDelegate,这里我们看看SettingDelegate怎么实现的:

public class SettingDelegate extends BaseDelegate<ItemData> {

public static final int SEPARATE_TYPE = 0;

public static final int SELF_INFO = 1;

public static final int ARROW_TYPE = 2;

public static final int CHECK_TYPE = 3;

public static final int TOGGLE_TYPE = 4;

public static final int LOGOUT_TYPE = 5;

@Override
public BaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
switch (viewType) {
case SEPARATE_TYPE:
return new SeparateViewHolder(parent, getItemView(parent, viewType));
case SELF_INFO:
return new SelfInfoViewHolder(parent, getItemView(parent, viewType));
case ARROW_TYPE:
return new ArrowViewHolder(parent, getItemView(parent, viewType));
case CHECK_TYPE:
return new CheckViewHolder(parent, getItemView(parent, viewType));
case TOGGLE_TYPE:
return new ToggleViewHolder(parent, getItemView(parent, viewType));
case LOGOUT_TYPE:
return new LogoutViewHolder(parent, getItemView(parent, viewType));
}
return null;
}

@Override
public int getItemViewType(ItemData data) {
return data.holderType;
}

@Override
public int getLayoutId(int viewType) {
switch (viewType) {
case SEPARATE_TYPE:
return R.layout.view_holder_setting_separate;
case SELF_INFO:
return R.layout.view_holder_setting_self_info;
case ARROW_TYPE:
return R.layout.view_holder_setting_arrow;
case CHECK_TYPE:
return R.layout.view_holder_setting_check;
case TOGGLE_TYPE:
return R.layout.view_holder_setting_toggle;
case LOGOUT_TYPE:
return R.layout.view_holder_setting_logout;
}
return 0;
}
}


1:根据图片我们可以看到有多种类型,getItemViewType根据数据返回不同的type

2:onCreateViewHolder根据type返回每一种ViewHolder

3:这里我们指定了具体的数据类型ItemData,这个是随便构造的,真正项目中都是根据实际的数据来构造的

ItemData

这里的ItemData只是为了掩饰而构造的数据类型,代码如下:

public class ItemData {

public int tag;

public int holderType;

public String itemDesc;

public Object data;

public ItemData(int tag, int holderType) {
this.tag = tag;
this.holderType = holderType;
}

public ItemData(int tag, int holderType, String itemDesc) {
this.tag = tag;
this.holderType = holderType;
this.itemDesc = itemDesc;
}

public ItemData(int tag, int holderType, Object data) {
this.tag = tag;
this.holderType = holderType;
this.data = data;
}
}


SelfInfoViewHolder

这里有5种类型,我们就随机挑选一种来进行演示,这里我们选择了头像部分。代码如下:

public class SelfInfoViewHolder extends BaseViewHolder<ItemData> {

private ImageView headView;

private TextView nameGender;

private TextView birthday;

/**
* @param parent
* @param view
*/
public SelfInfoViewHolder(ViewGroup parent, View view) {
super(parent, view);
}

@Override
public void findViews() {
headView = (ImageView) itemView.findViewById(R.id.self_info_head_view);
nameGender = (TextView) itemView.findViewById(R.id.self_name_gender);
birthday = (TextView) itemView.findViewById(R.id.birthday);
}

@Override
public void onBindViewHolder(ItemData data) {

}

@Override
public boolean enable() {
return false;
}
}


1:首先是继承自BaseViewHolder,指定了数据类型

2:查找了页面控件,保证在bind的时候使用。

3:onBindViewHolder进行数据与控件的绑定展示

4:enable指定了该条目不需要点击事件

总结

上面只是对Adapter的简单封装,不过大部分的功能已经够用了,很多人可能会在每一个ViewHolder构造的时候,传入Layout布局,这里采用Delegate来统一返回布局,将布局放在了一起,同时针对每一个页面可以实现直接的delegate,这样就将类型,处理与布局统一管理。方便改代码的时候查找代码。

代码

这里只是大致列出了其中一部分代码,为了方便使用最后给出代码地址:传送门

后记

本来开始写的时候还是新年第一天,写完的时候已经变成了新年第二天,要是昨天开始写,那岂不是就写了一年。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: