Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android 通用ListView、GridView適配器

Android 通用ListView、GridView適配器

編輯:關於Android編程

1、簡述

在Android開發肯定避免不了與adapter打交道,一般都是繼承於BaseAdapter重寫裡面幾個方法,然後一個ListView對應一個Adapter,那自然項目中就出現一大堆Adapter,鑒於此Adapter出現大量的重復代碼是否有辦法可以簡化呢?答案是肯定的。

2、常規寫法

只貼adapter部分,layout就不貼了,因為不是重點。

 

public class SimpleAdapter extends BaseAdapter {
    public static class ViewHolder {
        public TextView tvName;
    }

    private List studentList;
    private LayoutInflater inflater;

    public SimpleAdapter(Context context, List students) {
        inflater = LayoutInflater.from(context);
        this.studentList = students;
    }

    @Override
    public int getCount() {
        return studentList.size();
    }

    @Override
    public Student getItem(int position) {
        return studentList.get(position);
    }

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

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder holder;
        View view = convertView;
        if (view == null) {
            view = inflater.inflate(android.R.layout.simple_list_item_1, parent, false);
            holder = new ViewHolder();
            holder.tvName = (TextView) view.findViewById(android.R.id.text1);
            view.setTag(holder);
        } else {
            holder = (ViewHolder) view.getTag();
        }
        Student student = getItem(position);
        holder.tvName.setText(student.naem);
        return view;
    }
}

 

Activity中調用

 

setListAdapter(new SimpleAdapter(getContext(),datas));

 

這段代碼應該不陌生,當繼承BaseAdapter,必須重寫getCount、getItem、getItemId、getView這4個方法,然後寫一個構造方法傳入實體對象。可以說你再寫一個BaseAdapter也是同樣的寫法,仔細的你應該會發現,只有構造方法實體對象與getView中方法體業務邏輯有差異嘛,那麼是不是可以把實體對象改寫成泛型,getView方法體業務邏輯抽象出來,在外實現呢?那是必須可以的,接下來我們一步一步改善這要命的重復代碼。

3、實體對象改成泛型

新建一個adapter命名為CygAdapter,有變化的代碼加了注釋。
public class CygAdapter extends BaseAdapter {//變化
    public static class ViewHolder {
        public TextView tvName;
    }

    private List studentList;//變化
    private LayoutInflater inflater;

    public CygAdapter(Context context, List students) {
        inflater = LayoutInflater.from(context);
        this.studentList = students;
    }

    @Override
    public int getCount() {
        return studentList.size();
    }

    @Override
    public T getItem(int position) {//變化
        return studentList.get(position);
    }

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

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder holder;
        View view = convertView;
        if (view == null) {
            view = inflater.inflate(android.R.layout.simple_list_item_1, parent, false);
            holder = new ViewHolder();
            holder.tvName = (TextView) view.findViewById(android.R.id.text1);
            view.setTag(holder);
        } else {
            holder = (ViewHolder) view.getTag();
        }
        T student = getItem(position);//變化
        holder.tvName.setText(student.naem);//這裡出問題咯,這個T泛型類根本就沒有name變量嘛,怎麼辦?
        return view;
    }
}
上面代碼已成功把實體對象改成了泛型,但是getView中的業務邏輯這時出現了上面代碼中的問題,無法取到name變量,難道給Student實體類加注解?對於一個Student類注解是行得通滴,加一個@name注解嘛,但是又來一個學校School類、班級Class類那豈不是加N個不同命名的注解?這樣一來加注解方案肯定就走不下去了。那麼我們可以嘗試把getView的業務邏輯抽象出來在外部實現,那現在就需要一個抽象方法了。

4、抽象getView的業務邏輯

我們再CygAdapter改造一下,新建一個抽象方法onBindData(ViewHolder viewHolder, final T item, final int position);

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder holder;
        View view = convertView;
        if (view == null) {
            view = inflater.inflate(android.R.layout.simple_list_item_1, parent, false);
            holder = new ViewHolder();
            holder.tvName = (TextView) view.findViewById(android.R.id.text1);
            view.setTag(holder);
        } else {
            holder = (ViewHolder) view.getTag();
        }
        T student = getItem(position);
        //holder.tvName.setText(student.naem);//這裡出問題咯,怎麼辦?
        onBindData(holder,student,position);//把上面holder.tvName.setText(student.naem)抽象到外部實現
        return view;
    }
    //新建抽象方法
    public abstract void onBindData(ViewHolder viewHolder, final T item, final int position);
再來看在Activity中用法
        setListAdapter(new com.mylhyl.cygadapter.sample.blog.CygAdapter(getContext(),datas) {
            @Override
            public void onBindData(ViewHolder viewHolder, Student item, int position) {
                viewHolder.tvName.setText(item.naem);
            }
        });
\ 換一個School對象試試

        ArrayList datas = new ArrayList();
        datas.add(new School("珠海北師大"));
        datas.add(new School("長沙湖南大學"));
        setListAdapter(new com.mylhyl.cygadapter.sample.blog.CygAdapter(getContext(), datas) {
            @Override
            public void onBindData(ViewHolder viewHolder, School item, int position) {
                viewHolder.tvName.setText(item.address);
            }
        });
\ 是不是爽YY了,不管換什麼實體對象照樣搞定你。Student與School二個實體類都分別只有一個屬性name、address,那麼現在需求上變化,在Student類中新增一個age年齡屬性,顯示在ListView上。這樣一來我們的layout就得新加一個TextView來用顯示age,我們就不新建了利用系統自帶的android.R.layout.simple_list_item_2,於是就可以開干了,重新構造List數據集啥的

 

        ArrayList datas = new ArrayList();
        datas.add(new Student(21,"張三"));
        datas.add(new Student(22,"李四"));
        setListAdapter(new com.mylhyl.cygadapter.sample.blog.CygAdapter(getContext(), datas) {
            @Override
            public void onBindData(ViewHolder viewHolder, Student item, int position) {
                viewHolder.tvName.setText(item.naem);
                viewHolder.tvAge.setText(String.valueOf(item.age));//尼瑪沒有ViewHolder中沒有tvAge呀
            }
        });
上面代碼ViewHolder中沒有tvAge呀,新建一個不就得了,這樣肯定是不行的,與剛才說的加注解方案一樣的道理,ViewHolder中的變量只會越來越多,怎麼辦呢?竟然問題出現在ViewHolder就得從他來下手,我們看getView中的那段ViewHolder相關的代碼

 

沒有加age之前

        ViewHolder holder;
        View view = convertView;
        if (view == null) {
            view = inflater.inflate(android.R.layout.simple_list_item_1, parent, false);//一個TextView
            holder = new ViewHolder();
            holder.tvName = (TextView) view.findViewById(android.R.id.text1);//名字
            view.setTag(holder);
        } else {
            holder = (ViewHolder) view.getTag();
        }
加age之後
        ViewHolder holder;
        View view = convertView;
        if (view == null) {
            view = inflater.inflate(android.R.layout.simple_list_item_2, parent, false);//二個TextView
            holder = new ViewHolder();
            holder.tvName = (TextView) view.findViewById(android.R.id.text1);//名字
            holder.tvAge = (TextView) view.findViewById(android.R.id.text2);//年齡
            view.setTag(holder);
        } else {
            holder = (ViewHolder) view.getTag();
        }
上面代碼是不是看出點什麼呢?是不是除了inflate與findViewById部分不一樣,那是不是有點思路了?新建一個類抽出相同部分,寫成一個通用的ViewHolder,開干吧!

5、通用ViewHolder

這個是參考了鴻洋的 新建一個不可繼承的CygViewHolder,首先看getView中代碼,這個CygViewHolder類肯定不是每都new,只有當convertView為null才new的,所以先搞個靜態方法用於取得CygViewHolder,為了不被外部直接new CygViewHolder,我們得把構造方法變為私用。來啊來呀,看改造後的代碼啊! CygViewHolder
public final class CygViewHolder {
    private Context mContext;
    private View mItemView;

