BaseAdapter使用之逗比式、普通式和文藝式

關(guān)鍵字:BaseAdapter ViewHolder 逗比式 普通式 文藝式

前言:這篇文章也前幾篇一樣,也是我在 慕課網(wǎng) 上看 Android部分 的視頻記錄的學(xué)習(xí)筆記。這篇文章記錄的是 eclipse_xu 老師的視頻教程---Android必學(xué)-BaseAdapter的使用與優(yōu)化。

先附上徐大神** 慕課網(wǎng) 簡書 **的主頁:

一. 什么是數(shù)據(jù)適配器--BaseAdapter

數(shù)據(jù)適配器的作用

上圖表示了數(shù)據(jù)源Data Source、數(shù)據(jù)適配器Adapter以及顯示控件ListView之間的關(guān)系。

我們知道數(shù)據(jù)源中數(shù)據(jù)的來源可以有很多方式,而顯示控件顯示的方式也是不盡相同的,數(shù)據(jù)適配器正好建立了一個它們之間的一個關(guān)系,將數(shù)據(jù)源中的數(shù)據(jù)轉(zhuǎn)換為顯示控件所能顯示的數(shù)據(jù)格式。從而將數(shù)據(jù)的來源和數(shù)據(jù)的顯示進(jìn)行了解耦,這就是Android中普遍使用的適配器模式。

二. ListView的顯示和緩存機制

ListView的顯示和緩存機制

當(dāng)我們向上滑動ListView時,item1被滑出屏幕,item1成為了無用的控件,ListView會自動回收它,以便在新的item8進(jìn)來時,使用它來代替重新實例化一個新的item,這個被緩存的item會被保存在getView(int position, View convertView, ViewGroup parent)方法中的第二個參數(shù)convertView中。

總結(jié)為一句話就是,需要才顯示,顯示完就被回收到緩存

三. 數(shù)據(jù)適配器--BaseAdapter

要使用BaseAdapter,做法是自定義類來繼承BaseAdapter

BaseAdapter的基本結(jié)構(gòu):

  • public int getCount():適配器中數(shù)據(jù)集的數(shù)據(jù)個數(shù)
  • public Object getItem(int position):獲取數(shù)據(jù)集中與所給索引對應(yīng)的數(shù)據(jù)項
  • public long getItemId(int position):獲取指定行對應(yīng)的ID
  • public View getView(int position, View convertView, ViewGroup parent):獲取每一個Item顯示內(nèi)容

接下來開始我們的示例代碼:

1. 創(chuàng)建布局文件

主布局 activity_main.xml

<?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">

    <ListView
        android:id="@+id/lv_main"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</LinearLayout>

列表項布局 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">

    <ImageView
        android:id="@+id/iv_image"
        android:layout_width="60dp"
        android:layout_height="60dp"
        android:src="@mipmap/ic_launcher" />

    <TextView
        android:id="@+id/tv_title"
        android:layout_width="match_parent"
        android:layout_height="30dp"
        android:layout_toRightOf="@+id/iv_image"
        android:text="title"
        android:textSize="24sp" />

    <TextView
        android:id="@+id/tv_content"
        android:layout_width="match_parent"
        android:layout_height="30dp"
        android:layout_below="@+id/tv_title"
        android:layout_toRightOf="@+id/iv_image"
        android:text="content"
        android:gravity="bottom"
        android:textSize="20sp" />
</RelativeLayout>

2. 創(chuàng)建數(shù)據(jù)源

1)創(chuàng)建一個Bean對象,用于封裝item中顯示的內(nèi)容
ItemBean.java

public class ItemBean {

    public int imageID;
    public String title;
    public String content;

    public ItemBean(int imageID, String title, String content) {
        this.imageID = imageID;
        this.title = title;
        this.content = content;
    }
}

2)在MainActivity.java中模擬數(shù)據(jù)源:

public class MainActivity extends Activity {
    private ListView lv_main;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        lv_main = (ListView) findViewById(R.id.lv_main);

        List<ItemBean> itemBeanList = new ArrayList<>();
        //模擬數(shù)據(jù)源
        for (int i = 0; i < 20; i++) {
            itemBeanList.add(new ItemBean(
                    R.mipmap.ic_launcher,
                    "title" + i,
                    "content" + i));
        }
        //給ListView設(shè)置適配器
        lv_main.setAdapter(new MyAdapter(this, itemBeanList));

    }
}

3. BaseAdapter實現(xiàn)的三重境界

MyAdapter.java:除了getView方法另行介紹外,其他都一樣。

public class MyAdapter extends BaseAdapter {
    public List<ItemBean> itemBeanList;
    //用于把布局文件轉(zhuǎn)化為View對象
    private LayoutInflater layoutInflater;
    public MyAdapter(Context context, List<ItemBean> itemBeanList) {
        this.itemBeanList = itemBeanList;
        layoutInflater = LayoutInflater.from(context);
    }
    @Override
    public int getCount() {
        return itemBeanList.size();
    }
    @Override
    public Object getItem(int position) {
        return itemBeanList.get(position);
    }
    @Override
    public long getItemId(int position) {
        return position;
    }
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        return null;
    }
}

