【Android開発】カスタマイズしたダイアログの実装方法

先日リリースした「こづかい帳」ではダイアログを多用していますが、ほとんどが独自にカスタマイズしたダイアログです。
今回はその中でも特に作りこみ度が高かった数値入力のダイアログを例に、カスタマイズしたダイアログの実装方法についてまとめました。

本対応の目的

カスタマイズしたダイアログを呼び出して、ダイアログ内で入力した値を元の画面へ戻す機能を実装しています。

画面イメージは下記の通り。

CustomizedDialog1
ボタンを押下
CustomizedDialog2
ダイアログ上で値を入力
CustomizedDialog3
入力された値が表示

drawableの準備

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_enabled="false"><shape>
        <solid android:color="@color/button3" />
    </shape></item>
    <item android:state_pressed="true"><shape>
        <solid android:color="@color/button2" />
    </shape></item>
    <item><shape>
        <solid android:color="@color/button1" />
    </shape></item>
</selector>

ボタンの背景色を定義しているファイルです。

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:background="@android:color/black"
    android:gravity="center"
    android:orientation="vertical">

    <TextView
        android:id="@+id/tv_output"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="@dimen/dp_l"
        android:background="@android:color/darker_gray"
        android:gravity="end"
        android:padding="@dimen/dp_l"
        android:textSize="@dimen/sp_m" />

    <Button
        android:id="@+id/bt_open"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="@dimen/dp_l"
        android:background="@drawable/button_background"
        android:gravity="center"
        android:padding="@dimen/dp_l"
        android:text="@string/open_dialog"
        android:textColor="@android:color/white"
        android:textSize="@dimen/sp_m" />
</LinearLayout>

メイン画面のlayoutファイルです。ダイアログを起動するボタンとダイアログから連携された値を表示するTextViewのみを用意しています。

<?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:layout_alignParentTop="true"
    android:background="@color/dialog_background"
    android:gravity="center_horizontal"
    android:orientation="vertical">

    <TextView
        android:id="@+id/tv_input"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="@dimen/dp_l"
        android:layout_weight="1"
        android:background="@android:color/darker_gray"
        android:gravity="end"
        android:padding="@dimen/dp_l"
        android:textSize="@dimen/sp_m" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginLeft="@dimen/dp_m"
        android:layout_marginRight="@dimen/dp_m"
        android:layout_marginTop="@dimen/dp_m"
        android:layout_weight="1"
        android:orientation="horizontal">

        <Button
            android:id="@+id/bt_no7"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_margin="@dimen/dp_s"
            android:layout_weight="1"
            android:background="@drawable/button_background"
            android:padding="@dimen/dp_l"
            android:text="@string/no7"
            android:textColor="@android:color/white"
            android:textSize="@dimen/sp_m" />

        <Button
            android:id="@+id/bt_no8"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_margin="@dimen/dp_s"
            android:layout_weight="1"
            android:background="@drawable/button_background"
            android:padding="@dimen/dp_l"
            android:text="@string/no8"
            android:textColor="@android:color/white"
            android:textSize="@dimen/sp_m" />

        <Button
            android:id="@+id/bt_no9"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_margin="@dimen/dp_s"
            android:layout_weight="1"
            android:background="@drawable/button_background"
            android:padding="@dimen/dp_l"
            android:text="@string/no9"
            android:textColor="@android:color/white"
            android:textSize="@dimen/sp_m" />
    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginLeft="@dimen/dp_m"
        android:layout_marginRight="@dimen/dp_m"
        android:layout_weight="1"
        android:orientation="horizontal">

        <Button
            android:id="@+id/bt_no4"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_margin="@dimen/dp_s"
            android:layout_weight="1"
            android:background="@drawable/button_background"
            android:padding="@dimen/dp_l"
            android:text="@string/no4"
            android:textColor="@android:color/white"
            android:textSize="@dimen/sp_m" />

        <Button
            android:id="@+id/bt_no5"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_margin="@dimen/dp_s"
            android:layout_weight="1"
            android:background="@drawable/button_background"
            android:padding="@dimen/dp_l"
            android:text="@string/no5"
            android:textColor="@android:color/white"
            android:textSize="@dimen/sp_m" />

        <Button
            android:id="@+id/bt_no6"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_margin="@dimen/dp_s"
            android:layout_weight="1"
            android:background="@drawable/button_background"
            android:padding="@dimen/dp_l"
            android:text="@string/no6"
            android:textColor="@android:color/white"
            android:textSize="@dimen/sp_m" />
    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginLeft="@dimen/dp_m"
        android:layout_marginRight="@dimen/dp_m"
        android:layout_weight="1"
        android:orientation="horizontal">

        <Button
            android:id="@+id/bt_no1"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_margin="@dimen/dp_s"
            android:layout_weight="1"
            android:background="@drawable/button_background"
            android:padding="@dimen/dp_l"
            android:text="@string/no1"
            android:textColor="@android:color/white"
            android:textSize="@dimen/sp_m" />

        <Button
            android:id="@+id/bt_no2"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_margin="@dimen/dp_s"
            android:layout_weight="1"
            android:background="@drawable/button_background"
            android:padding="@dimen/dp_l"
            android:text="@string/no2"
            android:textColor="@android:color/white"
            android:textSize="@dimen/sp_m" />

        <Button
            android:id="@+id/bt_no3"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_margin="@dimen/dp_s"
            android:layout_weight="1"
            android:background="@drawable/button_background"
            android:padding="@dimen/dp_l"
            android:text="@string/no3"
            android:textColor="@android:color/white"
            android:textSize="@dimen/sp_m" />
    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginBottom="@dimen/dp_m"
        android:layout_marginLeft="@dimen/dp_m"
        android:layout_marginRight="@dimen/dp_m"
        android:layout_weight="1"
        android:orientation="horizontal">

        <Button
            android:id="@+id/bt_no0"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_margin="@dimen/dp_s"
            android:layout_weight="1"
            android:background="@drawable/button_background"
            android:padding="@dimen/dp_l"
            android:text="@string/no0"
            android:textColor="@android:color/white"
            android:textSize="@dimen/sp_m" />

        <Button
            android:id="@+id/bt_clear"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_margin="@dimen/dp_s"
            android:layout_weight="2"
            android:background="@drawable/button_background"
            android:padding="@dimen/dp_l"
            android:text="@string/clear"
            android:textColor="@android:color/white"
            android:textSize="@dimen/sp_m" />
    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <Button
            android:id="@+id/bt_ok"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginRight="@dimen/dp_s"
            android:layout_weight="1"
            android:background="@drawable/button_background"
            android:enabled="false"
            android:padding="@dimen/dp_l"
            android:text="@string/ok"
            android:textColor="@android:color/white"
            android:textSize="@dimen/sp_l" />

        <Button
            android:id="@+id/bt_close"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:background="@drawable/button_background"
            android:padding="@dimen/dp_l"
            android:text="@string/close"
            android:textColor="@android:color/white"
            android:textSize="@dimen/sp_l" />
    </LinearLayout>
</LinearLayout>

ダイアログ用のlayoutファイルです。

valuesの準備

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <!-- ボタン通常時 -->
    <color name="button1">#404040</color>
    <!-- ボタン押下時 -->
    <color name="button2">#808080</color>
    <!-- ボタン選択不可 -->
    <color name="button3">#b0b0b0</color>
    <!-- dialog背景色 -->
    <color name="dialog_background">#202020</color>
</resources>

色を定義するファイルです。

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

文字サイズと余白のサイズを定義するファイルです。

<resources>
    <string name="app_name">Sample</string>
    <string name="open_dialog">ダイアログを開く</string>
    <string name="err_msg">1,000,000,000以上は入力できません。</string>
    <string name="ok">○</string>
    <string name="close">×</string>
    <string name="no0">0</string>
    <string name="no1">1</string>
    <string name="no2">2</string>
    <string name="no3">3</string>
    <string name="no4">4</string>
    <string name="no5">5</string>
    <string name="no6">6</string>
    <string name="no7">7</string>
    <string name="no8">8</string>
    <string name="no9">9</string>
    <string name="clear">C</string>
</resources>

文字列を定義するファイルです。

Java側の記載

package jp.co.skys.android.sample;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

public class MainActivity extends Activity {

    TextView tvOutput;
    Button btOpen;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.layout_main);
        tvOutput = (TextView) findViewById(R.id.tv_output);
        btOpen =  (Button) findViewById(R.id.bt_open);

        //ボタン押下時
        btOpen.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                // ダイアログの表示
                final CustomizedDialog dialog = CustomizedDialog.newInstance();
                dialog.setOnOkButtonClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        //ダイアログから値を取得して、output用のTextViewに表示
                        tvOutput.setText(String.format("%1$,3d", dialog.getInputValue()));
                        //ダイアログを消す
                        dialog.dismiss();
                    }
                });
                dialog.show(getFragmentManager(), "dialog_fragment");
                dialog.setCancelable(false);
            }
        });
    }
}

メイン画面用のJavaファイルです。

package jp.co.skys.android.sample;

import android.app.AlertDialog;
import android.app.Dialog;
import android.app.DialogFragment;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.view.LayoutInflater;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.TextView;

public class CustomizedDialog extends DialogFragment {
    private View.OnClickListener okButtonClickListener = null;
    Dialog dialog;
    Long mInput = 0L;
    TextView tvInput;
    Button[] btNo = new Button[10];    //0~9までのボタン
    Button btClear;
    Button btOk;
    Button btClose;

    public static CustomizedDialog newInstance() {
        CustomizedDialog fragment = new CustomizedDialog();
        return fragment;
    }

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        //XMLとの紐付け
        LayoutInflater inflater = getActivity().getLayoutInflater();
        View view = inflater.inflate(R.layout.layout_dialog, null, false);
        tvInput = (TextView) view.findViewById(R.id.tv_input);
        btNo[0] = (Button) view.findViewById(R.id.bt_no0);
        btNo[1] = (Button) view.findViewById(R.id.bt_no1);
        btNo[2] = (Button) view.findViewById(R.id.bt_no2);
        btNo[3] = (Button) view.findViewById(R.id.bt_no3);
        btNo[4] = (Button) view.findViewById(R.id.bt_no4);
        btNo[5] = (Button) view.findViewById(R.id.bt_no5);
        btNo[6] = (Button) view.findViewById(R.id.bt_no6);
        btNo[7] = (Button) view.findViewById(R.id.bt_no7);
        btNo[8] = (Button) view.findViewById(R.id.bt_no8);
        btNo[9] = (Button) view.findViewById(R.id.bt_no9);
        btClear = (Button) view.findViewById(R.id.bt_clear);
        btOk = (Button) view.findViewById(R.id.bt_ok);
        btClose = (Button) view.findViewById(R.id.bt_close);

        //ダイアログの作成
        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(view);
        dialog.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));

        //数字ボタン押下時の処理
        for (int i = 0; i < 10; i++) {
            btNo[i].setOnClickListener(new View.OnClickListener() {
                public void onClick(View v) {
                    //入力された数値を元に一時的な値を取得
                    Long tmp = Long.parseLong(String.valueOf(mInput) + ((Button) v).getText().toString());
                    //一時的な値が1,000,000,000以上の場合はアラートを表示
                    if (tmp >= 1000000000) {
                        new AlertDialog.Builder(getActivity())
                                .setMessage(getText(R.string.err_msg))
                                .setPositiveButton(getText(R.string.ok), null)
                                .show();
                    } else {
                        mInput = tmp;
                        tvInput.setText(String.format("%1$,3d", mInput));
                    }
                    //OKボタンの活性化(mValueが0以外の場合のみ活性)
                    if (mInput == 0) {
                        btOk.setEnabled(false);
                    } else {
                        btOk.setEnabled(true);
                    }
                }
            });
        }

        //クリアボタン押下時の処理
        btClear.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                mInput = 0L;
                tvInput.setText("");
                btOk.setEnabled(false);
            }
        });

        //クローズボタン押下時はダイアログを消す
        btClose.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                dialog.dismiss();
            }
        });

        //OKボタンのリスナー
        btOk.setOnClickListener(okButtonClickListener);

        return dialog;
    }

    // ダイアログの横幅、高さ、表示位置を設定
    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        WindowManager.LayoutParams lp = dialog.getWindow().getAttributes();
        DisplayMetrics metrics = getResources().getDisplayMetrics();
        lp.width = (int) (metrics.widthPixels * 0.8);//横幅を80%
        //lp.height = (int) (metrics.heightPixels * 0.8);//高さを80%
        //lp.x = 100; //表示位置を指定した分、右へ移動
        //lp.y = 200; //表示位置を指定した分、下へ移動
        dialog.getWindow().setAttributes(lp);
    }

    public void setOnOkButtonClickListener(View.OnClickListener listener) {
        this.okButtonClickListener = listener;
    }

    //ダイアログ内の値を返すメソッド
    public Long getInputValue() {
        return mInput;
    }
}

今回の肝となるダイアログ用のJavaファイルです。
詳細はいつものようにソース内のコメントを参照ください。

最後に

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

CustomizedDialogSample.zip

今回の例ではダイアログを呼び出す際に引数を渡していませんが、引数を使うことで更に複雑な連携も可能となります。良かったらお試しください。

サブコンテンツ

このページの先頭へ