Android 的 ListView (二)

ListView - 定製介面

上一篇文章中,我們介紹了基本的 ListView 用法, 但是我們在專案中,常常會需要不只是顯示文字,而是圖片或者其他更豐富的 ListView 介面。

Imgur

客製化 Adpter

首先我們需要定義一個類別,作為 ListView Adpter 的轉接類型。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Fruit {
private String name;
private int imageId;

public Fruit(String name, int imageId) {
this.name = name;
this.imageId = imageId;
}

public String getName() {
return name;
}

public int getImageId() {
return imageId;
}
}

Fruit 有兩個變數,name 表示水果的名字, imageId 表示水果對應圖片的資源 id。接著我們需要一個自定義的佈局。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?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="wrap_content">

<ImageView
android:id="@+id/fruit_image"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/fruit_name"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="center"
android:layout_margin="10dp"/>
</LinearLayout>

在這個佈局中,我們定義了一個 ImageView 用來顯示水果的圖片,又定義一個 TextView 用來顯示水果的名稱。

接著我們需要一個自定義的 Adpter ,這個自定義 Adpter 繼承自 ArrayAdapter ,並將泛型類別指定為 Fruit 類。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class FruitAdapter extends ArrayAdapter<Fruit> {

private int resourceId;
public FruitAdapter(Context context, int textViewResourceId, List<Fruit> objects) {
super(context, textViewResourceId, objects);
resourceId = textViewResourceId;
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {
Fruit fruit = getItem(position);
View view = LayoutInflater.from(getContext()).inflate(resourceId, parent, false);
ImageView fruitImage = (ImageView) view.findViewById(R.id.fruit_image);
TextView fruitName = (TextView) view.findViewById(R.id.fruit_name);
fruitImage.setImageResource(fruit.getImageId());
fruitName.setText(fruit.getName());
return view;
}
}

FruitAdapter 覆寫了父類別的建構子,將 Context 、 ListView Child Item 佈局 id 還有資料內容都傳遞進來。又覆寫了 getView() ,這個 Function 會在每個 Child Item 被滾動到可視範圍內的時候呼叫。在 getView() 方法中, 首先通過 getItem() 得到目前的 Fruit 實體,然後使用 LayoutInflater 來將這個 Child Item 加入我們傳入的佈局中。

關於 LayoutInflater

LayoutInflater 這個 class 主要功能是將user-defined layout的xml實體化為對應的View物件。有別於setContentView(),可以不用立即將View呈現到UI畫面,讓開發者可依需求在適當的時間或地方呈現出來。

如何使用 LayoutInflater

1
View inflate(int resource, ViewGroup root, boolean attachToRoot)

這邊我們的三個參數設定為 false,因為一但 View 有了它的父節點佈局,那就再也不能添加到 ListView 中了。

結論

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public class MainActivity extends AppCompatActivity {

private List<Fruit> fruitList = new ArrayList<Fruit>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initFruits();
FruitAdapter adapter = new FruitAdapter(MainActivity.this, R.layout.fruit_item, fruitList);
ListView listView = (ListView)findViewById(R.id.list_view);
listView.setAdapter(adapter);
}

private void initFruits() {
for(int i = 0; i < 2; i++) {
Fruit apple = new Fruit("蘋果", R.drawable.apple_pic);
fruitList.add(apple);
Fruit banana = new Fruit("香蕉", R.drawable.banana_pic);
fruitList.add(banana);
Fruit orange = new Fruit("橘子", R.drawable.orange_pic);
fruitList.add(orange);
Fruit watermelon = new Fruit("西瓜", R.drawable.watermelon_pic);
fruitList.add(watermelon);
Fruit pear = new Fruit("梨子", R.drawable.pear_pic);
fruitList.add(pear);
Fruit grape = new Fruit("葡萄", R.drawable.grape_pic);
fruitList.add(grape);
Fruit pineapple = new Fruit("鳳梨", R.drawable.pineapple_pic);
fruitList.add(pineapple);
Fruit strawberry = new Fruit("草莓", R.drawable.strawberry_pic);
fruitList.add(strawberry);
Fruit cherry = new Fruit("櫻桃", R.drawable.cherry_pic);
fruitList.add(cherry);
Fruit mango = new Fruit("芒果", R.drawable.mango_pic);
fruitList.add(mango);

}
}
}

接著我們修改 MainActivity 的代碼,我們這邊加入了 initFruits 函式,用於初始化所有的水果數據。然後用 for 迴圈執行了兩次希望能夠有足夠的資料能讓 ListView 滑動。接著在 onCreate 建立 FruitAdapter 實體,並且將此實體傳給 ListView,這樣就完成了我們的客製化 ListView。