Android之自定義實現BaseAdapter(通用適配器一)

Android之自定義實現BaseAdapter(通用適配器)

通過前面的優化佈局之後,我們接着來講如何打造一個通用的適配器,那麼通用適配器能幹嗎呢?很簡單,減少我們對代碼的書寫,下面開始上代碼了。

MyAdapter.java

public class MyAdapter extends BaseAdapter {
    private List<Student> data;
    public MyAdapter(List<Student> data) {
        this.data = data;
    }
    @Override
    public int getCount() {
        return data == null ? 0 : data.size();
    }
    @Override
    public Object getItem(int position) {
        return data.get(position);
    }
    @Override
    public long getItemId(int position) {
        return position;
    }
    /**
     *
     * @param position
     * @param convertView
     * @param parent
     * @return
     */
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
      ViewHolder holder = null;
        if(convertView == null){
            //解析佈局
            convertView = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item,null);
            //創建ViewHolder持有類
            holder = new ViewHolder();
            //將每個控件的對象保存到持有類中
            holder.tvName = (TextView)convertView.findViewById(R.id.mTv1);
            holder.tvSex = (TextView)convertView.findViewById(R.id.mTv2);
            //將每個convertView對象中設置這個持有類對象
            convertView.setTag(holder);
        }
        //每次需要使用的時候都會拿到這個持有類
        holder = (ViewHolder)convertView.getTag();
        //然後可以直接使用這個類中的控件,對控件進行操作,而不用重複去findViewById了
        holder.tvName.setText(data.get(position).getName());
        holder.tvSex.setText(data.get(position).getSex());
        return convertView;
    }

    /**
     * 通過這個類來保存當前所有的控件id
     */
    static class ViewHolder{
        TextView tvName;
        TextView tvSex;
    }
}

在上面的代碼中,我們先看看有哪些代碼的格式或形式都是重複在使用的,首先不難看出,public int getCount() 、public long getItemId(int position) 、public long getItemId(int position)這三個方法是不是每次都要實現呢,由此我們可以先將這些代碼提取出來,放到MyBaseAdapter中,由於我們每次的重要部分是實現getView方法,所以這個方法我們不需要在這裏面寫,直接將MyBaseAdapter設置爲抽象類就可以了,然需要實現getView的類來繼承他即可,因此MyAdapter可以繼承MyBaseAdapter然後實現getView方法即可

MyBaseAdapter.java

public abstract class MyBaseAdapter extends BaseAdapter {
    protected List<Student> data;
    public MyBaseAdapter(List<Student> data){
        this.data = data;
    }
    @Override
    public int getCount() {
        return data == null ? 0 : data.size();
    }

    @Override
    public Object getItem(int position) {
        return data.get(position);
    }

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

MyAdapter.java

public class MyAdapter extends MyBaseAdapter {
    public MyAdapter(List<Student> data) {
        super(data);
        this.data = data;
    }
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
      ViewHolder holder = null;
        if(convertView == null){
            convertView = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item,null);
            holder = new ViewHolder();
            holder.tvName = (TextView)convertView.findViewById(R.id.mTv1);
            holder.tvSex = (TextView)convertView.findViewById(R.id.mTv2);
            convertView.setTag(holder);
        }
        holder = (ViewHolder)convertView.getTag();
        holder.tvName.setText(data.get(position).getName());
        holder.tvSex.setText(data.get(position).getSex());
        return convertView;
    }
    static class ViewHolder{
        TextView tvName;
        TextView tvSex;
    }
}

這樣,每次自定義只需要繼承MyBaseAdapter就可以了,不過還是那句話,沒有最優,只有更優,所以我們還要接着優化,接着封裝,那麼我們接着從上面的getView方法中看,還有哪些代碼是我們經常重複使用到的代碼呢?其實你會發現每次我們都需要操作相同的這段代碼:

 ViewHolder holder = null;
        if(convertView == null){
            convertView = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item,null);
            holder = new ViewHolder();
            holder.tvName = (TextView)convertView.findViewById(R.id.mTv1);
            holder.tvSex = (TextView)convertView.findViewById(R.id.mTv2);
            convertView.setTag(holder);
        }
        holder = (ViewHolder)convertView.getTag();
        holder.tvName.setText(data.get(position).getName());
        holder.tvSex.setText(data.get(position).getSex());
        return convertView;
    }

我們可以先再次理解下這段代碼,首先我們每次都要創建一個holder持有者對象,設置到對應的converView的setTag中,然後,每次要拿到hoder對象,對hoder對象中的控件進行操作,對於上面的代碼我們可以直接將他簡化爲以下幾個步奏

1.ViewHolder holder = 拿到holder //每次拿到對應的holder對象即可
2.TextView tv = holder.getView() //拿到每個控件對應的id
3. tv.setText() //對控件進行操作
4. return view //返回view即可

下面開始寫一個通用的ViewHolder通用類,來優化我們的實現,代碼如下:

ViewHolder.java

public class ViewHolder {
    //被點擊的當前位置
    private int position;

    //用一個map集合來保存每個控件的id,這個SparseArray是android提供的一個比map使用效率更高的一個
    //集合,但是侷限是,key只能是int類型,所以當鍵值對涉及到key是int類型時,可以優先考慮使用這個集合
    private SparseArray<View> array;

    //複用的佈局
    private View convertView;

    //上下文
    private Context context;

    //解析的佈局資源id
    private int layout;
    public ViewHolder(){

    }

    //帶三個構造的方法,這裏將構造方法私有,防止外界去創建,通過自身的靜態方法去創建對象即可
    private ViewHolder(ViewGroup parent,int position,int layout){
        this.position = position;
        this.context = parent.getContext();
        //每次創建對象,就將佈局解析出來
        convertView = LayoutInflater.from(parent.getContext()).inflate(layout,null);
        //然後將對象保存到convertView對應的setTag中,方便每次該獲取
        convertView.setTag(this);
        array = new SparseArray<>();
    }

    //通過這個方法,可以創建ViewHolder對象
    public static ViewHolder getHolder(View convertView, ViewGroup parent, int position,int layout){
        //每次判斷converView是否爲空,如果爲空就直接返回這個創建的對象
        if(convertView == null){
            return new ViewHolder(parent,position,layout);
        }else{
            //不爲空的情況,就跟我們上面的代碼一樣,每次通過複用的控件拿到對應的ViewHolder對象
            ViewHolder holder = (ViewHolder)convertView.getTag();
            //這裏一定要更新下下標的位置,雖然對象相同,但是我們每次都要更新現有的位置,
            holder.position = position;
            //返回已經創建好的holder對象
            return holder;
        }
    }

    /**
    * 這個方法是通過控件id拿到對應的控件
    */
    public <T extends View> T getView(int viewId){
        //每次通過viewId鍵去拿到到對應的控件
        View view =  array.get(viewId);
        //如果爲空,表示該集合中還沒有存入該控件
        if(view == null){
            //先要去通過converView拿到控件id
            view = convertView.findViewById(viewId);
            //保存到集合中,以便下次直接獲取
            array.put(viewId,view);
        }
        //返回View的子類控件,採用泛型的方便是不需要強制轉換了
        return (T)view;
    }
    //得到converView佈局
    public View getConvertView(){
        return convertView;
    }

通過上面的代碼我們就已經封裝好了一個通用的ViewHolder類了,下面我們的MyAdapter.java則可以更加簡單的只寫一下代碼了:

public class MyAdapter extends MyBaseAdapter {
    public MyAdapter(List<Student> data) {
        super(data);
    }
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder holder = ViewHolder.getHolder(convertView,parent,position, R.layout.list_item);
        TextView tvName = holder.getView(R.id.mTv1);
        TextView tvSex = holder.getView(R.id.mTv2);
        tvName.setText(data.get(position).getName());
        tvSex.setText(data.get(position).getSex());
        return holder.getConvertView();
    }
}

好了,上面的代碼是不更簡單了呢,其實我們這裏只是封裝了ViewHolder類哦,還有更通用的等着我們去封裝呢,下次我們需要封裝的就是如何把getView中的代碼再一次進行封裝,已達到更優。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章