android 手把手教你打造万能的ListView GridView的适配器

时间:2023-01-23 07:49:09

我们在开发项目中,listview是最常用的控件,也算是android中控件这块属于比较难点的,或容易出现问题的地方,说他难不是说简单的使用,那个相信大家会,比如结合网络访问,异步记载图片,滑动卡以及嵌套等,今天是写一篇关于ListView,GridView通用的adapter,而在adapter中getCount,getItem,getItemId,getView要复写这4个方法,下面看下我们adapter类常写的方式

布局文件

<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"
   >
    <ListView
        android:id="@+id/lv"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
         />
</RelativeLayout>


item布局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >
<TextView 
   android:id="@+id/tvContent"
   android:layout_width="fill_parent"
   android:layout_height="40dp"
   />
</LinearLayout>

MainActivity,class

public class MainActivity extends Activity {
private ListView lv;
private List<String> datas;
private LayoutInflater inflater;
  @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
lv = (ListView) findViewById(R.id.lv);
inflater = LayoutInflater.from(this);
datas = new ArrayList<String>();
datas.add("hello world");
datas.add("hello world");
datas.add("hello world");
datas.add("hello world");
datas.add("hello world");
datas.add("hello world");
datas.add("hello world");
datas.add("hello world");
datas.add("hello world");
datas.add("hello world");
datas.add("hello world");
datas.add("hello world");
datas.add("hello world");
datas.add("hello world");
MyAdapter adapter = new MyAdapter();
lv.setAdapter(adapter);
}
class MyAdapter extends BaseAdapter {
@Override
public int getCount() {
return datas.size();
}
@Override
public Object getItem(int position) {
return datas.get(position);
}


@Override
public long getItemId(int position) {
return position;
}


@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
if(convertView==null){
holder = new ViewHolder();
convertView = inflater.inflate(R.layout.item, null);
holder.tvContent = (TextView) convertView.findViewById(R.id.tvContent);
convertView.setTag(holder);
}else{
holder = (ViewHolder) convertView.getTag();
}
holder.tvContent.setText(datas.get(position));
return convertView;
}
}

static class ViewHolder{
TextView tvContent;
}
}

在adapter中我们发现其实adapter真正每次实现不一样的是getView()方法,我们要对其优化封装,现在想办法封装一个通用的adapter,如何抽取呢?其实无非是java面向对象的几个基本特征:抽象,封装,继承,多态,当我们看见一段代码在每个类中都会出现,那么我们就最好把这些相同的代码封装到一个父类中,而每个类针对不同的业务方法不一样,就让其子类去实现,那么这个时候我们想到了java中的抽象类了,现在就对adapter方法进行简单的封装下:

MyBaseAdapter.java

public abstract class MyBaseAdapter<T> extends BaseAdapter {
private List<T> datas;
private Context context;
public MyBaseAdapter(List<T> datas, Context context) {
super();
this.datas = datas;
this.context = context;
}
@Override
public int getCount() {
return datas.size();
}
@Override
public Object getItem(int position) {
return datas.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public abstract View getView(int position, View convertView, ViewGroup parent);
}

 我们发现以后只要继承此MyBaseAdapter就可以少写getCount,getItem,getItemId。只要复写getView()方法即可,这比之前优化进了一下步,现在我们看看getView()方法代码怎么写的

@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
if(convertView==null){
holder = new ViewHolder();
convertView = inflater.inflate(R.layout.item, null);
holder.tvContent = (TextView) convertView.findViewById(R.id.tvContent);
convertView.setTag(holder);
}else{
holder = (ViewHolder) convertView.getTag();
}
holder.tvContent.setText(datas.get(position));
return convertView;
}
}

static class ViewHolder{
TextView tvContent;
}

现在发现getView方法中与ViewHoler类绑定在一起,我们知道ViewHolder是通过converetView.setTag()与其绑定,这样当convertView复用时,就不用findViewById了,因为view的id都封装在ViewHolder中了,所以针对不同的listview,我们肯定要写不同的ViewHolder类了,那么现在想打造一个常用的ViewHolder,继续优化getView方法,

