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)などを
指定してください。

今回は、ここまでです。

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

ポチッとして頂けると、
次のコンテンツを作成する励みになります♪

ブログランキング・にほんブログ村へ

これからAndroidのアプリ開発やJavaでの開発を始めたい方へ

初めての Android のアプリ開発では、アプリケーション開発経験がない方や、
アプリケーション開発経験がある方でも、Java や C# などのオブジェクト指向言語が初めての方は、
書籍などによる独学ではアプリ開発できるようになるには、
かなりの時間がかかりますので、オンラインスクールでの習得をおススメします。

未経験者からシステムエンジニアを目指すのに最適かと、
まずは無料相談から♪

無料でJava言語を学べるのは、かなり魅力的♪
でも、応募資格は35歳以下です、、、
2022年12月から説明会が土曜日開催が追加されていますよ♪
説明会では、希望者に対してプログラミング体験もできるよ♪

未経験者からプログラマーを目指すのに最適かと、
まずは無料カウンセリングから♪

カリキュラムとサポートがしっかりしています、
お得なキャンペーンとかいろいろやっています♪

ゲーム系に強いスクール、UnityやUnrealEngineを習得するのに最適かと、
まずは無料オンライン相談から♪

コメント欄