Android開発のリストビューについてまとめて見た

#はじめに
こんにちは。某学校でプログラミング等の勉強中のサーバーサイドのプログラマーのワタタクです。:relaxed:
今回もAndroid開発していきましょう。
Androidには様々なリストビューの出し方があるので、筆者自身も混乱してきたので魔と寝ていきたいと思います。

#対象者
- Javaがなんとなく書ける人。
- Android開発が曖昧だけど多少できる人。　

#リストビューの基本
##リストタップのリスナ
○リストビューをタップした時のリスナクラス : **onItemClickListener**インタフェースを実装する。
``＊このインタフェースはAdapterViewクラスのメンバインタフェース``
○リスト設定メソッド

```
setOnItemClickListener(onItemClickListenerを実装したメンバインタフェースを実装したものを入れる)
```
##タップ要素の取得
**1)イベントハンドラ : onItemClick()**
引数は４つ

```
1.AdapterView<?> parent 
->タップされたListView全体

2.View view
->タップされた１行分の画面部品

3.int position
->タップされた行番号　
＊0から始まる

4.long id
->DBのデータをもとにListViewを生成した際の主キーの値
＊DBを使わない場合は上のpositionと同じ値
```
**2)データ取得**
○タップした行のデータはparentとpositionを使って取得できる

```
(String)parent.getItemAtPosition(position)
```
##サンプルコード
```java

private class ListItemClickListener implements AdapterView.OnItemClickListener {
    public void onItemClick(AdapterView<?> parent, View view, int position, long l) {
        String item = (String)parent.getItemAtPosition(position);
        Toast.makeText(ListClickSampleActivity.this, show, Toast.LENGTH_SHORT).show();
    }
}
```
#静的のリストビュー
**1) xmlで作る**

```xml:activity_list_click_sample.xml
<?xml version="1.0" encoding="utf-8"?>
<ListView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/lvMenu"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:entries="@array/lv_menu"/>
```
```xml:string.xml
<?xml version="1.0" encoding="UTF-8" ?>
<resources>
    <string name="app_name">リスト選択サンプル</string>
    <string-array name="lv_menu">
        <item>唐揚げ</item>
        <item>ハンバーグ定食</item>
        <item>生姜焼き定食</item>
        <item>ステーキ定食</item>
        <item>野菜炒め定食</item>
        <item>回鍋肉定食</item>
        <item>麻婆豆腐定食</item>
        <item>とんかつ定食</item>
        <item>ミンチカツ定食</item>
        <item>チキンかつ定食</item>
        <item>コロッケ定食</item>
        <item>焼き魚定食</item>
        <item>焼肉定食</item>
        <item>弱肉強食</item>
    </string-array>
</resources>
```

```java:ListClickSampleActivity.java
public class ListClickSampleActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_list_click_sample);

        ListView lvMenu = findViewById(R.id.lvMenu);
        lvMenu.setOnItemClickListener(ListItemClickListener);

    }

    AdapterView.OnItemClickListener ListItemClickListener = new AdapterView.OnItemClickListener() {
        @Override
        public void onItemClick(AdapterView<?> parent, View view, int position, long l) {
            String item = (String)parent.getItemAtPosition(position);
            String show = "あなたが選んだ定食：" + item;
            Toast.makeText(ListClickSampleActivity.this, show, Toast.LENGTH_SHORT).show();
        }
    };
}
```
**2)Javaコードで作る**

```xml
<?xml version="1.0" encoding="utf-8"?>
<ListView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/lvPref"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
</ListView>
```

