【Android】ListViewを使ったリスト表示

22 min read読了の目安(約20000字

ListViewとは

  • 垂直方向にスクロール可能な1列のリストを表示します。
  • ListViewにデータを表示するには、Adapterを利用します。
  • AdapterとはデータとViewの間を受け渡しするオブジェクトです。

ListViewに使うAdapterにはいくつかの種類がありますが、今回は以下のAdapterを使ってサンプルアプリを実装します。

  • ArrayAdapter
  • SimpleAdapter
  • BaseAdapter

アプリを作る

ArrayAdapter

ArrayAdapterを使ってAndroidのプラットフォームバージョンを表示するアプリを作ります。
プロジェクト名を「ArrayAdapter」で作成して、activity_main.xmlとMainActivity.javaを以下のようにします。

activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <ListView
        android:id="@+id/platform_version_list"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

</LinearLayout>
MainActivity.java
package com.example.arrayadapter;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.widget.ArrayAdapter;
import android.widget.ListView;

public class MainActivity extends AppCompatActivity {

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

        String[] platformVersion = {
            "Android 10.0",
            "Android 9",
            "Android 8.1",
            "Android 8.0",
            "Android 7.1.1",
            "Android 7.1",
            "Android 7.0",
            "Android 6.0",
            "Android 5.1",
            "Android 5.0",
            "Android 4.4W",
            "Android 4.4",
            "Android 4.3",
            "Android 4.2、4.2.2",
            "Android 4.1、4.1.1",
            "Android 4.0.3、4.0.4",
            "Android 4.0、4.0.1、4.0.2",
            "Android 3.2",
            "Android 3.1.x",
            "Android 3.0.x",
            "Android 2.3.4",
            "Android 2.3.3",
            "Android 2.3.2",
            "Android 2.3.1",
            "Android 2.3",
            "Android 2.2.x",
            "Android 2.1.x",
            "Android 2.0.1",
            "Android 2.0",
            "Android 1.6",
            "Android 1.5",
            "Android 1.1",
            "Android 1.0",
        };

        ArrayAdapter<String> arrayAdapter = new ArrayAdapter<>(
            getApplicationContext(),
            android.R.layout.simple_list_item_1,    // Androidに組み込まれているレイアウト
            platformVersion
        );

        ListView listView = findViewById(R.id.platform_version_list);

        listView.setAdapter(arrayAdapter);
    }
}

実行結果

SimpleAdapter

次にSimpleAdapterを使ってアプリを作ります。
専用のレイアウトファイルを作り、Android Versionの一覧リストを作成します。
プロジェクト名を「SimpleAdapter」で作成して以下のプログラムを作ります。

activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <ListView
        android:id="@+id/android_version_list"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</androidx.constraintlayout.widget.ConstraintLayout>
android_version.xml
  • ListViewのレイアウト
<?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:orientation="vertical">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <TextView
            android:id="@+id/platform_version"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_marginStart="8dp"
            android:layout_weight="0.4"
            android:gravity="start|center"
            android:minHeight="?android:attr/listPreferredItemHeightSmall"
            android:textColor="#DD000000"
            android:textSize="12sp" />

        <TextView
            android:id="@+id/api_level"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="0.1"
            android:gravity="start|center"
            android:minHeight="?android:attr/listPreferredItemHeightSmall"
            android:textColor="#DD000000"
            android:textSize="12sp" />

        <TextView
            android:id="@+id/version_code"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_marginStart="8dp"
            android:layout_weight="0.5"
            android:gravity="start|center"
            android:minHeight="?android:attr/listPreferredItemHeightSmall"
            android:textColor="#DD000000"
            android:textSize="12sp" />

    </LinearLayout>

</LinearLayout>
AndroidVersionDetail.java
  • Android Versionのデータを定義する
