DialogFragmentを自分なりに汎用的に使えるようにしてみた

## DialogFragmentをなるだけ簡単に使いたい

ダイアログを表示したいと思い、ササーッとぐぐってみると
`DialogFragment`なるものをつかってやるのがイマドキとのこと

使い方をみると、
`DialogFragment`を継承して`onCreateDialog()`でカスタマイズするっぽい

ただ、各所で紹介されているサンプルをそのまま鵜呑みにすると
イチケースにつきイチクラスのものを作らないとイケないような感じにとれる
ただ、タイトルが違うだけのdialogを表示したいだけなのに、
都度都度、継承クラスつくって〜〜とかって…無理っす(泣)

ということで、あれこれ調べながら、汎用的に使えるdialogクラスを自分なりに作ってみた

## ソース


```java:CommonDialogFragment.java
public class CommonDialogFragment extends DialogFragment {

	public interface CommonDialogInterface {
		public interface onClickListener {
			void onDialogButtonClick(String tag, Dialog dialog, int which);
		}
		
		public interface onShowListener {
			void onDialogShow(String tag, Dialog dialog);
		}
		
		public interface onItemClickListener {
			void onDialogItemClick(String tag, Dialog dialog, String title, int which);
		}
	}
	
	public static final String FIELD_LAYOUT = "layout";
	public static final String FIELD_TITLE = "title";
	public static final String FIELD_MESSAGE = "message";
	public static final String FIELD_LIST_ITEMS = "list_items";
	public static final String FIELD_LIST_ITEMS_STRING = "list_items_string";
	public static final String FIELD_LABEL_POSITIVE = "label_positive";
	public static final String FIELD_LABEL_NEGATIVE = "label_negative";
	public static final String FIELD_LABEL_NEUTRAL = "label_neutral";
		
	private CommonDialogInterface.onShowListener mListenerShow;
	private CommonDialogInterface.onClickListener mListenerOnClick;
	private CommonDialogInterface.onItemClickListener mListenerItemClick;
	private AlertDialog mAlertDialog;
	
	@Override
	public Dialog onCreateDialog(Bundle savedInstanceState) {
		
		Bundle args = getArguments();
		
        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
        
        // listener check
        if (getTargetFragment() != null) {
        	setListener(getTargetFragment());
        } else if (getActivity() != null) {
        	setListener(getActivity());
        }
        
        // dialog title
        if (args.containsKey(FIELD_TITLE)) {
        	builder.setTitle(args.getInt(FIELD_TITLE));
        }
        
        // dialog message
        if (args.containsKey(FIELD_MESSAGE)) {
        	builder.setMessage(args.getInt(FIELD_MESSAGE));
        }
        
        // dialog customize content view
        if (args.containsKey(FIELD_LAYOUT)) {
        	LayoutInflater inflater = (LayoutInflater)getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            View content = inflater.inflate(args.getInt(FIELD_LAYOUT), null);
        	builder.setView(content);
        }
        
        // dialog string list
        final List<String> items = new ArrayList<String>();
        if (args.containsKey(FIELD_LIST_ITEMS)) {
        	final int[] listItems = args.getIntArray(FIELD_LIST_ITEMS);
        	for (int i = 0; i < listItems.length; i++) {
        		items.add(getString(listItems[i]));
        	}
        }
        if (args.containsKey(FIELD_LIST_ITEMS_STRING)) {
        	final String[] listItems = args.getStringArray(FIELD_LIST_ITEMS_STRING);
        	for (int i = 0; i < listItems.length; i++) {
        		items.add(listItems[i]);
        	}
        }
        if (items.size() > 0) {
        	builder.setItems(items.toArray(new String[items.size()]), new DialogInterface.OnClickListener() {
				
				@Override
				public void onClick(DialogInterface dialog, int which) {
					if ( mListenerItemClick != null) {
						mListenerItemClick.onDialogItemClick(getTag(), mAlertDialog, items.get(which), which);
					}
				}
				
			});
        }
        
        // positive button title and click listener
        if (args.containsKey(FIELD_LABEL_POSITIVE)) {
        	builder.setPositiveButton(args.getInt(FIELD_LABEL_POSITIVE), new DialogInterface.OnClickListener() {
				
				@Override
				public void onClick(DialogInterface dialog, int which) {
					if (mListenerOnClick != null) {
						mListenerOnClick.onDialogButtonClick(getTag(), mAlertDialog, which);
					}
					
				}
			});
        }
        
        // negative button title and click listener
        if (args.containsKey(FIELD_LABEL_NEGATIVE)) {
        	builder.setNegativeButton(args.getInt(FIELD_LABEL_NEGATIVE), new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int which) {
                	if (mListenerOnClick != null) {
                		mListenerOnClick.onDialogButtonClick(getTag(), mAlertDialog, which);
                	}
                }
            });
        }
        
        // neutral button title and click listener
        if (args.containsKey(FIELD_LABEL_NEUTRAL)) {
        	builder.setNeutralButton(args.getInt(FIELD_LABEL_NEUTRAL), new DialogInterface.OnClickListener() {
				@Override
				public void onClick(DialogInterface dialog, int which) {
					if (mListenerOnClick != null) {
                		mListenerOnClick.onDialogButtonClick(getTag(), mAlertDialog, which);
                	}
				}
			});
        }
        
        // make dialog
        mAlertDialog = builder.create();
        
        // show listener
        if (mListenerShow != null) {
        	mAlertDialog.setOnShowListener(new DialogInterface.OnShowListener() {
				
				@Override
				public void onShow(DialogInterface dialog) {
					mListenerShow.onDialogShow(getTag(), mAlertDialog);	
				}
			});
        }
        
        return mAlertDialog;
    }
	
	private void setListener(Object target) {
		
		// on click listener
		if (target instanceof CommonDialogInterface.onClickListener) {
			mListenerOnClick = (CommonDialogInterface.onClickListener) target;
		}
		
		// on item click listener
		if (target instanceof CommonDialogInterface.onItemClickListener) {
			mListenerItemClick = (CommonDialogInterface.onItemClickListener) target;
		}
		
		// on show listener
		if (target instanceof CommonDialogInterface.onShowListener) {
			mListenerShow = (CommonDialogInterface.onShowListener) target;
		}
		
	}
	
}
```