也就是说,实际上们每个convertView会绑定一个ViewHolder对象,这个viewHolder主要用于帮convertView存储布局中的控件,那么ViewHolder类一个是提供setTag方法一个是对外提供一个获取View对象的方法,根据这2点我们写一个ViewHolder类出来,

public class ViewHolder {
private View convertView;
public void setTag(){
convertView.setTag(this);
}

public View getView(){

return convertView;
}
}

这个ViewHolder肯定是不能用的,convertView这个变量是从来的,一是通过set方法从外界设置进来,二是通过构造函数传递进来,但是如果是通过构造函数对convertView进行初始化,那么我们可以随便创建ViewHolder对象了,那么构造函数就不能对外使用了,这个时候我们就把构造函数做成private,所以我们只要对外提供一种方法就是获取到ViewHoler,而ViewHolder是和convertView绑定的,如果做缓存的都知道,先看看缓存有没有,如果没有就去就去new,在方法内部做一个判断即可,说了这么多,把刚才的思路转换成代码实现如下:

ViewHolder:

<span style="font-size:18px;">public class ViewHolder {
private View mConvertView;
private ViewHolder(Context context, ViewGroup parent, int layoutId, int position)
{
mConvertView = LayoutInflater.from(context).inflate(layoutId, parent, false);
mConvertView.setTag(this);
}
/**
* 获取一个ViewHolder对象
*/
public static ViewHolder get(Context context, View convertView,
ViewGroup parent, int layoutId, int position)
{
if (convertView == null)
{
return new ViewHolder(context, parent, layoutId, position);
}
return (ViewHolder) convertView.getTag();
}
public View getConvertView()
{
return mConvertView;
}
}</span>

adapter的getView方法如下:

@Override
public View getView(int position, View convertView, ViewGroup parent) {
//实例化一个viewHolder
ViewHolder viewHolder = ViewHolder.get(getApplicationContext(), convertView, parent,
R.layout.item, position);
return viewHolder.getConvertView();
}
此时心中一震窃喜,我很牛逼比,运行跑起来界面空白,我的去啊,赶脚好牛逼,怎么能让view设置数据到界面上呢,这又是一个废闹细胞的时刻到了,心想一个layout那么多view,怎么办啊,神啊,我突然想到了集合的概念,牛比不,抽烟去,等下继续写!集合?List能用么,不可以为什么呢?因为每个item上的view,是通过id去寻找的,这么一说我马上想到了用Map集合,但是我就不用,我使用android一个自带的容器,叫做 SparseArray这个类

public class ViewHolder {
private View mConvertView;
private final SparseArray<View> mViews;
private ViewHolder(Context context, ViewGroup parent, int layoutId, int position)
{
mViews = new SparseArray<View>();
mConvertView = LayoutInflater.from(context).inflate(layoutId, parent, false);
mConvertView.setTag(this);
}
/**
* 获取一个ViewHolder对象
*/
public static ViewHolder get(Context context, View convertView,
ViewGroup parent, int layoutId, int position)
{
if (convertView == null)
{
return new ViewHolder(context, parent, layoutId, position);
}
return (ViewHolder) convertView.getTag();
}
public View getConvertView()
{
return mConvertView;
}
/**
* 通过控件的Id获取对于的控件,如果没有则加入views
*/
public <T extends View> T $(int viewId)
{

View view = mViews.get(viewId);
if (view == null)
{
view = mConvertView.findViewById(viewId);
mViews.put(viewId, view);
}
return (T) view;
}
}

Adapter中getView方法如下:

class MyAdapter extends MyBaseAdapter<String> {
public MyAdapter(List<String> datas, Context context) {
super(datas, context);
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
   //实例化一个viewHolder  
       ViewHolder viewHolder = ViewHolder.get(getApplicationContext(), convertView, parent,  
               R.layout.item, position); 
       TextView tvContent = viewHolder.$(R.id.tvContent);
       tvContent.setText(datas.get(position));
       return viewHolder.getConvertView(); 
}
}
}

现在一个adapter中就这几行代码了,是不是之前adapter中的代码要少很多呢,大牛都是写很少的代码实现业务,我们在向大牛的方向前行!