2013年9月6日金曜日

DialogFragmentでのカスタムダイアログ実装方法

DialogFragmentによるカスタムダイアログ実装方法について、下記の点をまとめておく。お題としてパズドラ風のダイアログを実装してみる。
  • 基本
  • コンテンツ部分
  • スタイル
完成形

基本

DialogFragmentを継承したpublicクラスを作成する。注意点は下記の通り。

  • ファクトリーメソッド(下記例ではnewInstance())を用意する。
  • コンストラクタのオーバーロードを作らない、使わない
public class MyDialogFragment extends DialogFragment
{
  /**
   * ファクトリーメソッド
   */
  public static MyDialogFragment newInstance(String param)
  {
    MyDialogFragment instance = new MyDialogFragment();

    // ダイアログに渡すパラメータはBundleにまとめる
    Bundle arguments = new Bundle();
    arguments.putString("parameter", param);

    instance.setArguments(arguments);
    return instance;
  }
}

コンテンツ部分(お手軽パターン)

コンテンツ部分の実装は、onCreateDialog()やonCreateView()をオーバーライドして行う。 お手軽パターンでは、onCreateDialog()のみをオーバーライドして、必要な機能を有するDialogインスタンスを生成する。

/**
 * ダイアログコンテナを生成する。
 */
@Override
public Dialog onCreateDialog(Bundle b)
{
  // ダイアログのコンテンツ部分
  LayoutInflater i
    = (LayoutInflater) getActivity()
        .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
  View content = i.inflate(R.layout.mydialog_content, null);

  AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());

  // タイトル
  builder.setTitle("My Custom Dialog");
  // コンテンツ
  builder.setView(content);
  // OK
  builder.setPositiveButton(android.R.string.ok, null);

  Dialog dialog = builder.create();

  // ダイアログ外タップで消えないように設定
  dialog.setCanceledOnTouchOutside(false);

  return dialog;
}
  • 「お手軽」ではあるが、埋め込み部品として再利用(レイアウトの一部として配置)することができないデメリットがある。
  • HoneyComb以降は、ダイアログ外タップで閉じるのがデフォルトなので注意。

コンテンツ部分(お上品パターン)

お上品パターンでは、onCreateDialog()でダイアログコンテナを生成し、onCreateView()でコンテンツを生成する。OKボタンなどはレイアウトで、ダイアログの見栄えは後述のスタイルで賄う。

/**
 * ダイアログコンテナを生成する。
 */
@Override
public Dialog onCreateDialog(Bundle b)
{
  Dialog dialog = super.onCreateDialog(b);

  // タイトル
  dialog.setTitle("My Custom Dialog");
  // ダイアログ外タップで消えないように設定
  dialog.setCanceledOnTouchOutside(false);

  return dialog;
}

/**
 * UIを生成する。
 */
@Override
public View onCreateView(LayoutInflater i, ViewGroup c, Bundle b)
{
  View content = i.inflate(R.layout.mydialog_content, null);
  return content;
}
  • onCreateDialog()をオーバーライドしつつ、onCreateView()でnull以外を返すとAndroidRuntimeExceptionが発生する…と読めるような情報もあるが、AlertDialog使用方法の問題でありDialogFragment固有の問題では無い (AlertDialogを使わなければ良い)

コンテンツのレイアウト

ここでは、前述のお上品パターンで利用するレイアウト(ボタンも含める)を想定し、下図のようなレイアウトを作成する。RelativeLayoutを利用することで、ダイアログをお好みの位置に表示する。(下記例では画面下部に配置)

このレイアウトのコードは以下の通り。
<?xml version="1.0" encoding="utf-8"?>
<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_alignParentBottom="true"
    android:layout_margin="10dp"
    android:background="@drawable/dialog_bg"
    android:gravity="center_horizontal"
    android:orientation="vertical"
    android:paddingBottom="10dp"
    android:paddingLeft="10dp"
    android:paddingRight="10dp"
    android:paddingTop="20dp" >

    <TextView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_margin="10dp"
        android:background="#d595c2d7"
        android:minLines="7"
        android:text="以上の内容で送信してよろしいですか?"
        android:textColor="@android:color/black" />

    <Button
        android:id="@android:id/button1"
        android:layout_width="wrap_content"
        android:layout_height="35sp"
        android:background="@drawable/button_bg"
        android:minWidth="100dp"
        android:text="@android:string/ok"
        android:textColor="@android:color/white"
        android:textSize="22sp"
        android:textStyle="bold" />
    </LinearLayout>