    public static CygViewHolder get(Context context, int resource, View convertView, ViewGroup parent) {
        if (convertView == null) {
            View view = LayoutInflater.from(context).inflate(resource, parent, false);
            return new CygViewHolder(context, view);
        }
        return (CygViewHolder) convertView.getTag();
    }

    private CygViewHolder(Context context, View itemView) {
        mContext = context;
        mItemView = itemView;
        mItemView.setTag(this);
    }

    public View getItemView() {
        return mItemView;
    }
}
CygAdapter
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        //沒有了一堆重復代碼
        CygViewHolder viewHolder = CygViewHolder.get(context, resource, convertView, parent);
        T student = getItem(position);
        onBindData(viewHolder, student, position);
        return viewHolder.getItemView();
    }

    //ViewHolder改為CygViewHolder
    public abstract void onBindData(CygViewHolder viewHolder, final T item, final int position);
ListView綁定CygAdapter的時候出現問題了,無法查找控件
        ArrayList datas = new ArrayList();
        datas.add(new Student(21,"張三"));
        datas.add(new Student(22,"李四"));
        setListAdapter(new com.mylhyl.cygadapter.sample.blog.CygAdapter(getContext(), datas) {
            @Override
            public void onBindData(CygViewHolder viewHolder, Student item, int position) {
                //查找TextView,還是沒有tvName\tvAge?又得如何處理,想個辦法
                viewHolder.tvName = (TextView) view.findViewById(android.R.id.text1);
                viewHolder.tvAge = (TextView) view.findViewById(android.R.id.text2);
                //為TextView設置數據
                viewHolder.tvName.setText(item.naem);
                viewHolder.tvAge.setText(String.valueOf(item.age));
            }
        });
上面代碼onBindData綁定數據代碼段的問題如何處理呢?思路是這樣,既然CygViewHolder中已經有了每個Item的View,那麼我們可以在CygViewHolder寫個findViewById方法查找控件,並緩存在SparseArray中,如果有直接從緩存中返回,否則通過itemView.findViewById查找,緩存再返回。
public final class CygViewHolder {
    private Context mContext;
    private SparseArray mViews;//變化
    private View mItemView;

    public static CygViewHolder get(Context context, int resource, View convertView,
                                    ViewGroup parent) {
        if (convertView == null) {
            View view = LayoutInflater.from(context).inflate(resource, parent, false);
            return new CygViewHolder(context, view);
        }
        return (CygViewHolder) convertView.getTag();
    }

    private CygViewHolder(Context context, View itemView) {
        mContext = context;
        mViews = new SparseArray<>();//變化
        mItemView = itemView;
        mItemView.setTag(this);
    }


    public  T findViewById(int id) {//新增方法
        View view = mViews.get(id);
        if (view == null) {
            view = mItemView.findViewById(id);
            mViews.put(id, view);
        }
        return (T) view;
    }
}

使用

        ArrayList datas = new ArrayList();
        datas.add(new Student(21, "張三"));
        datas.add(new Student(22, "李四"));
        setListAdapter(new com.mylhyl.cygadapter.sample.blog.CygAdapter
                (getContext(), android.R.layout.simple_list_item_2, datas) {
            @Override
            public void onBindData(CygViewHolder viewHolder, Student item, int position) {
                TextView tvName = viewHolder.findViewById(android.R.id.text1);
                TextView tvAge = viewHolder.findViewById(android.R.id.text2);
                tvName.setText(item.naem);
                tvAge.setText(String.valueOf(item.age));
            }
        });

\   現在是不是簡單多了,adapter通用,只需使用時在onBindData中查到控件、給控件綁定數據,就這麼簡單。 內容比較多寫的啰嗦了點,但出發點是好的,請耐心閱讀,勿噴哦!!! 本庫地址:https://github.com/mylhyl/Android-CygAdapter  
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved