關(guān)鍵字:BaseAdapter
ViewHolder
逗比式
普通式
文藝式
前言:這篇文章也前幾篇一樣,也是我在 慕課網(wǎng) 上看 Android部分 的視頻記錄的學(xué)習(xí)筆記。這篇文章記錄的是 eclipse_xu 老師的視頻教程---Android必學(xué)-BaseAdapter的使用與優(yōu)化。
先附上徐大神** 慕課網(wǎng) 和 簡書 **的主頁:
- 慕課網(wǎng):http://www.imooc.com/space/teacher/id/347333,
這里有很多android教程,勤奮的徐大神。 - 簡書:http://www.lxweimin.com/users/dfc0ed52c22b/latest_articles
果斷關(guān)注
一. 什么是數(shù)據(jù)適配器--BaseAdapter
上圖表示了數(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的顯示和緩存機制
當(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的步驟:
- 創(chuàng)建Bean對象,用于封裝要顯示的數(shù)據(jù)
- 在自定義的BaseAdapter構(gòu)造函數(shù)中,初始化用于映射的數(shù)據(jù)List
- 創(chuàng)建ViewHolder類,創(chuàng)建數(shù)據(jù)映射關(guān)系
- 判斷convertView是否為空,為空就創(chuàng)建,并設(shè)置Tag,否則通過Tag取出ViewHolder
- 給ViewHolder中的控件設(shè)置數(shù)據(jù)
END