```java:PrefListActivity.java
public class PrefListActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_pref_list);

        ListView lvPref = findViewById(R.id.lvPref);

        List<String> prefList = createPrefectureList();
        ArrayAdapter<String> adapter = new ArrayAdapter<>(PrefListActivity.this, android.R.layout.simple_list_item_1, prefList);
        lvPref.setAdapter(adapter);

        lvPref.setOnItemClickListener(new ListItemClickListener());
    }

    /**
     * 都道府県リストを生成するメソッド。
     * @return 都道府県リストオブジェクト。
     */
    private List<String> createPrefectureList() {
        List<String> prefList = new ArrayList<>();
        prefList.add("北海道");
        prefList.add("青森県");
        prefList.add("岩手県");
        prefList.add("宮城県");
        prefList.add("秋田県");
        prefList.add("山形県");
        prefList.add("福島県");
        prefList.add("茨城県");
        prefList.add("栃木県");
        prefList.add("群馬県");
        prefList.add("埼玉県");
        prefList.add("千葉県");
        prefList.add("東京都");
        prefList.add("神奈川県");
        prefList.add("新潟県");
        prefList.add("富山県");
        prefList.add("石川県");
        prefList.add("福井県");
        prefList.add("山梨県");
        prefList.add("長野県");
        prefList.add("岐阜県");
        prefList.add("静岡県");
        prefList.add("愛知県");
        prefList.add("三重県");
        prefList.add("滋賀県");
        prefList.add("京都府");
        prefList.add("大阪府");
        prefList.add("兵庫県");
        prefList.add("奈良県");
        prefList.add("和歌山県");
        prefList.add("鳥取県");
        prefList.add("島根県");
        prefList.add("岡山県");
        prefList.add("広島県");
        prefList.add("山口県");
        prefList.add("徳島県");
        prefList.add("香川県");
        prefList.add("愛媛県");
        prefList.add("高知県");
        prefList.add("福岡県");
        prefList.add("佐賀県");
        prefList.add("長崎県");
        prefList.add("熊本県");
        prefList.add("大分県");
        prefList.add("宮崎県");
        prefList.add("鹿児島県");
        prefList.add("沖縄県");
        return prefList;
    }

    /**
     * リストが選択されたときの処理が記述されたメンバクラス。
     * 第2画面へ処理を移管する。
     */
    private class ListItemClickListener implements AdapterView.OnItemClickListener {
        @Override
        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
            String prefName = (String) parent.getItemAtPosition(position);

            Intent intent = new Intent(PrefListActivity.this, PrefEditActivity.class);
            intent.putExtra("selectedPrefNo", position);
            intent.putExtra("selectedPrefName", prefName);
            startActivity(intent);
        }
    }
}
```

#動的のリストビュー
**SimpleCursorAdapter**クラスを使う。
→DB内のデータをもとにリストビューを作りたいときに使う。
引数は６つ

```
1.コンテキスト

2.リストビューの各行のレイアウトを表すR値
＊simple_list_view_item_1で通常いい。

3.Cursorオブジェクト

4.String[] : カラム名の配列

5.int[] : 画面部品のR値の配列

6.0でいい
```
これをリストビューに表示（登録）する方法
->ListViewクラスの**setAdapter**メソッドを使う
##サンプルコード

```java:MemoListActivity.java
public class MemoListActivity extends AppCompatActivity {
    private ListView _lvMemoList;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_memo_list);

        _lvMemoList = findViewById(R.id.lvMemoList);
        _lvMemoList.setOnItemClickListener(new ListItemClickListener());
    }

    @Override
    protected void onResume() {
        super.onResume();
        DatabaseHelper helper = new DatabaseHelper(MemoListActivity.this);
        SQLiteDatabase db = helper.getWritableDatabase();
        Cursor cursor = DataAccess.findAll(db);
        String[] from = {"title"};
        int[] to = {android.R.id.text1};
        SimpleCursorAdapter adapter = new SimpleCursorAdapter(MemoListActivity.this, android.R.layout.simple_list_item_1, cursor, from, to, 0);
        _lvMemoList.setAdapter(adapter);
    }
}
```
``onResume : 画面を更新するメソッド``

##カスタマイズリスト
**SimpleAdapter**を使う。
##row
リストビューの各行をカスタマイズするには以下の手順をとる。
**1) 1行分のレイアウト.xmlファイルを用意する**
-> xmlファイルの書き方は通常のレイアウトファイルと同じ

```xml:res/layout/row.xml 
<LinearLayout ... >
    <ImageView ... />
    <TextView ... /> 
    <TextView ... /> 
    <TextView ... />
</LinearLayout>
```
``＊ファイル名はなんでも良い``

**2) アダプタをnewする時にレイアウトを指定する引数に 1) のファイルのR値を指定する。**

```
(ex.)
new.SimpleAdapter( ..., _list, R.layout.row, ... );
```
**3) アダプタクラスをnewする時の引数toのint配列では 1) のファイル中に記述したidのR値を指定する。**

```
(ex.)
int[] to = {R.id.tvMenuName, R.id.tvMenuPrice}
```
##ViewBinder 
**(1)ViewBinderとは**
``row.xmlを使うと、リストビュー先行のレイアウトがカスタマイズできる。 ``

では、データに合わせて、各行の表示内容まで変更するには?

