---
# System prepended metadata

title: ListView + Adapter
tags: [介紹]

---

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