package com.example.simpleadapter;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class AndroidVersionDetail {

    public static List<Map<String, String>> getData() {

        String[] platformVersionList = {
            "Android 10.0",
            "Android 9",
            "Android 8.1",
            "Android 8.0",
            "Android 7.1, 7.1.1",
            "Android 7.0",
            "Android 6.0",
            "Android 5.1",
            "Android 5.0",
            "Android 4.4W",
            "Android 4.4",
            "Android 4.3",
            "Android 4.2, 4.2.2",
            "Android 4.1, 4.1.1",
            "Android 4.0.3, 4.0.4",
            "Android 4.0, 4.0.1, 4.0.2",
            "Android 3.2",
            "Android 3.1.x",
            "Android 3.0.x",
            "Android 2.3.3, 2.3.4",
            "Android 2.3, 2.3.1, 2.3.2",
            "Android 2.2.x",
            "Android 2.1.x",
            "Android 2.0.1",
            "Android 2.0",
            "Android 1.6",
            "Android 1.5",
            "Android 1.1",
            "Android 1.0",
        };

        String[] apiLevelList = {
            "29",
            "28",
            "27",
            "26",
            "25",
            "24",
            "23",
            "22",
            "21",
            "20",
            "19",
            "18",
            "17",
            "16",
            "15",
            "14",
            "13",
            "12",
            "11",
            "10",
            "9",
            "8",
            "7",
            "6",
            "5",
            "4",
            "3",
            "2",
            "1",
        };

        String[] versionCodeList = {
            "Q",
            "P",
            "O_MR1",
            "O",
            "N_MR1",
            "N",
            "M",
            "LOLLIPOP_MR1",
            "LOLLIPOP",
            "KITKAT_WATCH",
            "KITKAT",
            "JELLY_BEAN_MR2",
            "JELLY_BEAN_MR1",
            "JELLY_BEAN",
            "ICE_CREAM_SANDWICH_MR1",
            "ICE_CREAM_SANDWICH",
            "HONEYCOMB_MR2",
            "HONEYCOMB_MR1",
            "HONEYCOMB",
            "GINGERBREAD_MR1",
            "GINGERBREAD",
            "FROYO",
            "ECLAIR_MR1",
            "ECLAIR_0_1",
            "ECLAIR",
            "DONUT",
            "CUPCAKE",
            "BASE_1_1",
            "BASE",
        };

        List<Map<String, String>> list = new ArrayList<>();

        for (int i = 0; i < platformVersionList.length; i++) {
            Map<String, String> data = new HashMap<>();
            data.put("platformVersion", platformVersionList[i]);
            data.put("apiLevel", apiLevelList[i]);
            data.put("versionCode", versionCodeList[i]);

            list.add(data);
        }

        return list;
    }
}
MainActivity.java
package com.example.simpleadapter;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.widget.ListView;
import android.widget.SimpleAdapter;

import java.util.List;
import java.util.Map;

public class MainActivity extends AppCompatActivity {

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

        List<Map<String, String>> androidVersionList = AndroidVersionDetail.getData();

        SimpleAdapter androidVersionListAdapter = new SimpleAdapter(
            getApplicationContext(),
            androidVersionList,
            R.layout.android_version,
            new String[]{"platformVersion", "apiLevel", "versionCode"},
            new int[]{R.id.platform_version, R.id.api_level, R.id.version_code}
        );

        ListView listView = findViewById(R.id.android_version_list);

        listView.setAdapter(androidVersionListAdapter);
    }
}

実行結果

BaseAdapter

最後にBaseAdapterを使ってアプリを作ります。
以下のページのような、マテリアルデザインのカラーパレットリストを作ってみます。
https://material.io/design/color/the-color-system.html#tools-for-picking-colors
(少し下に移動したところにある、Pinkのカラーパレットを作ります。)

プロジェクト名を「BaseAdapter」にして以下のプログラムを作っていきます。

color_palette.xml
  • ListViewのレイアウト
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/color"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:minHeight="50dp"
        android:padding="20dp" />

    <TextView
        android:id="@+id/color_code"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="start"
        android:layout_marginStart="20dp"
        android:gravity="center"
        android:minHeight="50dp"
        android:textSize="16sp" />

    <TextView
        android:id="@+id/color_value"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="end"
        android:layout_marginEnd="20dp"
        android:gravity="center"
        android:minHeight="50dp"
        android:textSize="16sp" />

</FrameLayout>
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <ListView
        android:id="@+id/color_palette_list"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:divider="@null"/>

</LinearLayout>
ColorPaletteDetail.java
  • カラーパレットのデータを定義
package com.example.baseadapter;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class ColorPaletteDetail {

    public static List<Map<String, String>> getData() {

        String[] colorCode = {
            "Pink 50",
            "100",
            "200",
            "300",
            "400",
            "500",
            "600",
            "700",
            "800",
            "900",
            "A100",
            "A200",
            "A400",
            "A700",
        };

        String[] colorValue = {
            "#FCE4EC",
            "#F8BBD0",
            "#F48FB1",
            "#F06292",
            "#EC407A",
            "#E91E63",
            "#D81B60",
            "#C2185B",
            "#AD1457",
            "#880E4F",
            "#FF80AB",
            "#FF4081",
            "#F50057",
            "#C51162",
        };

        String black = "#FF000000";
        String white = "#FFFFFFFF";

        String[] textColor = {
            black,
            black,
            black,
            black,
            black,
            black,
            white,
            white,
            white,
            white,
            black,
            black,
            black,
            white,
        };

        List<Map<String, String>> list = new ArrayList<>();

        for (int i = 0; i < colorCode.length; i++) {
            Map<String, String> palette = new HashMap<>();
            palette.put("colorCode", colorCode[i]);
            palette.put("colorValue", colorValue[i]);
            palette.put("textColor", textColor[i]);
            list.add(palette);
        }

        return list;
    }
}
ColorPaletteAdapter.java
package com.example.baseadapter;

import android.content.Context;
import android.graphics.Color;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;

import java.util.List;
import java.util.Map;

public class ColorPaletteAdapter extends BaseAdapter {

    private LayoutInflater _inflater;
    private int _layoutId;
    private List<Map<String, String>> _colorPaletteData;

    ColorPaletteAdapter(Context context, int layoutId, List<Map<String, String>> colorPaletteData) {
        this._inflater = LayoutInflater.from(context);
        this._layoutId = layoutId;
        this._colorPaletteData = colorPaletteData;
    }

    @Override
    public int getCount() {
        return _colorPaletteData.size();
    }

    @Override
    public Object getItem(int position) {
        return null;
    }

    @Override
    public long getItemId(int position) {
        return 0;
    }

    /**
     * AdapterViewで表示するViewを生成するためのメソッド
     *
     * @param position  // 表示するITEMの位置
     * @param convertView   // 以前まで表示していたView
     * @param parent    // getViewで生成されたViewの親となるViewGroup
     * @return view
     */
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {

        /*
          Viewを再利用できるか判定する
          nullであればViewを生成する
          nullでなければViewの表示だけ変更して使いまわす
         */
        if (convertView == null) {
            convertView = _inflater.inflate(_layoutId, null);
        }

        TextView color = convertView.findViewById(R.id.color);
        TextView colorCode = convertView.findViewById(R.id.color_code);
        TextView colorValue = convertView.findViewById(R.id.color_value);

        color.setBackgroundColor(
            Color.parseColor(_colorPaletteData.get(position).get("colorValue"))
        );

        colorCode.setText(
            _colorPaletteData.get(position).get("colorCode")
        );
        colorCode.setTextColor(
            Color.parseColor(_colorPaletteData.get(position).get("textColor"))
        );

        colorValue.setText(
            _colorPaletteData.get(position).get("colorValue")
        );
        colorValue.setTextColor(
            Color.parseColor(_colorPaletteData.get(position).get("textColor"))
        );

        return convertView;
    }
}
MainActivity.java
package com.example.baseadapter;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.widget.ListView;

import java.util.List;
import java.util.Map;

public class MainActivity extends AppCompatActivity {

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

        List<Map<String, String>> colorPaletteData = ColorPaletteDetail.getData();

        ColorPaletteAdapter colorPaletteAdapter = new ColorPaletteAdapter(
            MainActivity.this,
            R.layout.color_palette,
            colorPaletteData
        );

        ListView colorPaletteList = findViewById(R.id.color_palette_list);

        colorPaletteList.setAdapter(colorPaletteAdapter);
    }
}

実行結果

ViewHolderパターンを使う

ViewHolderとは

ViewHolderパターンとは、スクロールするたびにコストが高いfindViewByIdを回避するための仕組みです。
ListViewを生成するときの定石っぽいです。
ViewHolderパターンを使用して先ほど作ったカラーパレットを改修します。
ColorPaletteAdapter.javaを以下のように変更します。

ColorPaletteAdapter.java
package com.example.baseadapter;

import android.content.Context;
import android.graphics.Color;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;

import java.util.List;
import java.util.Map;

public class ColorPaletteAdapter extends BaseAdapter {

    private LayoutInflater _inflater;
    private int _layoutId;
    private List<Map<String, String>> _colorPaletteData;

    ColorPaletteAdapter(Context context, int layoutId, List<Map<String, String>> colorPaletteData) {
        this._inflater = LayoutInflater.from(context);
        this._layoutId = layoutId;
        this._colorPaletteData = colorPaletteData;
    }

    @Override
    public int getCount() {
        return _colorPaletteData.size();
    }

    @Override
    public Object getItem(int position) {
        return null;
    }

    @Override
    public long getItemId(int position) {
        return 0;
    }

    /**
     * AdapterViewで表示するViewを生成するためのメソッド
     *
     * @param position  // 表示するITEMの位置
     * @param convertView   // 以前まで表示していたView(リスト1行分)
     * @param parent    // getViewで生成されたViewの親となるViewGroup
     * @return view
     */
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {

        ViewHolder viewHolder;

        View view = convertView;

        // Viewを再利用できるか判定する
        if (view == null) {
            // レイアウトを生成する
            view = _inflater.inflate(_layoutId, null);

            // Viewオブジェクトを生成する
            viewHolder = createViewHolder(view);

            // Viewにオブジェクトを保持する
            view.setTag(viewHolder);
        }

        // Viewに保持したオブジェクトを取り出す
        viewHolder = (ViewHolder) view.getTag();

        viewHolder.color.setBackgroundColor(
            Color.parseColor(_colorPaletteData.get(position).get("colorValue"))
        );

        viewHolder.colorCode.setText(
            _colorPaletteData.get(position).get("colorCode")
        );
        viewHolder.colorCode.setTextColor(
            Color.parseColor(_colorPaletteData.get(position).get("textColor"))
        );

        viewHolder.colorValue.setText(
            _colorPaletteData.get(position).get("colorValue")
        );
        viewHolder.colorValue.setTextColor(
            Color.parseColor(_colorPaletteData.get(position).get("textColor"))
        );

        // returnしたViewは次のgetView()の引数になる
        return view;
    }

    private ViewHolder createViewHolder(View view) {
        TextView color = view.findViewById(R.id.color);
        TextView colorCode = view.findViewById(R.id.color_code);
        TextView colorValue = view.findViewById(R.id.color_value);

        return new ViewHolder(color, colorCode, colorValue);
    }

    private static class ViewHolder {
        TextView color;
        TextView colorCode;
        TextView colorValue;

        ViewHolder(TextView color, TextView colorCode, TextView colorValue) {
            this.color = color;
            this.colorCode = colorCode;
            this.colorValue = colorValue;
        }
    }

}

実行結果は先ほどと同じになります。