Androidはワンツーパンチ 三歩進んで二歩下がる

プログラミングやどうでもいい話

ExpandableListViewをカスタマイズするサンプルを作ってみた

ExpandableListViewの各行の表示を自分でカスタマイズしたレイアウトで実装したサンプルプログラムを作りました。
私の勉強メモです。

Androidに元々用意されているリスト表示形式では、1行に1項目か2項目しか表示できませんが、
リストの1行に当たる部分をXMLファイルに定義することで、様々な表現ができるようになります。

参考にしたのはこれらのサイト様です。いつもお世話になってます。本当にありがとうございます。
Android Expandable List のビューをカスタマイズ
シェフのアプリ開発秘話〜技術的要素を添えて〜

サンプルはこのようなイメージになります。
いまいちわかりにくいのですが、グループのView、子要素のViewを独自にxmlで指定しています。
テキストサイズを大きくしたり太字にしたりしています。
行が変わると背景が交互に黒とグレーになるようにプログラムから動的に指定しています。


サンプルの構成は
◯MyExpandableListAdapter.java
 ExpandableListViewを表示します。
 子要素がクリックされたら、内容をToastで表示します。
 インナークラスに MyExpandableListAdapterInner.java を持っており、ここでアダプターに要素をセットしています。
◯expandablegroupview.xml
 グループのView一行分のレイアウトです。
◯expandablechildview.xml
 子要素のView一行分のレイアウトです。
◯AndroidManifest.xml

では、レイアウトファイルのサンプルから
res/layout/expandablegroupview.xml

 <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/GroupLinearLayout"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical"
    android:paddingLeft="30dp" >

    <TextView
        android:id="@+id/GroupTextView01"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="left"
        android:layout_margin="10sp"
        android:textSize="20sp"
        android:textStyle="bold" />

</LinearLayout>

res/layout/expandablechildview.xml

<?xml version="1.0" encoding="utf-8"?>
<!-- リスト1行分のレイアウト -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical"
    android:id="@+id/ChildLinearLayout" >

    <TextView
        android:id="@+id/TextView01"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="left"
        android:layout_margin="10sp"
        android:textSize="20sp"
        android:textStyle="bold" />

    <TextView
        android:id="@+id/TextView02"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="10sp"
        android:layout_gravity="left" />

</LinearLayout>

src/MyExpandableListAdapter.java

package com.sakurafish.android.sample;

import android.app.ExpandableListActivity;
import android.content.Context;
import android.graphics.Color;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseExpandableListAdapter;
import android.widget.ExpandableListAdapter;
import android.widget.ExpandableListView;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;

public class MyExpandableListAdapter extends ExpandableListActivity {

    ExpandableListAdapter mAdapter;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // インナークラスでアダプターの内容をセット
        mAdapter = new MyExpandableListAdapterInner();
        // アダプターをExpandableListViewにセット
        setListAdapter(mAdapter);
    }

    @Override
    public boolean onChildClick(ExpandableListView parent, View v, int groupPosition,
            int childPosition, long id) {

        // 子要素がクリックされたら内容をToastで表示する
        Object o = mAdapter.getChild(groupPosition, childPosition);
        Toast.makeText(getApplicationContext(), o.toString(), Toast.LENGTH_SHORT).show();

        return super.onChildClick(parent, v, groupPosition, childPosition, id);
    }

    /***
     * アダプタークラス
     */
    public class MyExpandableListAdapterInner extends BaseExpandableListAdapter {

        // @formatter:off
        private final String[] groups = {"Group1", "Group2", "Group3"};  
        private final String[][][] children = {  
            {{"Group1 Child1", "Child text1"},
             {"Group1 Child2", "Child text2"},
             {"Group1 Child3", "Child text3"}},
            {{"Group2 Child1", "Child text1"}},
            {{"Group3 Child1", "Child text1"},
             {"Group3 Child2", "Child text2"},
             {"Group3 Child3", "Child text3"},
             {"Group3 Child4", "Child text4"},
             {"Group3 Child5", "Child text5"}}
            };
        // @formatter:on

        /** レイアウトをインフレートするための変数 */
        private LayoutInflater layoutInflater = (LayoutInflater) getApplicationContext()
                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);;

        @Override
        public Object getChild(int groupPosition, int childPosition) {
            return children[groupPosition][childPosition][0];
        }

        /***
         * 子要素を返す(オーバーロード)
         * 
         * @param groupPosition
         * @param childPosition
         * @param textPosition
         * @return
         */
        public String getChild(int groupPosition, int childPosition, int textPosition) {
            return children[groupPosition][childPosition][textPosition];
        }

        @Override
        public long getChildId(int groupPosition, int childPosition) {
            return childPosition;
        }

        @Override
        public int getChildrenCount(int groupPosition) {
            return children[groupPosition].length;
        }

        @Override
        public View getChildView(int groupPosition, int childPosition,
                boolean isLastChild, View convertView, ViewGroup parent) {

            // 子要素のViewを作る
            // convertViewがnullの時だけインフレートする(1行ごとに呼ばれるので使いまわす)
            if (convertView == null) {
                convertView = layoutInflater.inflate(R.layout.expandchildview,
                        null);
            }

            // 子要素の現在位置が奇数の場合はグレー、偶数の場合は黒を背景にセットする
            LinearLayout linearLayout = (LinearLayout)
                    convertView.findViewById(R.id.ChildLinearLayout);
            if ((childPosition % 2) != 0) {
                linearLayout.setBackgroundColor(Color.DKGRAY);
            } else {
                linearLayout.setBackgroundColor(Color.BLACK);
            }

            TextView textView1 = (TextView) convertView.findViewById(R.id.TextView01);
            TextView textView2 = (TextView) convertView.findViewById(R.id.TextView02);
            textView1.setText(getChild(groupPosition, childPosition, 0).toString());
            textView2.setText(getChild(groupPosition, childPosition, 1).toString());

            return convertView;
        }

        @Override
        public Object getGroup(int groupPosition) {
            return groups[groupPosition];
        }

        @Override
        public int getGroupCount() {
            return groups.length;
        }

        @Override
        public long getGroupId(int groupPosition) {
            return groupPosition;
        }

        @Override
        public View getGroupView(int groupPosition, boolean isExpanded,
                View convertView, ViewGroup parent) {
            // グループのViewを作る処理
            // convertViewがnullの時だけインフレートする(1行ごとに呼ばれるので使いまわす)
            if (convertView == null) {
                convertView = layoutInflater.inflate(R.layout.expandgroupview,
                        null);
            }

            // 子要素の現在位置が偶数の場合はグレー、奇数の場合は黒を背景にセットする
            LinearLayout linearLayout = (LinearLayout) convertView
                    .findViewById(R.id.GroupLinearLayout);
            if ((groupPosition % 2) == 0) {
                linearLayout.setBackgroundColor(Color.DKGRAY);
            } else {
                linearLayout.setBackgroundColor(Color.BLACK);
            }

            TextView textView = (TextView) convertView.findViewById(R.id.GroupTextView01);
            textView.setText(getGroup(groupPosition).toString());

            return convertView;
        }

        @Override
        public boolean isChildSelectable(int groupPosition, int childPosition) {
            return true;
        }

        @Override
        public boolean hasStableIds() {
            return true;
        }

    }
}

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.sakurafish.android.sample"
    android:versionCode="2"
    android:versionName="1.0" >

    <uses-sdk android:minSdkVersion="4" />

    <application
        android:debuggable="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name" >
        <activity
            android:name=".MyExpandableListAdapter"
            android:icon="@drawable/ic_launcher"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>