Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android ListView封裝(代碼優化):抽取方法共性,封裝 BaseAdapter 和 ViewHolder

Android ListView封裝(代碼優化):抽取方法共性,封裝 BaseAdapter 和 ViewHolder

編輯:關於Android編程

這裡寫圖片描述


大多App都會使用到的基本控件 ——- Listiew,特別像新聞浏覽類的比如說“今日關注”,或者“應用寶”這種匯集手機軟件集合的。而且大家都知道 需要給每個單獨的 ListView 搭配相應的適配器 Adapter 。如果你的項目中使用ListView 的頻率很少甚至沒有,那我不建議你對 ListView 進行抽取封裝,但是!如果它的使用滲透到App中大多頁面時,你必須考慮 對Adapter的公共方法進行抽取封裝到單獨的類中,來避免整個項目代碼的冗雜,使代碼結構規范化


一. 未封裝版 ListView

HomeFragment.java

@Override
public View onCreateSuccessView() {
    ListView view = new ListView(UIUtils.getContext());
    initData();
    view.setAdapter(new HomeAdapter());
    return view;
}

private void initData() {
    for (int i = 0; i < 50; i++) {
        mList.add("測試數據" + i);
    }
}

class HomeAdapter extends BaseAdapter {

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

    @Override
    public String getItem(int position) {
        return mList.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) {
            convertView = View.inflate(UIUtils.getContext(),
                    R.layout.list_item_home, null);
            holder = new ViewHolder();
            holder.tvContent = (TextView) convertView
                    .findViewById(R.id.tv_content);
            convertView.setTag(holder);
        } else {
            holder = (ViewHolder) convertView.getTag();
        }

        holder.tvContent.setText(getItem(position));

        return convertView;
    }
}

static class ViewHolder {
    public TextView tvContent;
}

這裡我們主要 把重點放在 AdapterViewHolder 的抽取封裝,首先簡單分析其邏輯組成

1.首先看到我自定義的 HomeAdapter 繼承的是 BaseAdapter 。繼承的四個方法中,前三個:getCountgetItemgetItemId 看的出來方法及其簡單,只涉及到一個不確定量——顯示的數據類,易封裝,使用泛型定義數據類型即可。

但是getView方法中步驟略復雜,首先梳理清楚方法裡的邏輯,才好進一步的封裝:
(1)加載布局文件,布局轉換(xml —> view)
(2)初始化控件(finViewById)
(3)給ViewHolder設置標記(setTag),便於下次復用
(4)給控件設置數據


static class ViewHolder {
    public TextView tvContent;
}

2. ViewHolder 類中定義的是一個單獨條目中的所有控件聲明,也許你會發現 不同的ListView中適配器 Adapter中有共性的方法,但是 不同頁面中展示條目不盡相同,根本無法提取。事實的確是這樣,但是Adapter中肯定會用到 ViewHolder類 ,而且getView方法中的部分邏輯還要依靠這個類去完成,所以我們還是 要繼續提取一個基類 BaseHolder,至於取決於不同頁面邏輯也不同的地方,封裝一個抽象方法丟給子類去實現即可!






二. BaseAdapter封裝

1.抽取BaseAdapter的共性方法,封裝到MyBaseAdapter

/**
 * 數據適配器的基類
 * 展示的數據類型不確定,故使用泛型!
 */
public class MyBaseAdapter extends BaseAdapter {

    private ArrayList list;

    public MyBaseAdapter(ArrayList list) {
        this.list = list;
    }

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

    @Override
    public T getItem(int position) {
        return list.get(position);
    }

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

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        return null;
    }
}

首先我們對 繼承Basedapter的三個方法:getCountgetItemgetItemId 進行封裝,由於方法中涉及到 展示的條目數據,這裡要增加一個 構造方法 —— 用來接收 數據集合。至於展示的數據集合類型無法確定,所以才用泛型來定義數據集。定義的類也要加上泛型。MyBaseAdapter


2. getView 封裝

這裡只剩下getView方法還未封裝,我們再次復習其中邏輯:
(1)加載布局文件,布局轉換(xml —> view)
(2)初始化控件(finViewById)
(3)給ViewHolder設置標記(setTag),便於下次復用
(4)給控件設置數據

MyBaseAdapter.java

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    BaseHolder holder = null;
    if (convertView == null) {
        // 在初始化holder的同時,已經對布局進行了加載,也給view設置了tag
        holder = getHolder(position);
    } else {
        holder = (BaseHolder) convertView.getTag();
    }
    // 刷新界面,更新數據
    holder.setData(getItem(position));

    return holder.getRootView();
}

// 返回BaseHolder的子類,必須實現
public abstract BaseHolder getHolder(int position);

