大家看到这个标题是不是觉得很诧异呢?什么叫终极适配器,其实就是这种适配器是万能的,所有需要使用适配器的组件,都可用这一个适配器就行。
既然这样,就来讲讲吧。
效果:
当然这是个简单的布局,用普通的适配器也可以实现,这里只是用它来做个例子,用终极适配器的话,以后你换其他布局,适配器是不用变的,减少了很多代码。
首先普通的适配器的写法是:
public class MyAdapter extends BaseAdapter{
private Context mContext;
private List<Bean> mDatas;
private LayoutInflater mLayoutInflater;
private int mResId;
public MyAdapter(Context context,List<Bean> data, int resId) {
mContext = context;
mDatas = data;
mLayoutInflater = LayoutInflater.from(mContext);
mResId = resId;
}
@Override
public int getCount() {
return mDatas.size();
}
@Override
public Object getItem(int position) {
return mDatas.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
Bean bean = mDatas.get(position);
ViewHolder holder;
if (convertView == null) {
convertView= mLayoutInflater.inflate(R.layout.myitem, parent, false);
holder = new ViewHolder();
holder.title_Tv = (TextView) convertView.findViewById(R.id.title_Tv);
holder.desc_Tv = (TextView) convertView.findViewById(R.id.desc_Tv);
holder.time_Tv = (TextView) convertView.findViewById(R.id.time_Tv);
holder.phone_Tv = (TextView) convertView.findViewById(R.id.phone_Tv);
convertView.setTag(holder);
}else {
holder = (ViewHolder) convertView.getTag();
}
holder.title_Tv.setText(bean.getTitle());
holder.desc_Tv.setText(bean.getDesc());
holder.time_Tv.setText(bean.getTime());
holder.phone_Tv.setText(bean.getPhone());
return convertView;
}
static class ViewHolder {
TextView title_Tv; //标题
TextView desc_Tv; //简介
TextView time_Tv; //时间
TextView phone_Tv; //电话
}
}
制作终极适配器的步骤:
首先我们得分析哪些代码是不变的,哪些是可变的,这样才能确定哪些代码能够减少。
这三个重写的方法应该是不变的。
@Override
public int getCount() {
return mDatas.size();
}
@Override
public Object getItem(int position) {
return mDatas.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
还有这些代码
ViewHolder holder;
if (convertView == null) {
convertView= mLayoutInflater.inflate(R.layout.myitem, parent, false);
holder = new ViewHolder();
holder.title_Tv = (TextView) convertView.findViewById(R.id.title_Tv);
holder.desc_Tv = (TextView) convertView.findViewById(R.id.desc_Tv);
holder.time_Tv = (TextView) convertView.findViewById(R.id.time_Tv);
holder.phone_Tv = (TextView) convertView.findViewById(R.id.phone_Tv);
convertView.setTag(holder);
}else {
holder = (ViewHolder) convertView.getTag();
}
只不过findViewById我们需要处理一下。所以我们应该把这些不变的代码抽取出来,不应该让用户重复写这些代码。
把这些代码抽取出来,当然这些代码都应该放在ViewHolder中
ViewHolder holder;
if (convertView == null) {
convertView= mLayoutInflater.inflate(R.layout.myitem, parent, false);
holder = new ViewHolder();
}else {
holder = (ViewHolder) convertView.getTag();
}
新建一个类 ViewHolder.java
public class ViewHolder {
private View mConvertView;
//ViewHolder构造函数,只有当convertView为空的时候才创建
public ViewHolder(Context context,View convertView, ViewGroup parent, int layouId) {
convertView = LayoutInflater.from(context).inflate(layouId,parent,false);
convertView.setTag(this); //将其setTag()
mConvertView = convertView;
}
//返回一个ViewHolder对象
public static ViewHolder getHolder(Context context, View convertView, ViewGroup parent, int layoutId) {
if (convertView == null) {
return new ViewHolder(context,convertView,parent,layoutId);
}else {
return (ViewHolder) convertView.getTag();
}
}
}
传过来的参数包括:Context context, View convertView, ViewGroup parent, int layoutId,这些参数都是加载布局文件所需要的。
然后就是这些代码需要抽取了:
holder.title_Tv = (TextView) convertView.findViewById(R.id.title_Tv);
holder.desc_Tv = (TextView) convertView.findViewById(R.id.desc_Tv);
holder.time_Tv = (TextView) convertView.findViewById(R.id.time_Tv);
holder.phone_Tv = (TextView) convertView.findViewById(R.id.phone_Tv);
新建方法:
public class ViewHolder {
//用来存布局中的各个组件,以键值对形式
private SparseArray<View> mViews = new SparseArray<>();
//返回一个View的子类对象,因为不确定用户布局有什么组件,相当于findViewById
//这里返回一个泛型,也可以返回一个View或Object
public <T extends View>T getView(int resId) {
View view = mViews.get(resId); //从集合中取出这个组件
if (view == null) { //如果为空,说明为第一屏
view = mConvertView.findViewById(resId); //从convertView中找
mViews.put(resId,view); //再将其以键值对存进去
}
return (T) view;
}
}
接下里的这个返回值就容易了,直接返回就行了
public class ViewHolder {
/** * @return 返回convertView */
public View getConvertView() {
return mConvertView;
}
}
这部分工作都完了,就只留下了为组件设置数据的那段代码了,这段代码由于是可变的,应该让用户来做,所以设置为抽象方法。
新建一个类 CommonAdapter.java 继承BaseAdapter:
public abstract class CommonAdapter<T> extends BaseAdapter {
//需要显示的数据,List中的类型为泛型,因为不知道用户的封装Bean
private List<T> mDatas;
//上下文
private Context mContext;
//布局文件Id
private int mLayoutId;
public CommonAdapter(Context context,List<T> data,int layoutId) {
mDatas = data;
mContext = context;
mLayoutId = layoutId;
}
@Override
public int getCount() {
return mDatas.size();
}
@Override
public Object getItem(int position) {
return mDatas.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = ViewHolder.getHolder(mContext,convertView, parent, mLayoutId);
setDatas(holder,getItem(position));
return holder.getConvertView();
}
/** * 为各个item中的控件设置数据 * @param holder ViewHolder * @param object 从集合中所取的一个对象 */
public abstract void setDatas(ViewHolder holder, Object object);
}
到这里就把刚才那段代码全部抽取出来了,那再来看一下如何使用吧。
使用步骤:
①添加ListView, 找ListView 这些步骤是一样的。
②新建一个 Adapter 继承 CommonAdapter
public class MagicAdapter extends CommonAdapter<Bean> {
public MagicAdapter(Context context, List<Bean> data, int layoutId) {
super(context, data, layoutId);
}
@Override
public void setDatas(ViewHolder holder, Object object) {
Bean bean = (Bean) object;
((TextView)holder.getView(R.id.title_Tv)).setText(bean.getTitle());
((TextView)holder.getView(R.id.desc_Tv)).setText(bean.getDesc());
((TextView)holder.getView(R.id.time_Tv)).setText(bean.getTime());
((TextView)holder.getView(R.id.phone_Tv)).setText(bean.getPhone());
}
}
注:
③为ListView设置适配器
//为listView设置适配器
mListView.setAdapter(new MagicAdapter(this,mDatas,R.layout.listview_item));
到这里就实现了万能的适配器了,是不是减少了很多代码。我们知道java三大特性是封装、继承、多态。这篇例子可以锻炼一下大家的封装能力。大家试试吧
核心代码:
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.briup.universal.MainActivity">
<ListView android:id="@+id/listView" android:layout_width="wrap_content" android:layout_height="wrap_content"/>
</RelativeLayout>
listview_item.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">
<TextView android:layout_marginTop="12dp" android:layout_marginLeft="12dp" android:textColor="#000" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="20sp" android:text="新技能get" android:id="@+id/title_Tv"/>
<TextView android:layout_marginLeft="12dp" android:layout_marginTop="10dp" android:textSize="18sp" android:textColor="#CDCDCD" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="打造万能适配器" android:layout_below="@id/title_Tv" android:id="@+id/desc_Tv"/>
<TextView android:id="@+id/time_Tv" android:layout_marginTop="10dp" android:layout_marginLeft="12dp" android:text="2016-5-21" android:layout_below="@id/desc_Tv" android:layout_width="wrap_content" android:layout_height="wrap_content"/>
<TextView android:id="@+id/phone_Tv" android:text="10086" android:layout_marginRight="12dp" android:layout_alignParentRight="true" android:layout_alignBaseline="@id/time_Tv" android:layout_width="wrap_content" android:layout_height="wrap_content"/>
</RelativeLayout>
MainActivity.java
public class MainActivity extends AppCompatActivity {
private ListView mListView;
private List<Bean> mDatas;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initData();
initView();
//为listView设置适配器
mListView.setAdapter(new MagicAdapter(this,mDatas,R.layout.listview_item));
}
/** * 初始化组件 */
private void initView() {
mListView = (ListView) findViewById(R.id.listView);
}
/** * 初始化数据 */
private void initData() {
mDatas = new ArrayList<>();
//模拟数据
for (int i=0; i < 6; i++) {
Bean bean = new Bean("新技能" +i,"打造万能适配器"+ i,"2016-5-22","10086");
mDatas.add(bean);
}
}
}
ViewHolder.java
public class ViewHolder {
private View mConvertView;
//用来存布局中的各个组件,以键值对形式
private SparseArray<View> mViews = new SparseArray<>();
//ViewHolder构造函数,只有当convertView为空的时候才创建
public ViewHolder(Context context,View convertView, ViewGroup parent, int layouId) {
convertView = LayoutInflater.from(context).inflate(layouId,parent,false);
convertView.setTag(this); //将其setTag()
mConvertView = convertView;
}
//返回一个ViewHolder对象
public static ViewHolder getHolder(Context context, View convertView, ViewGroup parent, int layoutId) {
if (convertView == null) {
return new ViewHolder(context,convertView,parent,layoutId);
}else {
return (ViewHolder) convertView.getTag();
}
}
//返回一个View的子类对象,因为不确定用户布局有什么组件,相当于findViewById
//这里返回一个泛型,也可以返回一个View或Object
public <T extends View>T getView(int resId) {
View view = mViews.get(resId); //从集合中取出这个组件
if (view == null) { //如果为空,说明为第一屏
view = mConvertView.findViewById(resId); //从convertView中找
mViews.put(resId,view); //再将其以键值对存进去
}
return (T) view;
}
/** * @return 返回convertView */
public View getConvertView() {
return mConvertView;
}
}
CommonAdapter.java
public abstract class CommonAdapter<T> extends BaseAdapter {
//需要显示的数据,List中的类型为泛型,因为不知道用户的封装Bean
private List<T> mDatas;
//上下文
private Context mContext;
//布局文件Id
private int mLayoutId;
public CommonAdapter(Context context,List<T> data,int layoutId) {
mDatas = data;
mContext = context;
mLayoutId = layoutId;
}
@Override
public int getCount() {
return mDatas.size();
}
@Override
public Object getItem(int position) {
return mDatas.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = ViewHolder.getHolder(mContext,convertView, parent, mLayoutId);
setDatas(holder,getItem(position));
return holder.getConvertView();
}
/** * 为各个item中的控件设置数据 * @param holder ViewHolder * @param object 从集合中所取的一个对象 */
public abstract void setDatas(ViewHolder holder, Object object);
}
Bean.java
public class Bean {
private String title; //标题
private String desc; //简介
private String time; //时间
private String phone; //电话
public Bean(String title, String desc, String time, String phone) {
this.title = title;
this.desc = desc;
this.time = time;
this.phone = phone;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
public String getTime() {
return time;
}
public void setTime(String time) {
this.time = time;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
}
MagicAdapter.java
public class MagicAdapter extends CommonAdapter<Bean> {
public MagicAdapter(Context context, List<Bean> data, int layoutId) {
super(context, data, layoutId);
}
@Override
public void setDatas(ViewHolder holder, Object object) {
Bean bean = (Bean) object;
((TextView)holder.getView(R.id.title_Tv)).setText(bean.getTitle());
((TextView)holder.getView(R.id.desc_Tv)).setText(bean.getDesc());
((TextView)holder.getView(R.id.time_Tv)).setText(bean.getTime());
((TextView)holder.getView(R.id.phone_Tv)).setText(bean.getPhone());
}
}