from - toの組み合わせでfromのデータをtoの画面部品に割り当てる処理を自作する (ViewBinderの作成)、手順は以下の通り。

**1) SimpleAdapter.ViewBinderインタフェースを実装したクラスを作成する。 -> privateなメンバクラスで良い**

**2) このクラスのsetViewValue()メソッドに画面部品へのデータの割り当て処理を記述する。(後述)**

**3) アダプタクラスのsetViewBinder()メソッドにこのクラスをnewしたものを渡す**

```
(ex.) 
private class CustomViewBinder implement SimpleAdapter.ViewBinder {
    @Override
    public boolean setViewValue() { 

         ここに各行内画面部品のデータ割り当てを記述
    }  
}

adapter.setViewBinder(new CustomViewBinder());
```

**(2) setViewValue() の引数**
setViewValue()のメソッドの引数は以下の3コ。

```
1. View view : 1 行分の中の画面部品。
2. Object data : 上記で第 1 引数に割り当てるデータ。
3. String textReprosentation : 第 2 引数の内容を文字列化したもの。第二引数が null の場合は、
空白文字列。
```

**(3)setViewValue()内の処理パターン**
setViewValue()内は引数 viewのidを使って分岐し、引数 data を使って処理を記述していく。 
→ switch 文が便利。

```
public booelan setViewValue ( .... ) {
  int viewId = view.getId(); ← id を取得
  switch( viewId ) { ← id に応じて処理を分岐 (switch)
    case R . id . imPhoneType : ← id 同士の比較なので R 値
      ImageView imPhoneType = ( ImageView ) view;
      int phoneType = ( Integer ) data;

      あとはお好きに。

      return true;     
    case .....
  }
  return false
}
```

``setViewValue() メソッド内では、ビューにデータを埋め込む独自処理を記述した場合、つまり、各 case の末尾に return true を記述する。さらに、メソッド末尾に return false を記述する。
→ この戻り値の値によって、os 側でデータの埋め込み処理を行うかどうかの判定を行なっている。``

**(4) SimpleCursorAdapter の場合**
SimpleCursorAdapter の場合でも、ViewBinder の setViewValue() の処理は SimpleAdapter と同じ。

```
view . getId() の値を switch で分岐させる。ただし、引数は以下の 3 コ。
1. View view : リスト 1 行中の画面部品。
2. Cursor cursor : 対象となっているリスト 1 行分のデータが格納されたカーソルオブジェクト。
3. int columnIndex : 上記第 1 引数に割り当てるデータのカラムインデックス。
```

##DBデータのリロード
リストビューの元データとなる DB 内のデータが更新された場合、DB からデータを取り直して再表示させる必要がある。

```
○以下のソースコードとなる。
SimpleCursorAdapter adapter = ( SimpleCursorAdapter ) _lvPhones . getAdapter();

adapter . changeCursor( cursor ); ← カーソルの入れ替え。 新しいカーソル
```
*(注)*
``onCreate() でアダプタの設定を行い、onResume() でカーソルの設定 ( 入れ替え ) を行うよう な場合、 onCreate() 内で SimpleCursorAdapter を new する際の引数 cursor には null を渡せば良い。``
##サンプルコード１
```java
public class CustomizedListView2Activity extends AppCompatActivity {
    /**
     * リストビューに表示させるリストデータ。
     */
    private List<Map<String, Object>> _list;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_customized_list_view2);

        _list = createList();

        String[] from = {"phoneType", "phoneNo", "phoneSex"};
        int[] to = {R.id.imPhoneType, R.id.tvPhoneNo, R.id.tvSex};
        SimpleAdapter adapter = new SimpleAdapter(CustomizedListView2Activity.this, _list, R.layout.row, from, to);
        adapter.setViewBinder(new CustomViewBinder());
        ListView lvPhones = findViewById(R.id.lvPhones);
        lvPhones.setAdapter(adapter);
    }

    /**
     * リストビューに表示されるリストデータを生成するメソッド。
     *
     * @return 生成されたリストデータ。
     */
    private List<Map<String, Object>> createList() {
        List<Map<String, Object>> list = new ArrayList<>();

        for (int i = 1; i <= 30; i++) {
            int phoneType = (int) (3 * Math.random()) + 1;
            int phoneNoInt = (int) (99999999 * Math.random());
            int phoneSexInt = (int) (10*Math.random() + 1);

            String phoneNo = "090" + String.format("%08d", phoneNoInt);

            int phoneSex = 1;
            if(phoneSexInt <= 5) {
                phoneSex = 0;
            }

            Map<String, Object> map = new HashMap<>();
            map.put("phoneType", phoneType);
            map.put("phoneNo", phoneNo);
            map.put("phoneSex", phoneSex);
            list.add(map);
        }

        return list;
    }

    /**
     * リストビューのカスタムビューバインダークラス。
     */
    private class CustomViewBinder implements SimpleAdapter.ViewBinder {

        @Override
        public boolean setViewValue(View view, Object data, String textRepresentation) {
            int viewId = view.getId();
            switch(viewId) {
                case R.id.imPhoneType:
                    ImageView imPhoneType = (ImageView) view;
                    int phoneType = (Integer) data;
                    switch(phoneType) {
                        case 2:
                            imPhoneType.setImageResource(android.R.drawable.ic_menu_crop);
                            break;
                        case 3:
                            imPhoneType.setImageResource(android.R.drawable.ic_menu_myplaces);
                            break;
                        default:
                            imPhoneType.setImageResource(android.R.drawable.ic_menu_call);
                            break;
                    }
                    return true;
                case R.id.tvPhoneNo:
                    TextView tvPhoneNo = (TextView) view;
                    String phoneNo = (String) data;
                    tvPhoneNo.setText(phoneNo);
                    return true;
                case R.id.tvSex:
                    TextView tvSex = (TextView) view;
                    int phoneSex = (Integer) data;
                    String sex = "♂";
                    if(phoneSex == 0) {
                        sex = "♀";
                    }
                    tvSex.setText(sex);
                    return true;
            }
            return false;
        }
    }
}
```