首先初始化我們從ViewHolder中提取的基類泛型 BaseHolder
(1)前3點邏輯 主要依賴於具體的頁面,不同的頁面應當對應不同的ViewHolder,所以在這個封裝一個抽象方法getHolder ,交由 子類繼承 MyBaseAdapter去實現!
(2)第四點邏輯 ,更新數據,直接調用 BaseHolder中的方法。





三. BaseHolder封裝

1.抽取ViewHolder的功能,封裝到BaseHolder

由以上可知,BaseHolder類中應做的事
1. 初始化item布局,2. findViewById方法:一個抽象方法,由子類在初始化布局時實現!
3. 給view設置tag:BaseHolder的構造方法中實現!
4. 刷新界面:抽象方法,由子類去實現!

所以,BaseHolder相當於是對getView方法的封裝

public abstract class BaseHolder {

    private View mRootView;// item的布局對象
    private T data;// item對應的數據

    public BaseHolder() {
        mRootView = initView();// 初始化布局
        mRootView.setTag(this);// 給view設置tag
    }

    // 初始化布局的方法必須由子類實現
    public abstract View initView();

    // 返回布局對象
    public View getRootView() {
        return mRootView;
    };

    // 設置數據
    public void setData(T data) {
        this.data = data;
        refreshView(data);
    }

    // 獲取數據
    public T getData() {
        return data;
    }

    // 刷新界面,更新數據,子類必須實現
    public abstract void refreshView(T data);
}



——–以上已經封裝完畢!———–



四. BaseHolder耦合於getView

解析BaseHolder:
若按照以上解釋,那BaseHolder只需要兩個抽象方法和構造函數即可,可並非如此!既然BaseHolder是對getView方法的一個封裝,那它需要完成getView中的所有功能,可它跟getView方法是耦合的,所以需要相互配合,再查看封裝好的 getView方法:

MyBaseAdapter.java

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    BaseHolder holder = null;
    if (convertView == null) {
        // 在初始化holder的同時,已經對布局進行了加載,也給view設置了tag
        holder = getHolder(position);
    } else {
        holder = (BaseHolder) convertView.getTag();
    }
    // 刷新界面,更新數據
    holder.setData(getItem(position));

    return holder.getRootView();
}

// 返回BaseHolder的子類,必須實現
public abstract BaseHolder getHolder(int position);


1. 這裡的holder調用的是 由子類實現的抽象方法

// 在初始化holder的同時,已經對布局進行了加載,也給view設置了tag
        holder = getHolder(position);

具體就是new 一個 繼承BaseHolder 的子類,所以我們new完這個子類後,需要完成getView的1.2.3點邏輯。所以在 Baseholder中的構造方法中完成此3點邏輯(抽象方法)。


// 刷新界面,更新數據
    holder.setData(getItem(position));

2. 而這裡的設置數據的方法不適合直接設為抽象,因為涉及到數據的傳遞(數據類型不確定,依舊為泛型)。方法中第一步是接收數據,第二步才是刷新數據。我們的做法是把第二步的邏輯(刷新數據)單獨拿出來,封裝成一個抽象方法,留給子類BaseHolder去實現!


return holder.getRootView();

3.getView 的最後一步 —— 將當前item的View返回!可是我們具體View的初始化都交由子類實現了,BaseHodler 並無View。這時我們就要在BaseHodler 單獨定義一個方法getRootView()來返回當前item的布局。



以上,才是BaseAdapter 和ViewHolder的完美搭配,即MyBaseAdapter 和 BaseHodler 完美搭配





舉一個栗子:

MyBaseAdapter 的繼承:

HomeFragment.java
public class HomeFragment extends BaseFragment {
    private ArrayList data;

    @Override
    public View onCreateSuccessView() {
        initData();
        ListView view = new ListView(UIUtils.getContext());
        view.setAdapter(new HomeAdapter(data));
        return view;
    }

   private void initData(){
        for (int i = 0; i < 50; i++) {
            data.add("測試數據" + i);
      }

class HomeAdapter extends MyBaseAdapter {
        private ArrayList data;

        public HomeAdapter(ArrayList data) {
            super(data);
        }

        @Override
        public BaseHolder getHolder() {
            return new HomeHolder();
        }
}

BaseHodler 的繼承:

public class HomeHolder extends BaseHolder {

    private TextView tvContent;

    @Override
    public View initView() {
        // 1. 加載布局
        View view = UIUtils.inflate(R.layout.list_item_home);
        // 2. 初始化控件
        tvContent = (TextView) view.findViewById(R.id.tv_content);
        return view;
    }

    @Override
    public void refreshView(Stringdata) {
        tvContent.setText(data);
    }

}

這裡寫圖片描述




喔~~~~大家感覺到了嗎?!代碼量驟減啊!!!好處自是不必多說,用心體會~
希望對你有幫助 :)

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved