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: `介紹`