</RelativeLayout>
さらに、ダイアログ背景をXMLで作成する。
drawable/dialog_bg_part1.xml(白枠だけの画像)
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" >
  <corners android:radius="10dp" />
  <stroke
    android:width="2dp"
    android:color="#ffffff" />
</shape>
drawable/dialog_bg_part2(黒枠にグラデーション塗りつぶしの画像)
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" >
  <corners android:radius="10dp" />
  <gradient
    android:angle="270"
    android:endColor="#f1114461"
    android:startColor="#f13c91ba" />
  <stroke
    android:width="1dp"
    android:color="#000000" />
</shape>
drawable/dialog_bg.xml(合体)
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
  <item android:drawable="@drawable/dialog_bg_part1"/>
  <item
    android:bottom="2dp"
    android:drawable="@drawable/dialog_bg_part2"
    android:left="2dp"
    android:right="2dp"
    android:top="2dp"/>
</layer-list>
ボタンの背景画像も同様に作成する
drawable/button_bg.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" >
  <corners android:radius="10dp" />
  <gradient
    android:angle="270"
    android:endColor="#222656"
    android:startColor="#6a75c7" />
  <stroke
    android:width="1px"
    android:color="#0c2446" />
</shape>

スタイル

最後に、背景(前項キャプチャの黒いところ)やタイトル(前項キャプチャのvalueと書いてあるとこ)を消して、ダイアログの表示アニメーションを制御するためにスタイルを定義および適用する。

values/styles.xml
Androidのダイアログテーマを拡張し、タイトル指定・背景・アニメーションの設定を上書きする。

<!-- タイトル無し、背景透明、アニメーション指定 -->
<style name="Theme.MyDialog" parent="@android:style/Theme.Dialog">
  <item name="android:windowNoTitle">true</item>
  <item name="android:windowBackground">@android:color/transparent</item>
  <item name="android:windowAnimationStyle">@style/Animation.MyDialog</item>
</style>

ウィンドウアニメーションスタイルは下記の通り定義する。表示する時のアニメーション(windowEnterAnimation)と消える時のアニメーション(windowExitAnimation)をそれぞれ指定。

<style name="Animation.MyDialog" parent="android:Animation.Dialog">
  <item name="android:windowEnterAnimation">@anim/options_panel_enter</item>
  <item name="android:windowExitAnimation">@anim/options_panel_exit</item>
</style>

アニメーション定義

anim/options_panel_enter.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
  android:interpolator="@android:anim/decelerate_interpolator" >
  <translate
    android:duration="@android:integer/config_shortAnimTime"
    android:fromYDelta="25%"
    android:toYDelta="0" />
  <alpha
    android:duration="@android:integer/config_shortAnimTime"
    android:fromAlpha="0.0"
    android:toAlpha="1.0" />
</set>

anim/options_panel_exit.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
  android:interpolator="@android:anim/accelerate_interpolator" >
  <translate
    android:duration="@android:integer/config_shortAnimTime"
    android:fromYDelta="0"
    android:toYDelta="50%" />
  <alpha
    android:duration="@android:integer/config_shortAnimTime"
    android:fromAlpha="1.0"
    android:toAlpha="0.0" />
</set>

DialogFragmentのonCreate()をオーバーライドして、スタイルを適用するコードを記述する。

/**
 * フラグメント生成コールバックメソッド
 */
@Override
public void onCreate(Bundle b)
{
  super.onCreate(b);

  setStyle(DialogFragment.STYLE_NORMAL, R.style.Theme_MyDialog);
}

おしまい


参考サイト

0 件のコメント:

コメントを投稿