[Android]折り畳みリストExpandableListViewを使う

投稿者:

折り畳み式リストはユーザが任意に不要なデータを非表示にして必要なデータだけを表示することができる。スマートフォンのような小さい画面で多めのデータを扱うときはとても有効だ。

Androidで折り畳み式リストを使いたいときはExpandableListViewクラスを使用する。

ListViewと同じで、ExpandableListViewもUIを担当するクラスとデータとレイアウトを担当するアダプターとセットで運用する。

アダプターはBaseExpandableListAdapterクラスを継承して作成する。データは親要素のリストとそれにぶら下がる子要素のふたつを用意する。子要素は「リストのリスト」みたいな形になるので少々頭がこんがらがるが、難しいことはない。

そんなわけでサンプルを見て行こう。

まずはレイアウト

activity_main.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">

    <ExpandableListView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/exlistview"/>
</LinearLayout>

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

    <TextView
        android:id="@+id/listitem_cars_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="15sp"
        android:layout_marginStart="60dp"
        android:paddingTop="5dp"
        android:paddingBottom="5dp"/>
</LinearLayout>

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

    <TextView
        android:id="@+id/listitem_makers_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="20sp"
        android:layout_marginStart="40dp"
        android:paddingTop="10dp"
        android:paddingBottom="10dp"/>
</LinearLayout>

ファイルの意味合いは以下のようになっている

  • activity_main.xml = メインアクティビティのレイアウト
  • listitem_cars.xml = 子要素のレイアウト
  • listitem_makers.xml = 親要素のレイアウト

続いてプログラム本体

MainActivity.java
public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //親要素のリスト
        List<String> makers = new ArrayList<>();
        makers.add("TOYOTA");
        makers.add("MAZDA");
        makers.add("HONDA");

        //子要素のリスト
        List<String> cars_toyota = new ArrayList<>();
        cars_toyota.add("CROWN");
        cars_toyota.add("PRIUS");
        cars_toyota.add("COROLLA");
        cars_toyota.add("VITZ");
        List<String> cars_mazda = new ArrayList<>();
        cars_mazda.add("ATENZA");
        cars_mazda.add("AXELA");
        cars_mazda.add("DEMIO");
        List<String> cars_honda = new ArrayList<>();
        cars_honda.add("LEGEND");
        cars_honda.add("CIVIC");
        cars_honda.add("FIT");
        List<List<String>> cars = new ArrayList<>();
        cars.add(cars_toyota);
        cars.add(cars_mazda);
        cars.add(cars_honda);

        //ExpandableListViewの初期化
        ExpandableListView exListView = findViewById(R.id.exlistview);
        CarMakerListAdapter adapter = new CarMakerListAdapter(this, makers, cars);
        exListView.setAdapter(adapter);

        exListView.setOnChildClickListener(new ExpandableListView.OnChildClickListener() {
            @Override
            public boolean onChildClick(ExpandableListView parent, View v, int groupPosition, int childPosition, long id) {
                //子要素をタップした時の処理
                //このサンプルではToastメッセージを表示するだけ
                CarMakerListAdapter adapter1 = (CarMakerListAdapter) parent.getExpandableListAdapter();
                String makername =  (String)adapter1.getGroup(groupPosition);       //親要素からメーカー名を取得
                String carname = (String)adapter1.getChild(groupPosition, childPosition);   //子要素から車名を取得
                Toast.makeText(getApplicationContext(), makername + " : " + carname, Toast.LENGTH_LONG).show();  //Toast生成
                return true;
            }
        });
    }



    class CarMakerListAdapter extends BaseExpandableListAdapter {
        //メンバ変数
        List<String> listMaker;     //親要素のリスト
        List<List<String>> listCar; //子要素のリスト
        Context context;

        //コンストラクタ
        CarMakerListAdapter (Context context, List<String> listMaker, List<List<String>> listCar) {
            this.context    = context;
            this.listMaker = listMaker;
            this.listCar = listCar;
        }

        @Override
        public int getGroupCount() {
            return listMaker.size();    //親要素の数を返す
        }

        @Override
        public int getChildrenCount(int groupPosition) {
            return listCar.get(groupPosition).size();   //子要素の数を返す
        }

        @Override
        public Object getGroup(int groupPosition) {
            return listMaker.get(groupPosition);    //親要素を取得
        }

        @Override
        public Object getChild(int groupPosition, int childPosition) {
            return listCar.get(groupPosition).get(childPosition);   //子要素を取得
        }

        @Override
        public long getGroupId(int groupPosition) {
            //親要素の固有IDを返す
            //このサンプルでは固有IDは無いので0
            return 0;
        }

        @Override
        public long getChildId(int groupPosition, int childPosition) {
            //子要素の固有IDを返す
            //このサンプルでは固有IDは無いので0
            return 0;
        }

        @Override
        public boolean hasStableIds() {
            //固有IDを持つかどうかを返す
            //このサンプルでは持たないのでfalse
            return false;
        }

        @Override
        public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) {
            //親要素のレイアウト生成
            if (convertView == null) {
                LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
                convertView = inflater.inflate(R.layout.listitem_makers, parent, false);
            }
            TextView tv = convertView.findViewById(R.id.listitem_makers_name);
            tv.setText(listMaker.get(groupPosition));
            return convertView;
        }

        @Override
        public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) {
            //子要素のレイアウト生成
            if (convertView == null) {
                LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
                convertView = inflater.inflate(R.layout.listitem_cars, parent, false);
            }
            TextView tv = convertView.findViewById(R.id.listitem_cars_name);
            tv.setText(listCar.get(groupPosition).get(childPosition));
            return convertView;
        }

        @Override
        public boolean isChildSelectable(int groupPosition, int childPosition) {
            //子要素がタップ可能かどうかを返す
            //このサンプルでは可能にするのでtrue
            return true;
        }
    }
}

アプリを実行すると次のようなリストが表示される。

リスト行のどれかをタップすると子要素が展開表示され、それをタップするとToastメッセージが表示されるはずだ。

ExpandableListViewを利用する上での注意点は、親要素の左に展開状態を示すアイコンが表示されるため、レイアウトを作成する際にその分のスペースを空けておかなければならない点だ。例ではTextViewの前に60dpマージンを設定している。

もしこのアイコンを表示したくないときはxmlファイルでgroupIndicatorパラメータに”@null”を指定すればよい。

<ExpandableListView
    android:id="@+id/expandable_listview"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:groupIndicator="@null"/>

groupIndicatorに自作アイコンなどを指定することもできる。
詳しくは以下のサイトを参照してほしい。
https://kamiya-kizuku.hatenablog.com/entry/2018/03/03/ExpandableListView…
https://techbooster.org/android/ui/6204/

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください