【Android開発】タイトル行固定で複数列のリストを表示する方法

新作アプリの開発を始めました。
詳しくはリリース時のお楽しみですが、子供向けの小遣い帳を作ろうと考えています。
本日は、そのアプリのメインとなる一覧画面の実装時の工夫についての共有です。

本対応の目的

一覧画面ではタイトル部分を上部に固定して、データ部分をスクロールできるようにしています。Excelで言うところの「ウィンドウ枠の固定」みたいなイメージです。

サンプルコードのキャプチャですが、こんな感じの画面を実装することができます。

縦1

縦2

横向きだとこんな感じ。

横1

横2

layoutの準備

<?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/title1"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:textSize="@dimen/sp_m"
            android:layout_weight="3"
            android:gravity="center"
            android:layout_marginRight="@dimen/dp_s"
            android:layout_marginBottom="@dimen/dp_s"
            android:padding="@dimen/dp_m"
            android:background ="@color/title"
            android:text="@string/title1"/>

        <TextView
            android:id="@+id/title2"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:textSize="@dimen/sp_m"
            android:layout_weight="2"
            android:gravity="center"
            android:layout_marginRight="@dimen/dp_s"
            android:layout_marginBottom="@dimen/dp_s"
            android:padding="@dimen/dp_m"
            android:background ="@color/title"
            android:text="@string/title2"/>

        <TextView
            android:id="@+id/title3"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:textSize="@dimen/sp_m"
            android:layout_weight="3"
            android:gravity="center"
            android:layout_marginBottom="@dimen/dp_s"
            android:padding="@dimen/dp_m"
            android:background ="@color/title"
            android:text="@string/title3"/>
    </LinearLayout>

    <ListView
        android:id="@+id/android:list"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1" />
</LinearLayout>

メインのレイアウトファイルでは、タイトル部のTextViewとデータ部の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="horizontal" >

    <TextView
        android:id="@+id/raw1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_weight="3"
        android:singleLine="true"
        android:layout_marginRight="@dimen/dp_s"
        android:padding="@dimen/dp_m"
        android:textSize="@dimen/sp_m" />

    <TextView
        android:id="@+id/raw2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_weight="2"
        android:singleLine="true"
        android:layout_marginRight="@dimen/dp_s"
        android:padding="@dimen/dp_m"
        android:textSize="@dimen/sp_m" />

    <TextView
        android:id="@+id/raw3"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_weight="3"
        android:singleLine="true"
        android:gravity="end"
        android:padding="@dimen/dp_m"
        android:textSize="@dimen/sp_m" />

</LinearLayout>

ListView用のレイアウトファイルを用意します。「layout_weight、margin、padding」をメインのレイアウトファイルで相対するTextViewと同様の設定とするのがポイントとなります。(同様にしないとタイトル部とデータ部でズレが発生)

valuesの準備

<resources>
    <string name="app_name">Sample</string>
    <string name="title1">日付</string>
    <string name="title2">ことがら</string>
    <string name="title3">残金</string>
</resources>

文言用ファイル。とりあえず必要最小限を設定しています。

<resources>
    <!-- 文字サイズ(小) -->
    <dimen name="sp_s">16sp</dimen>
    <!-- 文字サイズ(中) -->
    <dimen name="sp_m">20sp</dimen>
    <!-- 文字サイズ(大) -->
    <dimen name="sp_l">24sp</dimen>
    <!-- px指定のサイズ(小) -->
    <dimen name="px_s">1px</dimen>
    <!-- px指定のサイズ(中) -->
    <dimen name="px_m">2px</dimen>
    <!-- 余白などのサイズ(小) -->
    <dimen name="dp_s">1dp</dimen>
    <!-- 余白などのサイズ(中) -->
    <dimen name="dp_m">4dp</dimen>
    <!-- 余白などのサイズ(大) -->
    <dimen name="dp_l">8dp</dimen>
</resources>

