/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.support.v4.app;
import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
import android.app.Activity;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
import android.support.annotation.IntDef;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.RestrictTo;
import android.support.annotation.StyleRes;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* Static library support version of the framework's {@link android.app.DialogFragment}.
* Used to write apps that run on platforms prior to Android 3.0. When running
* on Android 3.0 or above, this implementation is still used; it does not try
* to switch to the framework's implementation. See the framework SDK
* documentation for a class overview.
* PS首先fragment生命周期是:
* 创建时
* onAttach()
* onCreate()
* onCreateView()
* onActivityCreated()
* 可见时
* onStart()
* onResume()
* 后台时
* onPause()
* onStop()
* 销毁时
* onPause()
* onStop()
* onDestroyView()
* onDestroy()
* onDetach()
*/
public class DialogFragment extends Fragment
implements DialogInterface.OnCancelListener, DialogInterface.OnDismissListener {
/** @hide */
@RestrictTo(LIBRARY_GROUP)
@IntDef({STYLE_NORMAL, STYLE_NO_TITLE, STYLE_NO_FRAME, STYLE_NO_INPUT})
@Retention(RetentionPolicy.SOURCE)
private @interface DialogStyle {}
/**
* Style for {@link #setStyle(int, int)}: a basic,
* normal dialog.
* 翻译:基础的样式
*/
public static final int STYLE_NORMAL = 0;
/**
* Style for {@link #setStyle(int, int)}: don't include
* a title area.
* 翻译:无标题的样式
*/
public static final int STYLE_NO_TITLE = 1;
/**
* Style for {@link #setStyle(int, int)}: don't draw
* any frame at all; the view hierarchy returned by {@link #onCreateView}
* is entirely responsible for drawing the dialog.
* 翻译:不画任何边框;(由onCreateView返回的)View绘制dialog的样貌
*/
public static final int STYLE_NO_FRAME = 2;
/**
* Style for {@link #setStyle(int, int)}: like
* {@link #STYLE_NO_FRAME}, but also disables all input to the dialog.
* The user can not touch it, and its window will not receive input focus.
* 翻译:与STYLE_NO_FRAME样式相同,但是无法在dialog上进行输入。用户无法触摸以及窗口无法获取到焦点。
*/
public static final int STYLE_NO_INPUT = 3;
private static final String SAVED_DIALOG_STATE_TAG = "android:savedDialogState";
private static final String SAVED_STYLE = "android:style";
private static final String SAVED_THEME = "android:theme";
private static final String SAVED_CANCELABLE = "android:cancelable";
private static final String SAVED_SHOWS_DIALOG = "android:showsDialog";
private static final String SAVED_BACK_STACK_ID = "android:backStackId";
int mStyle = STYLE_NORMAL;
int mTheme = 0;
boolean mCancelable = true;
boolean mShowsDialog = true;
int mBackStackId = -1;
Dialog mDialog;
boolean mViewDestroyed;
boolean mDismissed;
boolean mShownByMe;
public DialogFragment() {
}
/**
* Call to customize(定制化) the basic appearance(外观) and behavior of the
* fragment's dialog. This can be used for some common dialog behaviors,
* taking care of selecting flags, theme, and other options for you. The
* same effect can be achieve(实现) by manually(手动) setting Dialog and Window
* attributes yourself. Calling this after the fragment's Dialog is
* created will have no effect.
* 翻译:用于定制基本的外观与行为。也能被用于一些共同dialog行为定制,尤其是flags,theme和其他选项。
* 相同的效果也能手动设置diglog和window的attributes实现。在dialog创建完毕之后调用此方法是无效的。
*
* @param style Selects a standard style: may be {@link #STYLE_NORMAL},
* {@link #STYLE_NO_TITLE}, {@link #STYLE_NO_FRAME}, or
* {@link #STYLE_NO_INPUT}.
* @param theme Optional custom theme. If 0, an appropriate(适当的) theme (based
* on the style) will be selected for you.
* 翻译:可选的自定义theme。如果传入0,则系统会自动选择合适的theme(基于传入的style样式)。
*/
public void setStyle(@DialogStyle int style, @StyleRes int theme) {
mStyle = style;
if (mStyle == STYLE_NO_FRAME || mStyle == STYLE_NO_INPUT) {
mTheme = android.R.style.Theme_Panel;
}
if (theme != 0) {
mTheme = theme;
}
}
/**
* Display the dialog, adding the fragment to the given FragmentManager. This
* is a convenience(方便) for explicitly(明确地) creating a transaction, adding the
* fragment to it with the given tag, and committing it. This does
* <em>not</em> add the transaction to the back stack. When the fragment
* is dismissed, a new transaction will be executed(执行) to remove it from
* the activity.
* 翻译:显示dialog,添加fragment到传入的FragmentManager。这样可以很方便的创建transaction,通过tag添加fragment再commit。
* 这个不能添加transaction用来回退,因为当fragment dismissed的时候,一个新的transaction将会执行remove fragment。
* @param manager The FragmentManager this fragment will be added to.
* @param tag The tag for this fragment, as per
* {@link FragmentTransaction#add(Fragment, String) FragmentTransaction.add}.
*/
public void show(FragmentManager manager, String tag) {
mDismissed = false;
mShownByMe = true;
FragmentTransaction ft = manager.beginTransaction();
ft.add(this, tag);
ft.commit();
}
/**
* Display the dialog, adding the fragment using an existing transaction
* and then committing the transaction.
* 翻译:显示dialog ,使用一个已存在的transaction添加fragment然后commit它。
* ps:此方法返回回退ID用于控制回退。
* @param transaction An existing transaction in which to add the fragment.
* @param tag The tag for this fragment, as per
* {@link FragmentTransaction#add(Fragment, String) FragmentTransaction.add}.
* @return Returns the identifier of the committed transaction, as per
* {@link FragmentTransaction#commit() FragmentTransaction.commit()}.
*/
public int show(FragmentTransaction transaction, String tag) {
mDismissed = false;
mShownByMe = true;
transaction.add(this, tag);
mViewDestroyed = false;
mBackStackId = transaction.commit();
return mBackStackId;
}
/**
* Dismiss the fragment and its dialog. If the fragment was added to the
* back stack, all back stack state up to and including this entry will
* be popped. Otherwise, a new transaction will be committed to remove
* the fragment.
* 翻译:dismiss fragment以及它的dialog。 如果fragment在添加的时候加入了回退,
* 将会进行回退操作。反之没有加入回退,一个新的transaction将会remove它。
*/
public void dismiss() {
dismissInternal(false);
}
/**
* Version of {@link #dismiss()} that uses
* {@link FragmentTransaction#commitAllowingStateLoss()
* FragmentTransaction.commitAllowingStateLoss()}. See linked
* documentation for further details.
*/
public void dismissAllowingStateLoss() {
dismissInternal(true);
}
void dismissInternal(boolean allowStateLoss) {
if (mDismissed) {
return;
}
mDismissed = true;
mShownByMe = false;
if (mDialog != null) {
mDialog.dismiss();
mDialog = null;
}
mViewDestroyed = true;
if (mBackStackId >= 0) {
getFragmentManager().popBackStack(mBackStackId,
FragmentManager.POP_BACK_STACK_INCLUSIVE);
mBackStackId = -1;
} else {
FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.remove(this);
if (allowStateLoss) {
ft.commitAllowingStateLoss();
} else {
ft.commit();
}
}
}
public Dialog getDialog() {
return mDialog;
}
@StyleRes
public int getTheme() {
return mTheme;
}
/**
* Control whether the shown Dialog is cancelable. Use this instead of
* directly calling {@link Dialog#setCancelable(boolean)
* Dialog.setCancelable(boolean)}, because DialogFragment needs to change
* its behavior based on this.
*
* 翻译:控制dialog是否能被取消,默认为真
* @param cancelable If true, the dialog is cancelable. The default
* is true.
*/
public void setCancelable(boolean cancelable) {
mCancelable = cancelable;
if (mDialog != null) mDialog.setCancelable(cancelable);
}
/**
* Return the current value of {@link #setCancelable(boolean)}.
*/
public boolean isCancelable() {
return mCancelable;
}
/**
* Controls whether this fragment should be shown in a dialog. If not
* set, no Dialog will be created in {@link #onActivityCreated(Bundle)},
* and the fragment's view hierarchy will thus not be added to it. This
* allows you to instead use it as a normal fragment (embedded(嵌入式) inside of
* its activity).
*
* <p>This is normally set for you based on whether the fragment is
* associated(相关的) with a container view ID passed to
* {@link FragmentTransaction#add(int, Fragment) FragmentTransaction.add(int, Fragment)}.
* If the fragment was added with a container, setShowsDialog will be
* initialized to false; otherwise, it will be true.
*
* 翻译:控制fragment是否应该在dialog中显示。如果不设置,将不会在onActivityCreated(Bundle)方法中创建dialog以及view将不会因此添加进入。
此方法允许你将它作为一个普通的fragment嵌入到activity中。
* 如果fragment添加进一个container,setShowsDialog方法将被设置为false,否则为true。
* @param showsDialog If true, the fragment will be displayed in a Dialog.
* If false, no Dialog will be created and the fragment's view hierarchy
* left undisturbed(不受干扰的).
*/
public void setShowsDialog(boolean showsDialog) {
mShowsDialog = showsDialog;
}
/**
* Return the current value of {@link #setShowsDialog(boolean)}.
*/
public boolean getShowsDialog() {
return mShowsDialog;
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
if (!mShownByMe) {
// If not explicitly(明确地) shown through(通过) our API, take this as an
// indication(指示) that the dialog is no longer dismissed(不再消失).
mDismissed = false;
}
}
@Override
public void onDetach() {
super.onDetach();
if (!mShownByMe && !mDismissed) {
// The fragment was not shown by a direct call here, it is not
// dismissed, and now it is being detached... well, okay, thou
// art now dismissed. Have fun.
mDismissed = true;
}
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mShowsDialog = mContainerId == 0;
if (savedInstanceState != null) {
mStyle = savedInstanceState.getInt(SAVED_STYLE, STYLE_NORMAL);
mTheme = savedInstanceState.getInt(SAVED_THEME, 0);
mCancelable = savedInstanceState.getBoolean(SAVED_CANCELABLE, true);
mShowsDialog = savedInstanceState.getBoolean(SAVED_SHOWS_DIALOG, mShowsDialog);
mBackStackId = savedInstanceState.getInt(SAVED_BACK_STACK_ID, -1);
}
}
@Override
public LayoutInflater onGetLayoutInflater(Bundle savedInstanceState) {
if (!mShowsDialog) {
return super.onGetLayoutInflater(savedInstanceState);
}
mDialog = onCreateDialog(savedInstanceState);
if (mDialog != null) {
setupDialog(mDialog, mStyle);
return (LayoutInflater) mDialog.getContext().getSystemService(
Context.LAYOUT_INFLATER_SERVICE);
}
return (LayoutInflater) mHost.getContext().getSystemService(
Context.LAYOUT_INFLATER_SERVICE);
}
/** @hide */
@RestrictTo(LIBRARY_GROUP)
public void setupDialog(Dialog dialog, int style) {
switch (style) {
case STYLE_NO_INPUT:
dialog.getWindow().addFlags(
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
// fall through...
case STYLE_NO_FRAME:
case STYLE_NO_TITLE:
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
}
}
/**
* Override to build your own custom Dialog container. This is typically(通常)
* used to show an AlertDialog instead of a generic(一般的) Dialog; when doing so,
* {@link #onCreateView(LayoutInflater, ViewGroup, Bundle)} does not need
* to be implemented since the AlertDialog takes care of its own content.
* 翻译:覆盖以建立你自己的自定义dialog容器。通常显示一个AlertDialog,取代一般的dialog;当你是这样做的时候,
onCreateView(LayoutInflater, ViewGroup, Bundle)方法不再需要覆盖了,因为AlertDialog会自己实现。
*
* <p>This method will be called after {@link #onCreate(Bundle)} and
* before {@link #onCreateView(LayoutInflater, ViewGroup, Bundle)}. The
* default implementation simply instantiates and returns a {@link Dialog}
* class.
*
* 翻译:这个方法的调用顺序在onCreate(Bundle) → onCreateView(LayoutInflater, ViewGroup, Bundle)的之间。
如果不覆盖将会返回一个默认的dialog。
* <p><em>Note: DialogFragment own the {@link Dialog#setOnCancelListener
* Dialog.setOnCancelListener} and {@link Dialog#setOnDismissListener
* Dialog.setOnDismissListener} callbacks. You must not set them yourself.</em>
* To find out about these events, override {@link #onCancel(DialogInterface)}
* and {@link #onDismiss(DialogInterface)}.</p>
* 翻译:dialogfragment自己实现了setOnCancelListener和setOnDismissListener方法,你不必自己设置,只需
覆盖onCancel(DialogInterface)和onDismiss(DialogInterface)方法
*
* @param savedInstanceState The last saved instance state of the Fragment,
* or null if this is a freshly created Fragment.
*
* @return Return a new Dialog instance to be displayed by the Fragment.
*/
@NonNull
public Dialog onCreateDialog(Bundle savedInstanceState) {
return new Dialog(getActivity(), getTheme());
}
@Override
public void onCancel(DialogInterface dialog) {
}
@Override
public void onDismiss(DialogInterface dialog) {
if (!mViewDestroyed) {
// Note: we need to use allowStateLoss, because the dialog
// dispatches this asynchronously(异步) so we can receive the call
// after the activity is paused. Worst case(最糟糕的情况), when the user comes
// back to the activity they see the dialog again.
dismissInternal(true);
}
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if (!mShowsDialog) {
return;
}
View view = getView();
if (view != null) {
if (view.getParent() != null) {
throw new IllegalStateException(
"DialogFragment can not be attached to a container view");
}
mDialog.setContentView(view);
}
final Activity activity = getActivity();
if (activity != null) {
mDialog.setOwnerActivity(activity);
}
mDialog.setCancelable(mCancelable);
mDialog.setOnCancelListener(this);
mDialog.setOnDismissListener(this);
if (savedInstanceState != null) {
Bundle dialogState = savedInstanceState.getBundle(SAVED_DIALOG_STATE_TAG);
if (dialogState != null) {
mDialog.onRestoreInstanceState(dialogState);
}
}
}
@Override
public void onStart() {
super.onStart();
if (mDialog != null) {
mViewDestroyed = false;
mDialog.show();
}
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
if (mDialog != null) {
Bundle dialogState = mDialog.onSaveInstanceState();
if (dialogState != null) {
outState.putBundle(SAVED_DIALOG_STATE_TAG, dialogState);
}
}
if (mStyle != STYLE_NORMAL) {
outState.putInt(SAVED_STYLE, mStyle);
}
if (mTheme != 0) {
outState.putInt(SAVED_THEME, mTheme);
}
if (!mCancelable) {
outState.putBoolean(SAVED_CANCELABLE, mCancelable);
}
if (!mShowsDialog) {
outState.putBoolean(SAVED_SHOWS_DIALOG, mShowsDialog);
}
if (mBackStackId != -1) {
outState.putInt(SAVED_BACK_STACK_ID, mBackStackId);
}
}
@Override
public void onStop() {
super.onStop();
if (mDialog != null) {
mDialog.hide();
}
}
/**
* Remove dialog.
*/
@Override
public void onDestroyView() {
super.onDestroyView();
if (mDialog != null) {
// Set removed here because this dismissal(解雇) is just to hide
// the dialog -- we don't want this to cause the fragment to
// actually(实际上) be removed.
mViewDestroyed = true;
mDialog.dismiss();
mDialog = null;
}
}
}
DialogFragment源码
最新推荐文章于 2022-04-04 22:24:45 发布