Android学習小Demo(15)カスタムAlertDialogの実装


Androidのアプリケーションでは、AlertDialogを利用して情報をポップアップしたり、ユーザーに選択させたり、ユーザーに情報を知らせたりすることが多いが、システムが持つ背景効果は、一般的に需要を満たすことができないため、AlertDialogをカスタマイズする需要がある場合が多い.実はAlertDialogだけでなく、前の文章で述べたEditTextなど、アプリケーションを改善するために多くのコントロールをカスタマイズする必要があります.
今日はAndroidでAlertDialogをカスタマイズする方法について説明しますが、まず次の効果図を見てください.
[キャンセル](Cancel)ボタンまたは[保存](Save)ボタンをクリックすると、ダイアログボックスが表示され、ユーザーに通知されます.このダイアログボックスはカスタムAlertDialogです.
1)最初のステップ:レイアウトをカスタマイズします.次のようにします.
<?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="@drawable/shape_alert_dialog"
    android:minWidth="280dp"
    android:orientation="vertical" >

    <TextView
        android:id="@+id/tvAlertDialogTitle"
        android:textSize="20sp"
        android:textStyle="bold"
        android:layout_width="wrap_content"
        android:textColor="#FFFFFF"
        android:layout_height="50dp"
        android:layout_gravity="center"
        android:gravity="center" />

    <View
        android:id="@+id/vTitleLine"
        android:layout_width="match_parent"
        android:layout_height="1dp"
        android:background="@drawable/line_divider" >
    </View>

    <TextView
        android:id="@+id/tvAlertDialogMessage"
        android:layout_width="match_parent"
        android:textSize="16sp"
        android:layout_height="50dp"        
        android:visibility="gone" />

    <View
        android:id="@+id/vMessageLine"
        android:layout_width="match_parent"
        android:layout_height="1dp"
        android:background="@drawable/line_divider"
        android:visibility="gone" >
    </View>

    <RelativeLayout
        android:id="@+id/rlAlertDialogButtons"
        android:layout_width="270dp"
        android:layout_marginBottom="10dp"
        android:layout_height="50dp"
        android:layout_gravity="center" >

        <Button
            android:id="@+id/btnAlertDialogNegative"
            android:layout_width="120dp"
            android:layout_height="match_parent"
            android:layout_alignParentLeft="true"
            android:layout_alignParentTop="true"
            android:layout_margin="5dp"
            android:textColor="#FFFFFF"
            android:background="@drawable/shape_alert_button"
            android:gravity="center" />

        <Button
            android:id="@+id/btnAlertDialogPositive"
            android:layout_width="120dp"
            android:layout_height="match_parent"
            android:layout_alignParentRight="true"
            android:layout_alignParentTop="true"
            android:layout_margin="5dp"
            android:textColor="#FFFFFF"
            android:background="@drawable/shape_alert_button"
            android:gravity="center" />
    </RelativeLayout>

</LinearLayout>

上のレイアウトでは、タイトル(Title)とメッセージ(Message)を示す2つのTextViewが定義されており、2つの区切り線も含まれており、一番下には2つのボタンがあり、具体的なスタイルがどうなっているかは、皆さんが発揮しています.
2)2つ目のステップでは、CustomAlertDialogクラスを作成し、Dialogクラスを継承します.Androidの元のコードスタイルを継続するために、元のAlertDialogクラスを真似てbuilderクラスを作成することができます.具体的なコードは以下の通りです.
2.1)まずコンストラクション関数です.
public class CustomAlertDialog extends Dialog {
	

	public CustomAlertDialog(Context context) {
		super(context);
	}

	public CustomAlertDialog(Context context, int themeId) {
		super(context, themeId);
	}
ここでは、システムの元のDialogの属性を利用するため、システムのDialogトピックを継承し、stylesで修正するthemeIDである構造関数の1つを定義する.xmlでは、次のように定義されています.
    <!-- Custom Alert Dialog -->
    <style name="CustomAlertDialog" parent="android:style/Theme.Dialog">
        <item name="android:windowBackground">@android:color/transparent</item>
        <item name="android:windowNoTitle">true</item>
        <item name="android:windowIsFloating">true</item>
    </style>
このstyleでは、
a)DialogのwindowBackgroundプロパティが透明に設定され、システムのデフォルトダイアログの背景が削除されます.
b)システムダイアログのタイトルを削除し、
c)ダイアログのフローティング属性をtrueに設定し、このダイアログがView上にフローティングできるようにする.
2.2)Builderクラスを作成するには、AlertDialogクラスの方法に従って、私たちが必要とする方法を設定します.例えば、上のdemoでは、「確定」「キャンセル」ボタン、「タイトル」と「情報」のいくつかの項目だけが必要です.そのため、他の方法を実現する必要はありません.具体的なコードは以下の通りです.
public static class Builder {
		
		private Context mContext;
		private String mTitle, mMessage;
		private String mPositiveButtonText, mNegativeButtonText;

		private OnClickListener mPositiveButtonClickListener,
				mNegativeButtonClickListener;

		public Builder(Context context) {
			mContext = context;
		}

		public Builder setTitle(int resId) {
			mTitle = (String) mContext.getText(resId);
			return this;
		}

		public Builder setTitle(String title) {
			mTitle = title;
			return this;
		}

		public Builder setMessage(int resId) {
			mMessage = (String) mContext.getText(resId);
			return this;
		}

		public Builder setMessage(String message) {
			mMessage = message;
			return this;
		}

		public Builder setPositiveButton(int positiveButtonTextId,
				OnClickListener listener) {
			mPositiveButtonText = (String) mContext
					.getText(positiveButtonTextId);
			mPositiveButtonClickListener = listener;
			return this;
		}

		public Builder setPositiveButton(String positiveButtonText,
				OnClickListener listener) {
			mPositiveButtonText = positiveButtonText;
			mPositiveButtonClickListener = listener;
			return this;
		}

		public Builder setNegativeButton(int negativeButtonTextId,
				OnClickListener listener) {
			mNegativeButtonText = (String) mContext
					.getText(negativeButtonTextId);
			mNegativeButtonClickListener = listener;
			return this;
		}

		public Builder setNegativeButton(String negativeButtonText,
				OnClickListener listener) {
			mNegativeButtonText = negativeButtonText;
			mNegativeButtonClickListener = listener;
			return this;
		}

		public CustomAlertDialog create() {
			LayoutInflater inflater = LayoutInflater.from(mContext);
			View view = inflater.inflate(R.layout.custom_alert_dialog, null);
			final CustomAlertDialog customAlertDialog = new CustomAlertDialog(
					mContext, R.style.CustomAlertDialog);
			customAlertDialog.addContentView(view, new ViewGroup.LayoutParams(
					ViewGroup.LayoutParams.MATCH_PARENT,
					ViewGroup.LayoutParams.WRAP_CONTENT));
			TextView tvAlertTitle = (TextView) view
					.findViewById(R.id.tvAlertDialogTitle);
			tvAlertTitle.setText(mTitle);

			if (!TextUtils.isEmpty(mMessage)) {
				TextView tvAlertDialogMessage = (TextView) view
						.findViewById(R.id.tvAlertDialogMessage);
				tvAlertDialogMessage.setText(mMessage);				
				View vMessageLine = (View)view.findViewById(R.id.vMessageLine);
				vMessageLine.setVisibility(View.VISIBLE);
			}

			Button btnPositive = (Button) view
					.findViewById(R.id.btnAlertDialogPositive);
			if (!TextUtils.isEmpty(mPositiveButtonText)) {
				btnPositive.setText(mPositiveButtonText);
				if (mPositiveButtonClickListener != null) {
					btnPositive.setOnClickListener(new View.OnClickListener() {
						@Override
						public void onClick(View v) {
							mPositiveButtonClickListener.onClick(
									customAlertDialog, BUTTON_POSITIVE);
						}
					});
				}
			} else {
				btnPositive.setVisibility(View.GONE);
			}
			Button btnNegative = (Button) view
					.findViewById(R.id.btnAlertDialogNegative);
			if (!TextUtils.isEmpty(mNegativeButtonText)) {
				btnNegative.setText(mNegativeButtonText);
				if (mNegativeButtonClickListener != null) {
					btnNegative.setOnClickListener(new View.OnClickListener() {
						@Override
						public void onClick(View v) {
							mNegativeButtonClickListener.onClick(
									customAlertDialog, BUTTON_NEGATIVE);
						}
					});
				}else{
					btnNegative.setOnClickListener(new View.OnClickListener() {
						@Override
						public void onClick(View v) {
							customAlertDialog.dismiss();
						}
					});
				}
			} else {
				btnNegative.setVisibility(View.GONE);
			}
			if (View.VISIBLE == btnPositive.getVisibility()
					&& View.GONE == btnNegative.getVisibility()) {
				RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) btnPositive
						.getLayoutParams();				
				layoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT;	
				btnPositive.setLayoutParams(layoutParams);
			}
			return customAlertDialog;
		}

		public CustomAlertDialog show() {
			CustomAlertDialog dialog = create();
			dialog.show();
			return dialog;
		}

上にはsettittle,setMessage,setPositiveButton,setNegativeButtonの2つの実装が見られ,そのうちの1つは受信リソースid,1つは直接受信文字列である.
次はcreateメソッドです.ここでは、
a)LayoutInflaterを利用して、前に定義したレイアウトファイルを解析し、View変数に設定します.
b)同時に、先に定義したthemeId付きコンストラクション関数を使用して、CustomAlertDialogオブジェクトを作成し、ダイアログボックスにレイアウトをロードします.
c)Title,Message,PositiveButton,NegativeButtonなどの対応するコントロールの内容を初期化するには,もちろんパラメータ値による判断が必要であり,このダイアログを作成する際に付与値があれば,Titleは必須であり,Messageは必須ではなく,NegativeButtonも必須ではなく,そのため、実際の論理に基づいて表現するかどうかを判断する必要があります.
d)レイアウトを定義するときは、2つのボタンを定義しているので、それらは同じ大きさで左右に並んでいるので、1つのボタンしかない場合は、中央に置く必要があります(実はここでは、大きさを変えず、中央に置くことは実現していませんので、表示されているボタン全体を大きくして、中央に置くだけで、なぜだめなのかまだ分かりません).
e)最後にshowメソッドを定義し,ダイアログボックスを表示する.
f)Builderで2つのDialogInterfaceを定義した.OnClickListener、レイアウトのボタンをクリックすると、ボタンのViewに表示されます.OnClickListenerメソッドでDialogInterfaceを呼び出す.OnClickListenerのonClickメソッドですが、ActivityでAlertDialogを作成するときにこのインタフェースを実現するメソッドです.
2.3)ActivityでCustomAlertDialogを作成するのは、通常のAlertDialogメソッドを作成するのと同じです.なぜなら、私たちはそのメソッドに従って実現しているので、私たちが必要としないメソッドをいくつか削除しただけです.コードは以下の通りです.
		new CustomAlertDialog.Builder(context)
			.setTitle(title)
			.setNegativeButton(context.getString(R.string.dialog_no), null)
			.setPositiveButton(context.getString(R.string.dialog_yes), listener)
			.show();

ええ、ここまで、私たちのカスタムAlertDialogが実現しました.