1)逗比式(不推薦
public View getView(int position, View convertView, ViewGroup parent) {
        //逗比式
        View view = layoutInflater.inflate(R.layout.item, null);
        ImageView iv_image = (ImageView) view.findViewById(R.id.iv_image);
        TextView tv_title = (TextView) view.findViewById(R.id.tv_title);
        TextView tv_content = (TextView) view.findViewById(R.id.tv_content);
        
        //把所有的Bean對象中的數(shù)據(jù)設(shè)置給對應(yīng)的控件
        ItemBean bean = itemBeanList.get(position);

        iv_image.setImageResource(bean.imageID);
        tv_title.setText(bean.title);
        tv_content.setText(bean.content);
        return view;
    }

說明:逗比式?jīng)]有使用到ListView的緩存機制,每需要顯示一個item布局,就會創(chuàng)建一個新的View對象,本例中會創(chuàng)建20個View對象,顯然很浪費資源。
我們不應(yīng)該使用此方式

2)普通式(謹(jǐn)慎使用
public View getView(int position, View convertView, ViewGroup parent) {

        //普通式
        if(convertView == null){//判斷緩存的View是否為空
            convertView = layoutInflater.inflate(R.layout.item, null);
        }

        ImageView iv_image = (ImageView) convertView.findViewById(R.id.iv_image);
        TextView tv_title = (TextView) convertView.findViewById(R.id.tv_title);
        TextView tv_content = (TextView) convertView.findViewById(R.id.tv_content);

        //把所有的Bean對象中的數(shù)據(jù)設(shè)置給對應(yīng)的控件
        ItemBean bean = itemBeanList.get(position);

        iv_image.setImageResource(bean.imageID);
        tv_title.setText(bean.title);
        tv_content.setText(bean.content);

        return convertView;
    }

說明:充分利用了ListView的緩存特性,如果沒有緩存(convertView)才創(chuàng)建新的View。但是多次的使用findViewById方法還是會浪費大量的時間,因為每次在調(diào)用getView方法時,都會執(zhí)行findViewById方法,就會遍歷整個視圖樹,如果視圖樹很復(fù)雜,就會花費較長的時間。

3)文藝式(力推
public View getView(int position, View convertView, ViewGroup parent) {
        //文藝式
        ViewHolder viewHolder;
        //判斷converView是否為空
        if (convertView == null) {
            viewHolder = new ViewHolder();
            convertView = layoutInflater.inflate(R.layout.item, null);
            //將所用的控件保存到viewHolder中
            viewHolder.iv_image = (ImageView) convertView.findViewById(R.id.iv_image);
            viewHolder.tv_title = (TextView) convertView.findViewById(R.id.tv_title);
            viewHolder.tv_content = (TextView) convertView.findViewById(R.id.tv_content);
            //將viewHolder和convertView綁定
            convertView.setTag(viewHolder);
        } else {
            //如果存在convertView,取出在convertView中保存的viewHolder并賦值給viewHolder
            viewHolder = (ViewHolder) convertView.getTag();
        }
        ItemBean bean = itemBeanList.get(position);
        //通過viewHolder找到對應(yīng)控件,避免每次通過`findViewById`找控件
        viewHolder.iv_image.setImageResource(bean.imageID);
        viewHolder.tv_title.setText(bean.title);
        viewHolder.tv_content.setText(bean.content);
        return convertView;
    }
     //自定義ViewHolder類,屬性包含所用到的控件
    class ViewHolder {
        public ImageView iv_image;
        public TextView tv_title;
        public TextView tv_content;
    }

說明:viewHolder類的使用,是Google在2013年io大會上推薦使用的方式。文藝式不僅利用了ListView的緩存機制,更通過ViewHolder類來實現(xiàn)顯示數(shù)據(jù)和視圖的緩存,避免了每次通過findViewById尋找控件。

作為一名由情懷的程序員,這才是最文藝的寫法。---by eclipse_xu

為什么說文藝式要比前面兩種方式好的?
我們通過程序執(zhí)行的時間來做一下測試。



public class MyAdapter extends BaseAdapter {

    private long sumTime ;

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {

        long start = System.nanoTime();   

        long end = System.nanoTime();

        sumTime+=(end - start);

        return convertView;
    }

}

我們在MyAdapter 類中增加一個成員變量sumTime用于記錄顯示完所有item所用的總時間。在getView方法開始記錄下時間、結(jié)束再記錄下時間,所得差值就是一次getView所使用的時間,把它累加到sumTime中,得到總時間。

可以得到三種方式的總時間:

| 逗比式 | 普通式 | 文藝式
| :------ :|:-------:| :-------:
|22339710 ns| 20170441 ns| 17771535 ns

可以看出來,在時間上還是有很大差距的。

總結(jié)

在使用ViewHolder優(yōu)化ListView的步驟:

  1. 創(chuàng)建Bean對象,用于封裝要顯示的數(shù)據(jù)
  2. 在自定義的BaseAdapter構(gòu)造函數(shù)中,初始化用于映射的數(shù)據(jù)List
  3. 創(chuàng)建ViewHolder類,創(chuàng)建數(shù)據(jù)映射關(guān)系
  4. 判斷convertView是否為空,為空就創(chuàng)建,并設(shè)置Tag,否則通過Tag取出ViewHolder
  5. 給ViewHolder中的控件設(shè)置數(shù)據(jù)

END

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容