ListView + Adapter
===
[TOC]
`ListView` 為 Android 常用元件之一,將數據用清單方式顯示出來。
而 `ListView` 必須利用 **`Adapter`** 將資料載入,`Adapter` 是負責管理 `ListView` 每一列的資料與畫面,常用的有以下幾種類別:
* `ArrayAdapter` – `ListView` 簡易用法
* `SimpleAdapter` – 簡易客製化
* `BaseAdapter` – 完整客製化
根據所選擇的 `Adapter`,就要填入遵照其規範的資料格式。
## ArrayAdapter
`ListView` 可看成ios的 `UITableView`。
**`ArrayAdapter`** 是最基本的方法,**`ArrayAdapter`** 原理非常簡單,就是宣告一個陣列把值塞進去,接著 `ListView` 就會依照順序顯示出來,因為 `ListView` 無法做太多的變更,不夠彈性,因此真正開發APP時不太會使用 `ArrayAdapter`
```java=
ListView listview = (ListView) findViewById(R.id.listview);
// ListView 要顯示的內容
String[] str = {"新北市","台北市","台中市","台南市","高雄市"};
ArrayAdapter adapter = new ArrayAdapter(
this,
android.R.layout.simple_list_item_1, // 內建樣式
str // 資料
);
listview.setAdapter(adapter);
```
## SimpleAdapter
可以簡單客製化 `ListView` 樣式,可針對 `ListView` 的每一個 `item` (可視為 `UITablView` 的每一個 `cell`)做設計。不過只能允許接受`<key,value>`格式。
```java=
SimpleAdapter(
Context context,
List<? extends Map<String, ?>> data, // 要顯示的資料
int resource, // 對應的layout .xml檔; 也可以是系統提供內建的樣式: 如 android.R.layout.simple_list_item_2, 此時最後一個參數就要去該xml檔找到對應的元件id
String[] from, // data中的key值
int[] to // 對應到layout的元件id
)
```
延續前一個範例,另外新增一個 layout(`.xml`),命名為 `style_listview.xml`,填入
```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:background="#DEF8FF">
<LinearLayout
android:id="@+id/llBorder"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="5dp">
<TextView
android:id="@+id/tvLocal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="2"
android:text="TextView"
android:textColor="@android:color/black"
android:textSize="20sp" />
<TextView
android:id="@+id/tvName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="TextView"
android:textColor="@android:color/black"
android:textSize="20sp" />
</LinearLayout>
</LinearLayout>
```
將原 `MainActivity.jave` 改寫如下:
```java=
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.SimpleAdapter;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class MainActivity extends AppCompatActivity {
//ListView 要顯示的內容
public String[][] data = {
{"★","臺北市"},
{"-","信義區"},
{"★","新北市"},
{"-","板橋區"},
{"★","桃園市"},
{"-","桃園區"},
{"★","臺中市"},
{"-","西屯區"},
{"★","臺南市"},
{"-","安平區"},
{"-","新營區"},
{"★","高雄市"},
{"-","苓雅區"},
{"-","鳳山區"}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//將資料轉換成<key,value>的型態
List<Map<String, Object>> items = new ArrayList<Map<String,Object>>();
for (int i=0;i < data.length;i++){
Map<String, Object> item = new HashMap<String, Object>();
item.put("level", data[i][0]);
item.put("name", data[i][1]);
items.add(item);
}
//帶入對應資料
SimpleAdapter adapter = new SimpleAdapter(
this,
items,
R.layout.style_listview,
new String[]{"level", "name"},
new int[]{R.id.tvLocal, R.id.tvName}
);
ListView listview = (ListView) findViewById(R.id.listview);
listview.setAdapter(adapter);
listview.setOnItemClickListener(onClickListView);
}
/***
* 點擊ListView事件Method
*/
private AdapterView.OnItemClickListener onClickListView = new AdapterView.OnItemClickListener(){
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Toast.makeText(MainActivity.this,"點選第 "+(position +1) +" 個 \n內容:"+data[position][1], Toast.LENGTH_SHORT).show();
}
};
}
```
## BaseAdapter
可以完整客製化 `ListView` 樣式。
1. 延續前一個範例相同的layout。
2. 建立一個 `ViewAdapter` 類別,命名為 `ViewAdapter.java`,將此類別繼承 `BaseAdapter` ,繼承 `BaseAdapter` 後會出現幾個方法必須實作:
```java
import android.graphics.Color;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.LinearLayout;
import android.widget.TextView;
public class ViewAdapter extends BaseAdapter {
private String[][] ElementsData ; //資料
private LayoutInflater inflater; //加載layout
private int indentionBase; //item缩排
//優化Listview 避免重新加載
//這邊宣告你會動到的Item元件
static class ViewHolder{
LinearLayout rlBorder;
TextView Name;
TextView Local;
}
//初始化
public ViewAdapter(String[][] data, LayoutInflater inflater){
this.ElementsData = data;
this.inflater = inflater;
indentionBase = 100;
}
//取得數量
@Override
public int getCount() {
return ElementsData.length;
}
//取得Item
@Override
public Object getItem(int position) {
return ElementsData[position];
}
//此範例沒有特別設計ID所以隨便回傳一個值
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
//當ListView被拖拉時會不斷觸發getView,為了避免重複加載必須加上這個判斷
if (convertView == null) {
holder = new ViewHolder();
convertView = inflater.inflate(R.layout.style_listview, null);
holder.Name = (TextView) convertView.findViewById(R.id.tvName);
holder.Local = (TextView) convertView.findViewById(R.id.tvLocal);
holder.rlBorder = (LinearLayout) convertView.findViewById(R.id.llBorder);
convertView.setTag(holder);
} else {
// 有 item 被回收時觸發
holder = (ViewHolder) convertView.getTag();
}
if (ElementsData[position][0].equals("1")){
holder.Local.setText("★");
holder.Name.setText(ElementsData[position][1]);
holder.rlBorder.setBackgroundColor(Color.parseColor("#FFDBC9"));
}else{
holder.Local.setText("");
holder.Name.setText(ElementsData[position][1]);
LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) holder.rlBorder.getLayoutParams();
lp.setMargins(indentionBase,0, 0,0); // 縮排
}
return convertView;
}
}
```
3. 改寫主程式:
```java
import android.content.Context;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity {
//ListView 要顯示的內容
public String[][] data = {
{"1","臺北市"},
{"2","信義區"},
{"1","新北市"},
{"2","板橋區"},
{"1","桃園市"},
{"2","桃園區"},
{"1","臺中市"},
{"2","西屯區"},
{"1","臺南市"},
{"2","安平區"},
{"2","新營區"},
{"1","高雄市"},
{"2","苓雅區"},
{"2","鳳山區"}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ListView listview = (ListView) findViewById(R.id.listview);
LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
ViewAdapter adapter = new ViewAdapter(data,inflater);
listview.setAdapter(adapter);
listview.setOnItemClickListener(onClickListView); //指定事件 Method
}
/***
* 點擊ListView事件Method
*/
private AdapterView.OnItemClickListener onClickListView = new AdapterView.OnItemClickListener(){
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Toast.makeText(MainActivity.this,"點選第 "+(position +1) +" 個 \n內容:"+data[position][1], Toast.LENGTH_SHORT).show();
}
};
}
```
## Ref.
[ArrayAdapter](http://learnexp.tw/%E3%80%90android%E3%80%91listview-%E6%95%99%E5%AD%B8-%E5%BE%9E-4%E5%88%B05/)
[SimpleAdapter](https://learnexp.tw/%E3%80%90android%E3%80%91listview-%E9%80%B2%E9%9A%8E%E7%94%A8%E6%B3%95-simpleadapter/)
[BaseAdapter](http://learnexp.tw/%E3%80%90android%E3%80%91listview-%E9%80%B2%E9%9A%8E%E7%94%A8%E6%B3%95-baseadapter/)
###### tags: `介紹`