Android界面中有時候需要顯示稍微復雜的界面時,就需要我們自定義一個adapter,而此adapter就要繼承BaseAdapter,重新其中的方法.
Android中Adapter類其實就是把數據源綁定到指定的View上,然后再返回該View,而返回來的這個View就是ListView中的某一 行item。這里返回來的View正是由我們的Adapter中的getView方法返回的。這樣就會容易理解數據是怎樣一條一條顯示在ListView 中的。
在完成這篇文章中的例子之后,我思考了很長時間,關於重寫一個adapter,這其中真的有很多講究,遇到一處不懂的都會查閱很長時間,但也不能保證我已經把其中的重中之重已經找完了,只要你想延伸都可以發現其中的無限.....
問題1:為什么我們要重寫一個adapter?
問題2:android中這么多adapter,什么情況下該重寫哪一個adapter(ArrayAdapter/SimpleAdapter/SimpleCursorAdapter...)?
問題3:我們重寫的adapter為什么是一個內部類,是否建議把adapter做成一個內部類?
問題4:理解應用中的數據源一般都會用一個Map類型的List,有何意途?
問題5:通過adapter是怎樣做到把一條一條的item放到ListView中的?
問題6:理解重寫adapter時,重寫的幾個方法(getCount()/getItem()/getItemId()/getView())?
問題7:理解ListView使用adapter的機制
案例概覽步驟:
1)創建2個layout,一個是界面頂部的顯示,一個是ListView中的內容
1.1)ListView_header.xml(
注:自我感覺界面中的控件的位置擺放與layout_width/layout_height/orientation/gravity/layout_gravity的屬性設置關系非常大,一定要注意每個屬性的用法)
<?xml version="1.0" encoding="UTF-8"?>
<!-- 第一個LinearLayout,充當父容器,要包攬兩行,因此設置其 android:orientation為 縱向-->
<LinearLayout xmlns:android=" http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">
<!-- 第二個LinearLayout
android:gravity 表示該LinearLayout中的類容相對於該LinearLayout 水平居中
android:layout_gravity 表示的是該LinearLayout相對於它上面的父容器(這里是第一個LinearLayout)水平居中
-->
<LinearLayout android:id="@+id/toprow"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android: gravity="center_horizontal">
<TextView android:id="@+id/quna"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#ff9966ff"
android:textSize="25dp"
android:text="@string/places"/>
<!-- 用一個圖片按鈕 -->
<ImageButton android:id="@+id/goBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/quna"/>
</LinearLayout>
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/placesList"
android:textColor="#ff9966ff"
android:textSize="20dp"/>
<ListView android:id="@+id/list_places"
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
<!-- 第一個LinearLayout,充當父容器,要包攬兩行,因此設置其 android:orientation為 縱向-->
<LinearLayout xmlns:android=" http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">
<!-- 第二個LinearLayout
android:gravity 表示該LinearLayout中的類容相對於該LinearLayout 水平居中
android:layout_gravity 表示的是該LinearLayout相對於它上面的父容器(這里是第一個LinearLayout)水平居中
-->
<LinearLayout android:id="@+id/toprow"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android: gravity="center_horizontal">
<TextView android:id="@+id/quna"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#ff9966ff"
android:textSize="25dp"
android:text="@string/places"/>
<!-- 用一個圖片按鈕 -->
<ImageButton android:id="@+id/goBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/quna"/>
</LinearLayout>
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/placesList"
android:textColor="#ff9966ff"
android:textSize="20dp"/>
<ListView android:id="@+id/list_places"
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
1.2)listview_item.xml
<?xml version="1.0" encoding="UTF-8"?>
<LinearLayout xmlns:android=" http://schemas.android.com/apk/res/android"
android:orientation="horizontal" //此屬性的設置非常重要,決定里面的控件橫向擺放
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<!-- android:layout_margin設置圖片與旁邊文章的距離 -->
<ImageView android:id="@+id/image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="5dp"/>
<LinearLayout android:orientation="vertical" //也很重要決定里面的兩個textview豎着放
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<TextView android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#ff3399ff"
android:textSize="20dp"/>
<TextView android:id="@+id/info"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#ff3399ff"
android:textSize="13dp"/>
</LinearLayout>
<!-- 注意此處button的屬性android:layout_gravity的用法 ,這里設置了都沒效果
這里不是很理解為什么要在加一個LinearLayout,里面的button才可以居右??
而且LinearLayout的width必須為fill_parent才可以
-->
<LinearLayout android:layout_width="fill_parent"
android:layout_height="wrap_content"
>
<Button android:id="@+id/viewBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/btn_select"
android:layout_gravity="bottom|right"/>
</LinearLayout>
</LinearLayout>
<LinearLayout xmlns:android=" http://schemas.android.com/apk/res/android"
android:orientation="horizontal" //此屬性的設置非常重要,決定里面的控件橫向擺放
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<!-- android:layout_margin設置圖片與旁邊文章的距離 -->
<ImageView android:id="@+id/image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="5dp"/>
<LinearLayout android:orientation="vertical" //也很重要決定里面的兩個textview豎着放
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<TextView android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#ff3399ff"
android:textSize="20dp"/>
<TextView android:id="@+id/info"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#ff3399ff"
android:textSize="13dp"/>
</LinearLayout>
<!-- 注意此處button的屬性android:layout_gravity的用法 ,這里設置了都沒效果
這里不是很理解為什么要在加一個LinearLayout,里面的button才可以居右??
而且LinearLayout的width必須為fill_parent才可以
-->
<LinearLayout android:layout_width="fill_parent"
android:layout_height="wrap_content"
>
<Button android:id="@+id/viewBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/btn_select"
android:layout_gravity="bottom|right"/>
</LinearLayout>
</LinearLayout>
2)、寫java class,開發activity(建一個class繼承Activity,添加click事件的時候還要實現onClickListener接口)
2.1) 重寫onCreate方法----------------------------------------------->
private ListView listView;
private ImageButton goBtn;
private List<Map<String, Object>> testData;
private ImageButton goBtn;
private List<Map<String, Object>> testData;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout. listview_header); // 這里運行該項目的時候,讓其顯示listview_header.xml界面(layout),然后再將listview要顯示的item項加到里面
//獲得 listview_header.xml中的ListView控件
super.onCreate(savedInstanceState);
setContentView(R.layout. listview_header); // 這里運行該項目的時候,讓其顯示listview_header.xml界面(layout),然后再將listview要顯示的item項加到里面
//獲得 listview_header.xml中的ListView控件
listView = (ListView) findViewById(R.id.list_places);
//獲得imageButton給它添加click事件
goBtn = (ImageButton) findViewById(R.id.goBtn);
goBtn.setOnClickListener(new OnClickListener() {
//這里只是做出了簡單的彈出框
@Override
public void onClick(View v) {
new AlertDialog.Builder(BaseAdapterTest2.this).setTitle("想去的國家:")
//獲得imageButton給它添加click事件
goBtn = (ImageButton) findViewById(R.id.goBtn);
goBtn.setOnClickListener(new OnClickListener() {
//這里只是做出了簡單的彈出框
@Override
public void onClick(View v) {
new AlertDialog.Builder(BaseAdapterTest2.this).setTitle("想去的國家:")
.setMessage("i want to go France")
.setPositiveButton("確定", null).sho();
}
});
//數據源
testData = buildData();
MyAdapter adapter = new MyAdapter(this);
}
});
//數據源
testData = buildData();
MyAdapter adapter = new MyAdapter(this);
//通過setAdapter而把數據綁定到ListView中
listView.setAdapter(adapter);
}
listView.setAdapter(adapter);
}
2.2)創建數據,注意這里為什么要是Map類型的List集合
private List<Map<String, Object>> buildData(){
List<Map<String, Object>> data = new ArrayList<Map<String,Object>>();
Map<String, Object> map = new HashMap<String, Object>();
List<Map<String, Object>> data = new ArrayList<Map<String,Object>>();
Map<String, Object> map = new HashMap<String, Object>();
map.put("title", "中國");
map.put("info", "China");
map.put("image", R.drawable.p6);
data.add(map);
map = new HashMap<String, Object>();
map.put("title", "英國");
map.put("info", "England");
map.put("image", R.drawable.p7);
data.add(map);
map = new HashMap<String, Object>();
map.put("title", "美國");
map.put("info", "America");
map.put("image", R.drawable.p9);
map = new HashMap<String, Object>();
map.put("title", "荷蘭");
map.put("info", "Dutch");
map.put("image", R.drawable.p9);
data.add(map);
map = new HashMap<String, Object>();
map.put("title", "新西蘭");
map.put("info", "New Zealand");
map.put("image", R.drawable.p7);
data.add(map);
map.put("info", "China");
map.put("image", R.drawable.p6);
data.add(map);
map = new HashMap<String, Object>();
map.put("title", "英國");
map.put("info", "England");
map.put("image", R.drawable.p7);
data.add(map);
map = new HashMap<String, Object>();
map.put("title", "美國");
map.put("info", "America");
map.put("image", R.drawable.p9);
map = new HashMap<String, Object>();
map.put("title", "荷蘭");
map.put("info", "Dutch");
map.put("image", R.drawable.p9);
data.add(map);
map = new HashMap<String, Object>();
map.put("title", "新西蘭");
map.put("info", "New Zealand");
map.put("image", R.drawable.p7);
data.add(map);
data.add(map);
return data;
return data;
}
2.3)寫自定義adapter(
它是一個內部類,繼承自BaseAdapter,並不確定是否自定義的adapter一般都是繼承它),尤其注意其中的幾個方法
public class MyAdapter extends BaseAdapter{
private LayoutInflater inflater
;//這個一定要懂它的用法及作用
//構造函數:要理解(這里構造方法的意義非常強大,你也可以傳一個數據集合的參數,可以根據需要來傳參數)
public MyAdapter(Context context){
this.inflater = LayoutInflater.from(context);
}
//這里的getCount方法是程序在加載顯示到ui上時就要先讀取的,這里獲得的值決定了listview顯示多少行
@Override
public int getCount() {
//在實際應用中,此處的返回值是由從數據庫中查詢出來的數據的總條數
return testData.size();
}
//構造函數:要理解(這里構造方法的意義非常強大,你也可以傳一個數據集合的參數,可以根據需要來傳參數)
public MyAdapter(Context context){
this.inflater = LayoutInflater.from(context);
}
//這里的getCount方法是程序在加載顯示到ui上時就要先讀取的,這里獲得的值決定了listview顯示多少行
@Override
public int getCount() {
//在實際應用中,此處的返回值是由從數據庫中查詢出來的數據的總條數
return testData.size();
}
//根據ListView所在位置返回View
@Override
public Object getItem(int position) {
// TODO Auto-generated method stub
return this.testData.get(position);
}
public Object getItem(int position) {
// TODO Auto-generated method stub
return this.testData.get(position);
}
//根據ListView位置得到數據源集合中的Id
@Override
public long getItemId(int position) {
// TODO Auto-generated method stub
return position;
}
public long getItemId(int position) {
// TODO Auto-generated method stub
return position;
}
//重寫adapter最重要的就是重寫此方法,此方法也是決定listview界面的樣式的
@Override
public View getView(int position, View convertView, ViewGroup parent) {
//有很多例子中都用到這個holder,理解下??
ViewHolder holder = null;
@Override
public View getView(int position, View convertView, ViewGroup parent) {
//有很多例子中都用到這個holder,理解下??
ViewHolder holder = null;
//思考這里為何要判斷convertView是否為空 ??
if(convertView == null){
holder = new ViewHolder();
//把vlist layout轉換成View【LayoutInflater的作用】
convertView = inflater.inflate(R.layout.vlist, null);
//通過上面layout得到的view來獲取里面的具體控件
holder.image = (ImageView) convertView.findViewById(R.id.image);
holder.title = (TextView) convertView.findViewById(R.id.title);
holder.info = (TextView) convertView.findViewById(R.id.info);
holder.viewBtn = (Button) convertView.findViewById(R.id.viewBtn);
//不懂這里setTag什么意思??
convertView.setTag(holder);
}
else{
holder = (ViewHolder) convertView.getTag();
}
//這里testData.get(position).get("title1")),其實就是從list集合(testData)中取出對應索引的map,然后再根據鍵值對取值
holder.image.setBackgroundResource((Integer) testData.get(position).get("image1"));
holder.title.setText((String) testData.get(position).get("title1"));
holder.info.setText((String) testData.get(position).get("info1"));
//為listview上的button添加click監聽
holder.viewBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
.....
}
});
return convertView;
}
}
if(convertView == null){
holder = new ViewHolder();
//把vlist layout轉換成View【LayoutInflater的作用】
convertView = inflater.inflate(R.layout.vlist, null);
//通過上面layout得到的view來獲取里面的具體控件
holder.image = (ImageView) convertView.findViewById(R.id.image);
holder.title = (TextView) convertView.findViewById(R.id.title);
holder.info = (TextView) convertView.findViewById(R.id.info);
holder.viewBtn = (Button) convertView.findViewById(R.id.viewBtn);
//不懂這里setTag什么意思??
convertView.setTag(holder);
}
else{
holder = (ViewHolder) convertView.getTag();
}
//這里testData.get(position).get("title1")),其實就是從list集合(testData)中取出對應索引的map,然后再根據鍵值對取值
holder.image.setBackgroundResource((Integer) testData.get(position).get("image1"));
holder.title.setText((String) testData.get(position).get("title1"));
holder.info.setText((String) testData.get(position).get("info1"));
//為listview上的button添加click監聽
holder.viewBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
.....
}
});
return convertView;
}
}
到此,整個案例的關鍵部分已經全部出來了,做的過程中有很多地方沒有想通也都做有注釋,做完以后,多揣摩了幾遍才將就理解,嘿嘿

重點提出一個疑問:不能確定是否在onCreate方法中的 new Adapter,在里面表面上看是只調用了一次而進入自定義Adapter中調用(
其實也沒有直接調用,自己想的它可能有一個自己的內部機制,每new完一個Adapter就直接調用getCount/getView方法嗎)它里面的方法 ,調用一次就會繪制一個ListView中的
一個item項,那么有很多條item的時候,它是否要那樣循環調用很多次呢??
getView方法中的參數,convertview那塊。在初始化的時候,每顯示一行item項都會調用一次getView方法但每次調用的時候convertview為null(因為還沒有舊的view); 如果屏幕移動了之后,並且導致某些item項跑到屏幕外面,此時如果還有新的item需要產生,則這些item顯示時調用的getView方法中的 convertview就不為null,而是那些移出到屏幕之外的view,我們所要做的就是將需要顯示的item項填充到移除屏幕外的(舊的)view 中去,注意【convertview為null的不僅僅是初始化顯示的那些item,還有一些是已經開始移入屏幕但還沒有view被回收的那些 item】。
運行結果:
