snailgesture 2019-06-28
1.最简单的使用方法
2.源码分析
5.常见问题总结
如下所示:
public class CustomDialogFragment extends DialogFragment { @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); //设置样式 setStyle(DialogFragment.STYLE_NO_TITLE, R.style.CenterDialog); } @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { return inflater.inflate(R.layout.view_fragment_dialog, container, false); } public static void showDialog(FragmentActivity activity){ CustomDialogFragment customDialogFragment = new CustomDialogFragment(); customDialogFragment.show(activity.getSupportFragmentManager(),"yc"); } } //然后一行代码调用 CustomDialogFragment.showDialog(this);
1.2.1 创建theme主题样式,并且进行设置
STYLE_NORMAL:会显示一个普通的dialog STYLE_NO_TITLE:不带标题的dialog STYLE_NO_FRAME:无框的dialog STYLE_NO_INPUT:无法输入内容的dialog,即不接收输入的焦点,而且触摸无效。
1.2.4 如何去掉标题栏,也许你会问,为什么第二种要在super.onActivityCreated(savedInstanceState)之前设置呢。这个是因为,看了源码之后才知道onActivityCreated这个方法中,有mDialog.setContentView(view)这一步,说到setContentView是不是很熟悉。没错,后面再深度解析这块源码思路……
//第一种 //设置样式时,使用STYLE_NO_TITLE setStyle(DialogFragment.STYLE_NO_TITLE, R.style.CenterDialog); //第二种 @Override public void onActivityCreated(Bundle savedInstanceState) { Window window = getDialog().getWindow(); if(window!=null){ window.requestFeature(Window.FEATURE_NO_TITLE); } super.onActivityCreated(savedInstanceState); }
onCreate这个方法主要是保存一些属性状态,比如style样式,theme注意,是否可以取消,后退栈的ID等等。
@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); } }
mShowsDialog这个参数的作用
这个方法很重要呢,注意是设置对话框的基本外观和设置主题等等。通过手动设置Dialog和Window可以实现相同的效果,如果是在对话框创建之后调用它将会失去作用……
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; } }
该方法的作用主要是:当DialogFragment依附的Activity被创建的时候调用,此时fragment的活动窗体被初始化
onCreateDialog方法,你可以重写这个方法,创建一个自己定义好的dialog。默认情况下,会自己创建一个Dialog。
@NonNull public Dialog onCreateDialog(Bundle savedInstanceState) { return new Dialog(getActivity(), getTheme()); }
共同点:这两种显示方式都是通过tag的方式将DialogFragment以事务的形式提交,不同的是第二种方式是采用已经创建过的transaction,并且他返回了一个int类型的数值mBackStackId,mBackStackId是干什么用的呢?
public void show(FragmentManager manager, String tag) { mDismissed = false; mShownByMe = true; FragmentTransaction ft = manager.beginTransaction(); ft.add(this, tag); ft.commit(); } public int show(FragmentTransaction transaction, String tag) { mDismissed = false; mShownByMe = true; transaction.add(this, tag); mViewDestroyed = false; mBackStackId = transaction.commit(); return mBackStackId; }
在源码中可以看到这两个方法都调用了dismissInternal(boolean)方法,不同的是传入的boolean值一个为false一个为true,那么究竟这个boolean起到什么作用呢?
具体看下面代码
@Override public void onStart() { super.onStart(); if (mDialog != null) { mViewDestroyed = false; mDialog.show(); } } @Override public void onStop() { super.onStop(); if (mDialog != null) { mDialog.hide(); } } @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; } }
第一种:链式编程,如下所示
BottomDialogFragment.create(getSupportFragmentManager()) .setViewListener(new BottomDialogFragment.ViewListener() { @Override public void bindView(View v) { } }) .setLayoutRes(R.layout.dialog_bottom_layout_list) .setDimAmount(0.5f) .setTag("BottomDialog") .setCancelOutside(true) .setHeight(getScreenHeight() / 2) .show();
第二种:直接继承,可以高度定制自己想要的弹窗
public class ADialog extends BaseDialogFragment { @Override protected boolean isCancel() { return false; } @Override public int getLayoutRes() { return 0; } @Override public void bindView(View v) { } }
报错日志如下:
lang.IllegalStateException: Can not perform this action after onSaveInstanceState at android.support.v4.app.FragmentManagerImpl.checkStateLoss(FragmentManager.java:1493)
出现该问题的原因
追踪报错日志的来源
//第一步: public FragmentManager getSupportFragmentManager() { return mFragments.getSupportFragmentManager(); } //第二步: public FragmentManager getSupportFragmentManager() { return mHost.getFragmentManagerImpl(); } //第三步: FragmentManagerImpl getFragmentManagerImpl() { return mFragmentManager; } //第四步:看beginTransaction()方法 @Override public FragmentTransaction beginTransaction() { return new BackStackRecord(this); } //第五步:看BackStackRecord类中看commit方法 @Override public int commit() { return commitInternal(false); } @Override public int commitAllowingStateLoss() { return commitInternal(true); } //第六步:可以看到这俩函数的区别就是commitInternal()方法中参数一个为true,一个为false int commitInternal(boolean allowStateLoss) { if (mCommitted) throw new IllegalStateException("commit already called"); if (FragmentManagerImpl.DEBUG) { Log.v(TAG, "Commit: " + this); LogWriter logw = new LogWriter(TAG); PrintWriter pw = new PrintWriter(logw); dump(" ", null, pw, null); pw.close(); } mCommitted = true; if (mAddToBackStack) { mIndex = mManager.allocBackStackIndex(this); } else { mIndex = -1; } mManager.enqueueAction(this, allowStateLoss); return mIndex; } //第七步:再追踪到enqueueAction(this,allowStateLoss) public void enqueueAction(OpGenerator action, boolean allowStateLoss) { if (!allowStateLoss) { checkStateLoss(); } synchronized (this) { if (mDestroyed || mHost == null) { throw new IllegalStateException("Activity has been destroyed"); } if (mPendingActions == null) { mPendingActions = new ArrayList<>(); } mPendingActions.add(action); scheduleCommit(); } } //第八步:checkStateLoss()方法,这里可以看到抛出的错误日志呢 private void checkStateLoss() { if (mStateSaved) { throw new IllegalStateException( "Can not perform this action after onSaveInstanceState"); } if (mNoTransactionsBecause != null) { throw new IllegalStateException( "Can not perform this action inside of " + mNoTransactionsBecause); } }