dimen用ファイル。文字サイズ、余白サイズをまとめて指定します。

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <!-- タイトル背景 -->
    <color name="title">#ff6767</color>
    <!-- データ1背景 -->
    <color name="data1">#ffcdcd</color>
    <!-- データ2背景 -->
    <color name="data2">#ff9a9a</color>
</resources>

色設定用ファイル。タイトル行、データ部の偶数行、奇数行の背景色を指定します。

Java側の記載

package jp.co.skys.android.sample;

import android.app.ListActivity;
import android.content.Context;
import android.content.res.Resources;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.TextView;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;

public class multipleRowListSample extends ListActivity {

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

        // サンプル用のデータを準備
        List<Data> dataList = new ArrayList<>();

        // 現在時刻取得
        Date dt = new Date();
        long timestamp = dt.getTime();

        // サンプル用のデータを詰め込む
        for (int i = 0; i <= 100; i++) {
            Data data = new Data();
            data.setLongData(timestamp - 86400000 * (long) i);
            data.setStringData(String.valueOf(i) + ":abcdefghijklmnopqrstuvwxyz");
            data.setIntData(i * i * i);
            dataList.add(data);
        }

        // リストにサンプル用のデータを受け渡す
        ListAdapter adapter = new ListAdapter(this, dataList);
        setListAdapter(adapter);
    }
}

// データ格納用クラス
class Data {
    private long longData;       //日付用
    private String stringData;  //文言用
    private int intData;        //数値用

    public void setLongData(long tmp) {
        this.longData = tmp;
    }

    public long getLongData() {
        return longData;
    }

    public void setStringData(String tmp) {
        this.stringData = tmp;
    }

    public String getStringData() {
        return stringData;
    }

    public void setIntData(int tmp) {
        this.intData = tmp;
    }

    public long getIntData() {
        return intData;
    }
}

// リスト表示制御用クラス
class ListAdapter extends ArrayAdapter<Data> {
    private LayoutInflater inflater;
    // values/colors.xmlより設定値を取得するために利用。
    private Resources r;

    public ListAdapter(Context context, List<Data> objects) {
        super(context, 0, objects);
        inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        r = context.getResources();
    }

    @Override
    public View getView(final int position, View view, ViewGroup parent) {
        // layout/raw.xmlを紐付ける
        if (view == null) {
            view = inflater.inflate(R.layout.raw, parent, false);
        }
        final Data data = this.getItem(position);
        TextView tvData1 = (TextView) view.findViewById(R.id.raw1);
        TextView tvData2 = (TextView) view.findViewById(R.id.raw2);
        TextView tvData3 = (TextView) view.findViewById(R.id.raw3);
        if (data != null) {
            //1列目は日付データとしてフォーマット変更の上、表示
            SimpleDateFormat ymd = new SimpleDateFormat("yy/MM/dd", Locale.JAPANESE);
            tvData1.setText(ymd.format(data.getLongData()));
            //2列目は文字列なのでそのまま表示
            tvData2.setText(data.getStringData());
            //3列目は数値データのため、3桁ごとにカンマを入れて表示
            tvData3.setText(String.format("%1$,3d", data.getIntData()));
        }
        //偶数行の場合の背景色を設定
        if (position % 2 == 0) {
            tvData1.setBackgroundColor(r.getColor(R.color.data1));
            tvData2.setBackgroundColor(r.getColor(R.color.data1));
            tvData3.setBackgroundColor(r.getColor(R.color.data1));
        }
        //奇数行の場合の背景色を設定
        else {
            tvData1.setBackgroundColor(r.getColor(R.color.data2));
            tvData2.setBackgroundColor(r.getColor(R.color.data2));
            tvData3.setBackgroundColor(r.getColor(R.color.data2));
        }
        return view;
    }
}

最後に

上記一式含まれるファイルを下記よりダウンロードできます。
multipleRowListSample.zip

もう少しスマートに実装出来れば良かったのですが、こんなのでも参考になれば幸いです。

サブコンテンツ

このページの先頭へ