在Android中,建立對話框有兩種,一種是Dialog,另一種則是今天的主題,官方推薦的DialogFragmet,關於Dialog的使用就不贅述了,今天主要介紹DialogFragment的使用以及一些須要注意的事項,主要有如下幾點。
DialogFragmeng相比Dialog的優點。java
DialogFrargment的使用。android
DialogFragment簡單源碼分析。app
DialogFragment須要注意的一些小細節。ide
方式一:重寫DialogFragment的onCreateDialog方法源碼分析
方式二:重寫DialogFragment的onCreateView方法佈局
1.方式一具體使用學習
@NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
Logger.d(TAG, "onCreateDialog");
AppCompatDialog appCompatDialog = new AppCompatDialog(requireActivity());
TextView textView = new TextView(requireActivity());
textView.setText("經過onCreateDialog使用DialogFragment");
textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 28);
appCompatDialog.setContentView(textView);
return appCompatDialog;
}
複製代碼
private void showOnCreateDialogFragment(){
DialogFragment dialogFragment = new DialogFragment();
dialogFragment.show(getSupportFragmentManager(), "tag");
}
複製代碼
2.方式二具體使用動畫
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
Logger.d(TAG, "onCreateView");
TextView textView = new TextView(requireActivity());
textView.setText("經過onCreateView使用DialogFragment");
textView.setGravity(Gravity.CENTER);
textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 28);
return textView;
}
複製代碼
3.DialogFragment中對Dialog屬性的設置,咱們能夠在DialogFragment的onStar中來對Dialog的樣式進行設置,有一點須要注意的是,對於Dialog樣式的設置,必須在onCretaeDialog方法後面執行,否則得不到Dialog實例,咱們能夠打印一下DialogFragment從建立到show出來時執行的具體生命週期方法,具體內容以下:ui
2019-03-10 14:19:10.971 22626-22626/org.lym.sourcecodeparse D/DialogFragment: onAttach
2019-03-10 14:19:10.971 22626-22626/org.lym.sourcecodeparse D/DialogFragment: onCreate
2019-03-10 14:19:10.972 22626-22626/org.lym.sourcecodeparse D/DialogFragment: onCreateDialog
2019-03-10 14:19:10.994 22626-22626/org.lym.sourcecodeparse D/DialogFragment: onActivityCreated
2019-03-10 14:19:11.186 22626-22626/org.lym.sourcecodeparse D/DialogFragment: onStart
2019-03-10 14:19:11.186 22626-22626/org.lym.sourcecodeparse D/DialogFragment: onResume
複製代碼
@Override
public void onStart() {
super.onStart();
Dialog dialog = getDialog();
Window window = dialog.getWindow();
if (window == null) {
return;
}
WindowManager.LayoutParams attributes = window.getAttributes();
//設置Dialog窗口的高度
attributes.height = WindowManager.LayoutParams.MATCH_PARENT;
//設置Dialog窗口的寬度
attributes.width = WindowManager.LayoutParams.MATCH_PARENT;
//設置Dialog的居中方向
attributes.gravity = Gravity.CENTER;
//設置Dialog彈出時背景的透明度
attributes.dimAmount = 0.6f;
//設置Dialog水平方向的間距
attributes.horizontalMargin = 0f;
//設置Dialog垂直方向的間距
attributes.verticalMargin = 0f;
//設置Dialog顯示時X軸的座標,具體屏幕X軸的偏移量
attributes.x = 0;
//設置Dialog顯示時Y軸的座標,距離屏幕Y軸的偏移量
attributes.y = 0;
//設置Dialog的透明度
attributes.alpha = 0f;
//設置Dialog顯示和消失時的動畫
attributes.windowAnimations = 0;
window.setAttributes(attributes);
Logger.d(TAG, "onStart");
}
複製代碼
1.下面先貼一下DialogFragment的部分源碼,並進行簡要分析。this
public class DialogFragment extends Fragment implements OnCancelListener, OnDismissListener {
boolean mViewDestroyed;
boolean mDismissed;
boolean mShownByMe;
public DialogFragment() {
}
//標準的顯示Fragment的方法,沒什麼好說的
public void show(FragmentManager manager, String tag) {
this.mDismissed = false;
this.mShownByMe = true;
FragmentTransaction ft = manager.beginTransaction();
ft.add(this, tag);
ft.commit();
}
//傳入一個事務管理,將fragment加入到事務管理中,並返回回退棧id
//返回的mBackStackId將在下文中用到
public int show(FragmentTransaction transaction, String tag) {
this.mDismissed = false;
this.mShownByMe = true;
transaction.add(this, tag);
this.mViewDestroyed = false;
this.mBackStackId = transaction.commit();
return this.mBackStackId;
}
//第一個show方法的增強版,看名字就知道,使用這個方法以後,則會當即執行fragment當中相關的方法,這個待會兒做出解釋
public void showNow(FragmentManager manager, String tag) {
this.mDismissed = false;
this.mShownByMe = true;
FragmentTransaction ft = manager.beginTransaction();
ft.add(this, tag);
ft.commitNow();
}
//Dialaog消失時的回調,setOndismissListener是在onActivityCreate中設置的,當前正是把dialog給dismiss,並無讓dialogfragment出棧
public void dismiss() {
this.dismissInternal(false);
}
//dismiss的增強版,先消失dialog,並將dialogfragment移除棧內
public void dismissAllowingStateLoss() {
this.dismissInternal(true);
}
//顯示判斷dialog,是否已經消失,若是已經消失,則不作任何操做
//1.若是dialog實例不爲空,先調用dialog的dismiss方法,隱藏dialog
//2.若是先前調用的是public int show(FragmentTransaction transaction, String tag)
//方法顯示的dialogfragment,那麼此時會根據以前返回的mBackStackId來將fragment移除棧內
//3.若是不是則再啓用事務將dialogfragment移除棧內,這裏會根據傳入的allowStateLoss來區分
//提交事務的方法
void dismissInternal(boolean allowStateLoss) {
if (!this.mDismissed) {
this.mDismissed = true;
this.mShownByMe = false;
if (this.mDialog != null) {
this.mDialog.dismiss();
}
this.mViewDestroyed = true;
if (this.mBackStackId >= 0) {
this.getFragmentManager().popBackStack(this.mBackStackId, 1);
this.mBackStackId = -1;
} else {
FragmentTransaction ft = this.getFragmentManager().beginTransaction();
ft.remove(this);
if (allowStateLoss) {
ft.commitAllowingStateLoss();
} else {
ft.commit();
}
}
}
}
//若是子類重寫該方法,那麼使用的就是你自定義的dialog
@NonNull
public Dialog onCreateDialog(Bundle savedInstanceState) {
return new Dialog(this.getActivity(), this.getTheme());
}
//dialog設置了onDismissListener後的回調,若是dialog正常消失,次回調中的方法不會調用到
//由於在DialogFragment的onStar方法中將mViewDestroyed變量賦值爲true,dialog顯示設置到調用顯示出來的生命週期回調咱們已經打印過了。
public void onDismiss(DialogInterface dialog) {
if (!this.mViewDestroyed) {
this.dismissInternal(true);
}
}
//該方法,先判斷Dialog是否已經顯示,而後會取onCreateView中返回的View,若是View不爲空,那麼該View將做爲Dialog的內容佈局,因此,若是你同時重寫了onCreateDialog和onCreateView方法,那麼會優先採用onCreateView當中的View做爲內容佈局,而後再做了一些監聽設置
//設置了dialog是否可點擊
//設置了dialog的消失監聽onDismissListener,因此消失時會回調文中的dimiss方法
//設置了dialog的取消監聽onCancelListener,在取消時會回調文中的onCancel方法
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if (this.mShowsDialog) {
View view = this.getView();
if (view != null) {
if (view.getParent() != null) {
throw new IllegalStateException("DialogFragment can not be attached to a container view");
}
this.mDialog.setContentView(view);
}
Activity activity = this.getActivity();
if (activity != null) {
this.mDialog.setOwnerActivity(activity);
}
this.mDialog.setCancelable(this.mCancelable);
this.mDialog.setOnCancelListener(this);
this.mDialog.setOnDismissListener(this);
if (savedInstanceState != null) {
Bundle dialogState = savedInstanceState.getBundle("android:savedDialogState");
if (dialogState != null) {
this.mDialog.onRestoreInstanceState(dialogState);
}
}
}
}
}
複製代碼
2.源碼分析總結
能夠重寫onCreateDialog或者onCreateView來建立DialogFragment的視圖,可是onCretaeView的優先級要高於onCreateDilaog,因此沒有特殊的須要,通常經過重寫onCreateView來建立Dialog的視圖便可。
從一開始咱們就打印了DialogFragment從建立視圖到show出來回調的一系列生命週期方法,咱們得知設置dialog的屬性能夠在DialogFragment中的onStar回調中進行。
對於dialog的顯示咱們經過查看源碼得知一共有三個方法,show()方法傳入一個fragmentManager以及一個tag,showNow方法,傳入一個fragmentManger和一個tag,show()方法,傳入一個事務和一個tag,並返回該事務中回退棧的id,在調用dismissInternal()方法時,會根據顯示DialogFragment的類型來操做DialogFragment出棧。
DialogFragment會在onActivityCreate()中對Dialog的內容試圖進行一次賦值,若是你重寫了onCrateView方法,並返回了一個不爲空的實例,那麼將會做爲該Dialog的內容視圖,而後再對dialog設置了是否可取消,消失監聽,以及取消監聽
經過上面的源碼簡析,咱們得知在DialogFragment消失時,只會將dialog給隱藏,並不會講DialogFragment移除棧內,若是想講DialogFragment在dialog消失時移除棧內,那麼須要手動調用dismissInternal()方法
能夠經過重寫onCreateDialog和onCreateView來設置DialogFragment的視圖,但onCraeteView設置的視圖優先級要高於在onCreateDialog。
設置Dialog的屬性須要在onCreateDialog回調後設置,否則getDialog獲得的對象則會是null,推薦在onStar方法中對Dialog進行屬性設置。
DialogFragment單個實例只能show一次DialogFrament,若是屢次展現的話,則會拋出以下異常
Process: org.lym.sourcecodeparse, PID: 27108
java.lang.IllegalStateException: Fragment already added: DialogFragment{8262d74 #0 tag}
at android.support.v4.app.FragmentManagerImpl.addFragment(FragmentManager.java:1893)
at android.support.v4.app.BackStackRecord.executeOps(BackStackRecord.java:760)
at android.support.v4.app.FragmentManagerImpl.executeOps(FragmentManager.java:2595)
at android.support.v4.app.FragmentManagerImpl.executeOpsTogether(FragmentManager.java:2382)
at android.support.v4.app.FragmentManagerImpl.removeRedundantOperationsAndExecute(FragmentManager.java:2337)
複製代碼
DialogFragment在正常Dismiss後並不會直接從當前的棧中移除,而是在DialogFragment中的onDestroyView()回調時,纔會對DialogFragment進行出棧操做,因此若是你若是須要在Activity中頻繁的顯示隱藏一個DialogFragment,那麼在dismiss時須要手動的調用dismissAllowingStateLoss()方法,而且再次show時不能用上一個DialogFragment實例。
DialogFragment並無對Dialog的消失提供監聽給調用者使用,可是咱們經過源碼分析得知,DialogFragment在onActivityCreate當中其實已經幫咱們設置了onDismissListener,因此咱們若是須要對Dialog的消失進行監聽的話只需重寫onDismiss方法便可,還有一種方式則是覆蓋父類設置的onDismissListener監聽,可是需注意的時,這個監聽的複寫,必須在父類onActivityCreate方法調用以後,關於消失監聽的兩種寫法以下:
//mListener爲提供到外部使用的回調
@Override
public void onDismiss(DialogInterface dialog) {
super.onDismiss(dialog);
mListener.onDismiss(dialog);
Logger.d(TAG, "onDismiss");
}
//複寫setOnDismissListener必須發生在父類的onActivityCreate以後
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if (getDialog() != null && null != mListener) {
getDialog().setOnDismissListener(new DialogInterface.OnDismissListener() {
@Override
public void onDismiss(DialogInterface dialog) {
ToastUtils.showToast("覆蓋後的OnDismiss Listener");
}
});
}
Logger.d(TAG, "onActivityCreated");
}
複製代碼
結語:以上即是全文的內容,是本身在使用DialogFragment中碰到了一些坑以及學習後的一些理解,但願能對您有幫助。