DialogFragment.java revision b7a2e4772220c4b41df1260cedaf8912f4b07547
1/* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package android.app; 18 19import android.content.DialogInterface; 20import android.os.Bundle; 21import android.view.LayoutInflater; 22import android.view.View; 23import android.view.ViewGroup; 24import android.view.Window; 25import android.view.WindowManager; 26 27/** 28 * A fragment that displays a dialog window, floating on top of its 29 * activity's window. This fragment contains a Dialog object, which it 30 * displays as appropriate based on the fragment's state. Control of 31 * the dialog (deciding when to show, hide, dismiss it) should be done through 32 * the API here, not with direct calls on the dialog. 33 * 34 * <p>Implementations should override this class and implement 35 * {@link #onCreateView(LayoutInflater, ViewGroup, Bundle)} to supply the 36 * content of the dialog. Alternatively, they can override 37 * {@link #onCreateDialog(Bundle)} to create an entirely custom dialog, such 38 * as an AlertDialog, with its own content. 39 */ 40public class DialogFragment extends Fragment 41 implements DialogInterface.OnCancelListener, DialogInterface.OnDismissListener { 42 43 /** 44 * Style for {@link #DialogFragment(int, int)} constructor: a basic, 45 * normal dialog. 46 */ 47 public static final int STYLE_NORMAL = 0; 48 49 /** 50 * Style for {@link #DialogFragment(int, int)} constructor: don't include 51 * a title area. 52 */ 53 public static final int STYLE_NO_TITLE = 1; 54 55 /** 56 * Style for {@link #DialogFragment(int, int)} constructor: don't draw 57 * any frame at all; the view hierarchy returned by {@link #onCreateView} 58 * is entirely responsible for drawing the dialog. 59 */ 60 public static final int STYLE_NO_FRAME = 2; 61 62 /** 63 * Style for {@link #DialogFragment(int, int)} constructor: like 64 * {@link #STYLE_NO_FRAME}, but also disables all input to the dialog. 65 * The user can not touch it, and its window will not receive input focus. 66 */ 67 public static final int STYLE_NO_INPUT = 3; 68 69 private static final String SAVED_DIALOG_STATE_TAG = "android:savedDialogState"; 70 private static final String SAVED_STYLE = "android:style"; 71 private static final String SAVED_THEME = "android:theme"; 72 private static final String SAVED_CANCELABLE = "android:cancelable"; 73 private static final String SAVED_BACK_STACK_ID = "android:backStackId"; 74 75 int mStyle = STYLE_NORMAL; 76 int mTheme = 0; 77 boolean mCancelable = true; 78 int mBackStackId = -1; 79 80 Dialog mDialog; 81 boolean mDestroyed; 82 83 public DialogFragment() { 84 } 85 86 /** 87 * Call to customize the basic appearance and behavior of the 88 * fragment's dialog. This can be used for some common dialog behaviors, 89 * taking care of selecting flags, theme, and other options for you. The 90 * same effect can be achieve by manually setting Dialog and Window 91 * attributes yourself. Calling this after the fragment's Dialog is 92 * created will have no effect. 93 * 94 * @param style Selects a standard style: may be {@link #STYLE_NORMAL}, 95 * {@link #STYLE_NO_TITLE}, {@link #STYLE_NO_FRAME}, or 96 * {@link #STYLE_NO_INPUT}. 97 * @param theme Optional custom theme. If 0, an appropriate theme (based 98 * on the style) will be selected for you. 99 */ 100 public void setStyle(int style, int theme) { 101 mStyle = style; 102 if (mStyle == STYLE_NO_FRAME || mStyle == STYLE_NO_INPUT) { 103 mTheme = android.R.style.Theme_Dialog_NoFrame; 104 } 105 if (theme != 0) { 106 mTheme = theme; 107 } 108 } 109 110 /** 111 * Display the dialog, adding the fragment to the given activity. This 112 * is a convenience for explicitly creating a transaction, adding the 113 * fragment to it with the given tag, and committing it. This does 114 * <em>not</em> add the transaction to the back stack. When the fragment 115 * is dismissed, a new transaction will be executed to remove it from 116 * the activity. 117 * @param activity The activity this fragment will be added to. 118 * @param tag The tag for this fragment, as per 119 * {@link FragmentTransaction#add(Fragment, String) FragmentTransaction.add}. 120 */ 121 public void show(Activity activity, String tag) { 122 FragmentTransaction ft = activity.openFragmentTransaction(); 123 ft.add(this, tag); 124 ft.commit(); 125 } 126 127 /** 128 * Display the dialog, adding the fragment to the given activity using 129 * an existing transaction and then committing the transaction. 130 * @param activity The activity this fragment will be added to. 131 * @param transaction An existing transaction in which to add the fragment. 132 * @param tag The tag for this fragment, as per 133 * {@link FragmentTransaction#add(Fragment, String) FragmentTransaction.add}. 134 * @return Returns the identifier of the committed transaction, as per 135 * {@link FragmentTransaction#commit() FragmentTransaction.commit()}. 136 */ 137 public int show(Activity activity, FragmentTransaction transaction, String tag) { 138 transaction.add(this, tag); 139 mBackStackId = transaction.commit(); 140 return mBackStackId; 141 } 142 143 /** 144 * Dismiss the fragment and its dialog. If the fragment was added to the 145 * back stack, all back stack state up to and including this entry will 146 * be popped. Otherwise, a new transaction will be committed to remove 147 * the fragment. 148 */ 149 public void dismiss() { 150 if (mDialog != null) { 151 mDialog.dismiss(); 152 } 153 if (mBackStackId >= 0) { 154 getActivity().popBackStack(mBackStackId, Activity.POP_BACK_STACK_INCLUSIVE); 155 mBackStackId = -1; 156 } else { 157 FragmentTransaction ft = getActivity().openFragmentTransaction(); 158 ft.remove(this); 159 ft.commit(); 160 } 161 } 162 163 public Dialog getDialog() { 164 return mDialog; 165 } 166 167 public int getTheme() { 168 return mTheme; 169 } 170 171 public void setCancelable(boolean cancelable) { 172 mCancelable = cancelable; 173 if (mDialog != null) mDialog.setCancelable(cancelable); 174 } 175 176 public boolean getCancelable() { 177 return mCancelable; 178 } 179 180 @Override 181 public void onCreate(Bundle savedInstanceState) { 182 super.onCreate(savedInstanceState); 183 if (savedInstanceState != null) { 184 mStyle = savedInstanceState.getInt(SAVED_STYLE, mStyle); 185 mTheme = savedInstanceState.getInt(SAVED_THEME, mTheme); 186 mCancelable = savedInstanceState.getBoolean(SAVED_CANCELABLE, mCancelable); 187 mBackStackId = savedInstanceState.getInt(SAVED_BACK_STACK_ID, mBackStackId); 188 } 189 } 190 191 public Dialog onCreateDialog(Bundle savedInstanceState) { 192 return new Dialog(getActivity(), getTheme()); 193 } 194 195 public void onCancel(DialogInterface dialog) { 196 if (mBackStackId >= 0) { 197 // If this fragment is part of the back stack, then cancelling 198 // the dialog means popping off the back stack. 199 getActivity().popBackStack(mBackStackId, Activity.POP_BACK_STACK_INCLUSIVE); 200 mBackStackId = -1; 201 } 202 } 203 204 public void onDismiss(DialogInterface dialog) { 205 } 206 207 @Override 208 public void onActivityCreated(Bundle savedInstanceState) { 209 super.onActivityCreated(savedInstanceState); 210 mDialog = onCreateDialog(savedInstanceState); 211 mDestroyed = false; 212 switch (mStyle) { 213 case STYLE_NO_INPUT: 214 mDialog.getWindow().addFlags( 215 WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | 216 WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE); 217 // fall through... 218 case STYLE_NO_FRAME: 219 case STYLE_NO_TITLE: 220 mDialog.requestWindowFeature(Window.FEATURE_NO_TITLE); 221 } 222 View view = getView(); 223 if (view != null) { 224 if (view.getParent() != null) { 225 throw new IllegalStateException("DialogFragment can not be attached to a container view"); 226 } 227 mDialog.setContentView(view); 228 } 229 mDialog.setOwnerActivity(getActivity()); 230 mDialog.setCancelable(mCancelable); 231 mDialog.setOnCancelListener(this); 232 mDialog.setOnDismissListener(this); 233 if (savedInstanceState != null) { 234 Bundle dialogState = savedInstanceState.getBundle(SAVED_DIALOG_STATE_TAG); 235 if (dialogState != null) { 236 mDialog.onRestoreInstanceState(dialogState); 237 } 238 } 239 } 240 241 @Override 242 public void onStart() { 243 super.onStart(); 244 mDialog.show(); 245 } 246 247 @Override 248 public void onSaveInstanceState(Bundle outState) { 249 super.onSaveInstanceState(outState); 250 if (mDialog != null) { 251 Bundle dialogState = mDialog.onSaveInstanceState(); 252 if (dialogState != null) { 253 outState.putBundle(SAVED_DIALOG_STATE_TAG, dialogState); 254 } 255 } 256 outState.putInt(SAVED_STYLE, mStyle); 257 outState.putInt(SAVED_THEME, mTheme); 258 outState.putBoolean(SAVED_CANCELABLE, mCancelable); 259 outState.putInt(SAVED_BACK_STACK_ID, mBackStackId); 260 } 261 262 @Override 263 public void onStop() { 264 super.onStop(); 265 mDialog.hide(); 266 } 267 268 /** 269 * Detach from list view. 270 */ 271 @Override 272 public void onDestroyView() { 273 super.onDestroyView(); 274 mDestroyed = true; 275 mDialog.dismiss(); 276 mDialog = null; 277 } 278} 279