Androidアプリ開発

表示できる行数を指定できるListView

この記事は約21分で読めます。
記事内に広告が含まれています。
スポンサーリンク

こんにちは、まっさん(@Tera_Msaki)です。

この記事は Android スマホ用のアプリ開発の中で、
今後の開発で再使用性が高いと思われるコーディングをまとめたものです。
Java での開発経験、XML 構文規則、Android のアプリ開発経験がある方を対象としています。
Android のアプリ開発でお役にたててれば、嬉しいです。
(これから Android のアプリ開発や Java での開発を始めたい方への案内は、
記事の最後で紹介します)

この記事のテーマ


 表示できる行数を指定できるListViewを実装する

ListView は、Andoroid アプリでリスト表示するベーシックライブラリです。
選択肢をリスト表示し、利用者に選択させる場合に使用します。

ListView は、使い勝手が良いライブラリですが、選択肢があらかじめ決まっているような場合は、標準的な使用方法(ListViewの高さを指定)で問題ありません。
しかし、選択肢の数が決まっていない場合、
ListView の表示できる行数を指定することができないため、件数が多いと表示領域が画面全体に及んだり、逆に少ない場合は、表示領域を余らすことになります。
今回、表示できる行数を指定できる
ListView の実装について、説明したいと思います。

表示領域が画面全体になってしまった ListView

ListViewを使用したダイアログ画面

利用者に選択肢を選択させる場合、選択肢をリスト表示するダイアログ画面がよく使用されます。
リスト表示する選択肢の数が可変で、表示できる行数を指定できる
ListView をもつ、ダイアログ画面を実装します。

public class CustomDialogList extends DialogFragment {
    private static final boolean        DEBUG = false;
    private static final String         TAG = CustomDialogList.class.getSimpleName();
    private CustomDialogItemListener    customDialogItemListener = null;
    private final int[]                 layout = {0,R.layout.dialog_list};
    private final int[]                 listLayout = {0, R.layout.item_dialog};
    private final int[]                 item = {0, R.id.item};
    private final int[]                 id = {0,R.id.dialog_item};

    @NonNull
    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        String title = requireArguments().getString("TITLE");
        int max = requireArguments().getString("MAX") != null ? Integer.parseInt(requireArguments().getString("MAX")) : 10;
        CustomDialogParcel[] customDialogParcels = (CustomDialogParcel[]) requireArguments().getParcelableArray("PAC");
        Dialog dialog = new Dialog(getActivity());
        dialog.getWindow().requestFeature(Window.FEATURE_NO_TITLE);
        dialog.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN);
        dialog.setContentView(layout[customDialogParcels.length]);
        DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
        int dialogWidth = (int) (displayMetrics.widthPixels * 0.98f);
        int dialogHeight = (int) (displayMetrics.heightPixels * 0.98f);
        WindowManager.LayoutParams layoutParams = dialog.getWindow().getAttributes();
        layoutParams.width = (Math.min(dialogWidth, dialogHeight));
        dialog.getWindow().setAttributes(layoutParams);
        dialog.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
        dialog.setCanceledOnTouchOutside(true);
        // タイトル
        TextView textView = dialog.findViewById(R.id.dialog_title);
        textView.setText(title);
        // リスト
        ListView listView = dialog.findViewById(id[customDialogParcels.length]);
        ArrayAdapter<String> arrayAdapter = new ArrayAdapter<>(getActivity(), listLayout[customDialogParcels.length], item[customDialogParcels.length]);
        for (String string : customDialogParcels[0].getValue().split("\t"))
            arrayAdapter.add(string);
        listView.setAdapter(arrayAdapter);
        listView.setOnItemClickListener((parent, view, position, id) -> {
            if (customDialogItemListener != null)
                customDialogItemListener.doItemClick(parent, view, position, id);
            dismiss();
        });
        dialog.findViewById(R.id.dialog_close).setOnClickListener(view -> {
            if (customDialogItemListener != null)
                customDialogItemListener.doCloseClick(view);
            dismiss();
        });
        setListViewHeight(listView, max);
        return dialog;
    }

    private void setListViewHeight(ListView listView, int max) {
        ListAdapter listAdapter = listView.getAdapter();
        if (listAdapter == null) { return; }
        int height = 0;
        int count = Math.min(listAdapter.getCount(), max);
        for (int i = 0 ; i < count ; i++) {
            View listItem = listAdapter.getView(i, null, listView);
            listItem.measure(0, 0);
            height += listItem.getMeasuredHeight();
        }
        float dp = getResources().getDisplayMetrics().density;
        ViewGroup.LayoutParams params = listView.getLayoutParams();
        ViewGroup.MarginLayoutParams  marginLayoutParams = (ViewGroup.MarginLayoutParams) params;
        params.height = height + (int)(marginLayoutParams.topMargin / dp) + (int)(marginLayoutParams.bottomMargin / dp)
                + (int)(listView.getPaddingTop() / dp) + (int)(listView.getPaddingBottom() / dp);
        listView.setLayoutParams(params);
    }

    public void setCustomDialogItemListener(CustomDialogItemListener customDialogItemListener) {
        this.customDialogItemListener = customDialogItemListener;
    }

    @Override
    public void onDetach() {
        super.onDetach();
        if (customDialogItemListener != null)
            customDialogItemListener = null;
    }
}

ダイアログ画面の呼び出す際の引数で、ダイアログのタイトル( TITLE )、表示できる行数( MAX )を指定しています。リスト表示する選択肢は、タブ区切りでバックし、パーセル( customDialogParcels )を使用して、引き渡しています。ListView の高さは、ListView にセットするアダプタから、リスト数が表示できる行数以下はリスト数、以上は表示できる行数として、ListView に表示するアイテムの高さ、上下マージンとパディングを考慮して、ListView の高さを再計算して、設定しています。

パーセル( customDialogParcels )

アクティビティ(またはフラグメント)からダイアログを呼び出す際、値や属性などを構造化したパーセルにして引き渡す実装がスマートです。
構造化する部分は、用途に応じてカスタムします。

public class CustomDialogParcel implements Parcelable {
    private String property;
    private int type;
    private int length;
    private String value;

    // コンストラクタ //
    public CustomDialogParcel() { }
    public CustomDialogParcel(String property, int type, int length, String value) {
        this.property = property;
        this.type = type;
        this.length = length;
        this.value = value;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(property);
        dest.writeInt(type);
        dest.writeInt(length);
        dest.writeString(value);
    }

    @Override
    public int describeContents() {
        return 0;
    }

    protected CustomDialogParcel(Parcel in) {
        property = in.readString();
        type = in.readInt();
        length = in.readInt();
        value = in.readString();
    }

    public static final Creator<CustomDialogParcel> CREATOR = new Creator<CustomDialogParcel>() {
        @Override
        public CustomDialogParcel createFromParcel(Parcel in) {
            return new CustomDialogParcel(in);
        }
        @Override
        public CustomDialogParcel[] newArray(int size) {
            return new CustomDialogParcel[size];
        }
    };

    public String getProperty() {
        return property;
    }
    public String getValue() {
        return value;
    }
    public int getType() {
        return type;
    }
    public int getLength() {
        return length;
    }
    public void setType(int type) {
        this.type = type;
    }
    public void setLength(int length) {
        this.length = length;
    }
    public void setValue(String value) {
        this.value = value;
    }
}

構造化する内容は、入力ダイアログでの用途を想定して、プロパティ名( String )、型( int )、長さ( int )、値( String )としています。

ダイアログの呼び出し

ダイアログのタイトル( TITLE )、表示できる行数( MAX )、リスト表示する選択肢は、タブ区切りでバックしたパーセル( customDialogParcels )を設定してダイアログを呼び出します。

    : 
            String itemLists = itemList[0];
            for (int i = 1; i < itemList.length; i++) {
                itemLists = String.format("%s\t%s", itemLists, itemList[i]);
            }
            Bundle bundle = new Bundle();
            bundle.putString("TITLE", title);
            bundle.putString("MAX", "10");
            customDialogParcels = new CustomDialogParcel[1];
            customDialogParcels[0] = new CustomDialogParcel("non", 0, 0, itemLists);
            bundle.putParcelableArray("PAC", customDialogParcels);
            CustomDialogList customDialogList = new CustomDialogList();
            customDialogList.setCustomDialogItemListener(new CustomDialogItemListener() {
                @Override
                public void doItemClick(AdapterView<?> parent, View view, int position, long id) {
                    // 選択した内容に応じた処理を記述する
                    :
                }

                @Override
                public void doCloseClick(View view) {
                    // 処理があれば、ここに記述する
                    :
                }
            });
            fragmentManager = getSupportFragmentManager();
            customDialogList.setArguments(bundle);
            customDialogList.show(fragmentManager, title);
    :

選択肢の数だけパーセルを用意する実装も可能ですが、タブ区切りでバックした1つのパーセルを引き渡す実装としています。インタフェースを使用して、アクティビティ(またはフラグメント)側で選択した内容に応じた処理を記述します。

表示できる行数を 10 として表示した ListView
10件以上ある場合は、スクロールします。
スクロール途中で止めているため、表示が切れているようにみえます
表示できる行数以下で表示した ListView

◎ダイアログのレイアウト

<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        :
        android:layout_alignParentTop="true"
        android:layout_marginTop="8dp"
        android:layout_marginLeft="8dp"
        android:layout_marginRight="8dp"
        android:paddingBottom="8dp"
        android:gravity="center_horizontal"
        android:orientation="vertical">
        <TextView
            android:id="@+id/dialog_title"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            :
            android:gravity="center"
            android:padding="4dp"/>
        <ListView
            android:id="@+id/dialog_item"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:layout_marginStart="16dp"
            android:layout_marginTop="16dp"
            android:layout_marginEnd="8dp"
            android:paddingBottom="16dp" />
    </LinearLayout>
    <Button
        android:id="@+id/dialog_close"
        android:layout_width="30dp"
        android:layout_height="30dp"
        :
        android:layout_alignParentEnd="true"
        android:layout_alignParentTop="true"/>
</RelativeLayout>

使用する環境に応じた背景( background )や文字サイズ( testSize )、色( textColor )などを指定してください。

◎ListViewに表示するアイテムのレイアウト

LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    tools:layout_editor_absoluteX="8dp">
    <TextView
        android:id="@+id/item"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        :
        android:padding="3dp"/>
</LinearLayout>

使用する環境に応じた背景( background )や文字サイズ( testSize )、色( textColor )などを指定してください。

今回は、ここまでです。

誤字脱字、意味不明でわかりづらい、
もっと詳しく知りたいなどのご意見は、
このページの最後にある
コメントか、
こちら
から、お願いいたします♪