### なにしてるのかっていうと

titleとかmessageとか`AlertDialog.Builder`で設定可能なものを
`Bundle`つかってうけとって、それを元にdialogを作るっていう感じ。

それだけだと、ボタンとかリストのクリック判定ができないので
独自のinterfaceを作るから、呼び出しもとではそれを実装してね…と

それだけです

なので、設定可能なものを全部網羅しているわけじゃないので、
足りないものがアレば、他を真似して追加するだけで拡張は可能なはず

## 使い方

### 表示編

```java:MainActivity.java

// title & message
private void openDialogMessageType() {
    Bundle args = new Bundle();
    args.putInt(CommonDialogFragment.FIELD_TITLE, R.string.app_name);
    args.putInt(CommonDialogFragment.FIELD_MESSAGE, R.string.hello_world);
    args.putInt(CommonDialogFragment.FIELD_LABEL_POSITIVE, android.R.string.ok);
    CommonDialogFragment dialogFragment = new CommonDialogFragment();
    dialogFragment.setArguments(args);
    dialogFragment.show(getSupportFragmentManager(), "dialog1");
}

// original view
private void openDialogOriginalView() {
    Bundle args = new Bundle();
    args.putInt(CommonDialogFragment.FIELD_TITLE, R.string.app_name);
    // 自分で定義したレイアウト
    args.putInt(CommonDialogFragment.FIELD_LAYOUT, R.layout.dialog_prof);
    args.putInt(CommonDialogFragment.FIELD_LABEL_POSITIVE, android.R.string.ok);
    args.putInt(CommonDialogFragment.FIELD_LABEL_NEGATIVE, android.R.string.cancel);
    CommonDialogFragment dialogFragment = new CommonDialogFragment();
    dialogFragment.setArguments(args);
    dialogFragment.show(getSupportFragmentManager(), "dialog2");
}

// list items
private void openDialogListItems() {
    Bundle args = new Bundle();
    args.putInt(CommonDialogFragment.FIELD_TITLE, R.string.app_name);
    // 定義されてる文字なら
    args.putIntArray(CommonDialogFragment.FIELD_LIST_ITEMS, new int[] {R.string.item1, R.string.item2});
    // ソースで動的に文字列をつくるなら
    args.putStringArray(CommonDialogFragment.FIELD_LIST_ITEMS_STRING, new String[] {"item a", "item b"});
    args.putInt(CommonDialogFragment.FIELD_LABEL_NEGATIVE, android.R.string.cancel);
    args.putInt(CommonDialogFragment.FIELD_LABEL_NEUTRAL, android.R.string.selectAll);

    CommonDialogFragment dialogFragment = new CommonDialogFragment();
    dialogFragment.setArguments(args);
    dialogFragment.show(getSupportFragmentManager(), "dialog3");
}
```