##サンプルコード2

```java
public class CustomizedListView3Activity extends AppCompatActivity {
    
    private ListView _lvPhones;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_customized_list_view3);
        _lvPhones = findViewById(R.id.lvPhones);

        String[] from = {"phone_type", "phone_no", "phone_sex"};
        int[] to = {R.id.imPhoneType, R.id.tvPhoneNo, R.id.tvSex};
        SimpleCursorAdapter adapter = new SimpleCursorAdapter(CustomizedListView3Activity.this, R.layout.row, null, from, to, 0);
        adapter.setViewBinder(new CustomViewBinder());
        _lvPhones.setAdapter(adapter);
    }

    @Override
    public void onResume() {
        super.onResume();
        setNewCursor();
    }

    /**
     * カーソルアダプタ内のカーソルを更新するメソッド。
     */
    private void setNewCursor() {
        DatabaseHelper helper = new DatabaseHelper(CustomizedListView3Activity.this);
        SQLiteDatabase db = helper.getWritableDatabase();
        Cursor cursor = DataAccess.findAll(db);
        SimpleCursorAdapter adapter = (SimpleCursorAdapter) _lvPhones.getAdapter();
        adapter.changeCursor(cursor);
    }

    /**
     * リストビューのカスタムビューバインダークラス。
     *
     * @author Shinzo SAITO
     */
    private class CustomViewBinder implements SimpleCursorAdapter.ViewBinder {

        @Override
        public boolean setViewValue(View view, Cursor cursor, int columnIndex) {
            int viewId = view.getId();
            switch(viewId) {
                case R.id.imPhoneType:
                    ImageView imPhoneType = (ImageView) view;
                    int phoneType = cursor.getInt(columnIndex);
                    switch(phoneType) {
                        case 2:
                            imPhoneType.setImageResource(android.R.drawable.ic_menu_crop);
                            break;
                        case 3:
                            imPhoneType.setImageResource(android.R.drawable.ic_menu_myplaces);
                            break;
                        default:
                            imPhoneType.setImageResource(android.R.drawable.ic_menu_call);
                            break;
                    }
                    return true;
                case R.id.tvPhoneNo:
                    TextView tvPhoneNo = (TextView) view;
                    String phoneNo = cursor.getString(columnIndex);
                    tvPhoneNo.setText(phoneNo);
                    return true;
                case R.id.tvSex:
                    TextView tvSex = (TextView) view;
                    int phoneSex = cursor.getInt(columnIndex);
                    String sex = "♂";
                    if(phoneSex == 0) {
                        sex = "♀";
                    }
                    tvSex.setText(sex);
                    return true;
            }
            return false;
        }
    }
}

```


以上。Androidでのリストビューについてまとめて見ました。
もし何か間違っている等のご指摘があればご連絡ください。
最後まで読んで頂きありがとうございました。