### 基本的には

`Bundle`に表示したいフィールドを設定して、`setArguments()`で渡してあげて、表示するだけ

### 結果

**dialog1**
![device-2014-04-25-022447.png](https://qiita-image-store.s3.amazonaws.com/0/33521/0fc3c93c-ab49-c10a-e132-d44a477843f1.png)

**dialog2**
![device-2014-04-25-022641.png](https://qiita-image-store.s3.amazonaws.com/0/33521/c8a92f64-60c4-d894-ea7f-2a211af855da.png)

**dialog3**
![device-2014-04-25-022823.png](https://qiita-image-store.s3.amazonaws.com/0/33521/9b3812ee-ea24-669d-5a4f-76240c298f92.png)

### listener設定編

このままだと表示はできても、
ボタンをおした時とか、リストアイテムをおした時とかで
なにもアクションおこせないので、listenerを設定する
(表示するだけでいい。特になにか押されたって、アクションをこす必要なければ不要)

いまのところ、設定できるlistenerは

* onClickListener
* onShowListener
* onItemClickListener

で、それぞれ

* ボタン(positive / negative / neutral)が押下されたとき
* dialogが表示されたとき
* リストアイテムがクリックされたとき

に対応
実装にはinterfaceを実装するだけ

```java:MainActivity.java
public class MainActivity extends ActionBarActivity 
    implements 
    CommonDialogInterface.onClickListener, 
    CommonDialogInterface.onItemClickListener,
    CommonDialogInterface.onShowListener {

    /*
     * 同一activityで複数のdialogを表示するときは
     * tagで判断して処理を振り分けるかんじで
     */

    @Override
    public void onDialogButtonClick(String tag, Dialog dialog, int which) {
        if ("dialog2".equals(tag)) {
            // dialog 2のクリック
            if (DialogInterface.BUTTON_POSITIVE == which) {
                // ok ボタンがおされた
            } else if (DialogInterface.BUTTON_NEGATIVE == which) {
                // cancel ボタンがおされた
            }
        }
    }

    @Override
    public void onDialogItemClick(String tag, Dialog dialog, String title, int which) {
        // title には押されたリストアイテムのもの
        // which には押されたリストアイテムのindex
        if ( ! "dialog3".equals(tag)) {
            return;
        }
        if (getString(R.string.item1).equals(title)) {
            // item 1 がおされた
        } else if ("item a".equals(title)) {
            // item a がおされた
        } else if (which == 3) {
            // 4番目 item b がおされた
        }
    }

    @Override
    public void onDialogShow(String tag, Dialog dialog) {
        // dialogが表示されたら
        // EditTextとかで初期値を動的にやりたい場合とかに使ってる
        if ( ! "dialog2".equals(tag)) {
            return;
        }
        EditText txt = (EditText) dialog.findViewById(R.id.name);
        txt.setText("hogehoge");
    }

}
```

**初期値いれたばあい**
![device-2014-04-25-024955.png](https://qiita-image-store.s3.amazonaws.com/0/33521/d7ec37b3-7694-ec2b-b8ca-2ad263d82cd7.png)

## おわり

コレをベースに、足りないものは`Bundle`の設定フィールドをふやしたり
`listener`をふやしたりで、ある程度は網羅できそう。

ちょっとここまでやるの、大変だったけど、
これで今後は楽にできそう

Bundleを保持しておけば、同じダイアログを別々のタイミングでサクッと呼べるのもメリットですかね？

androidアプリ開発の経験は、他言語にもまして初心者なので
なにかあれば、ご指摘頂けるとと嬉しいです。

### 参考

[ux_design_tokyo](http://qiita.com/ux_design_tokyo)さんの[[Android] DialogFragmentを使ってダイアログを表示する-Qiita](http://qiita.com/ux_design_tokyo/items/61ca074566d1570b37d3)