DialogFragment 的特点是具有dialog 的 效果,同时又可以拥有Fragment 的生命周期,因此我们可以像管理Fragment 一样管理DialogFragment 。在 Android 中我们已经Dialog 类了,为什么还要增加一个DialogFragment 。在使用过程中DialogFragment 有事什么样子的呢,我们通过一个demo 来介绍。
设计一个从底部弹出的弹出框,同时 弹出框中的包汗tab页。那么我们需要在弹出框布局中加入 Viewpager+ Fragment 的设计。对于复杂的 dialog 我们可以用 DialogFragment 来做。
fragment 布局文件
fragment Java文件
/**
* @author by nate_fu on 2018/9/13.
* @version vision 1.0
* @Email: [email protected]
*/
public class MyDialogFragment extends DialogFragment {
private View view;
private ViewPager viewPager;
private SmartTabLayout vpTab;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setStyle(DialogFragment.STYLE_NO_TITLE, R.style.CustomDialog);
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
view = inflater.inflate(R.layout.fragment_dialog,container,false);
initViewpage();
return view;
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
Window window = getDialog().getWindow();
getDialog().setCanceledOnTouchOutside(true);
window.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
window.setWindowAnimations(R.style.dialogWindowAnim);
WindowManager.LayoutParams wlp = window.getAttributes();
wlp.dimAmount=0f;
wlp.width = WindowManager.LayoutParams.MATCH_PARENT ;
wlp.height =WindowManager.LayoutParams.MATCH_PARENT ;
window.setAttributes(wlp);
}
private void initViewpage(){
FragmentPagerItemAdapter adapter = new FragmentPagerItemAdapter(
getChildFragmentManager(), FragmentPagerItems.with(getActivity())
.add("XX",InfoFragment.class)
.add("XX",InfoFragment.class)
.add("XX",InfoFragment.class)
.add("XX",InfoFragment.class)
.add("XX",InfoFragment.class)
.create());
viewPager = (ViewPager)view.findViewById(R.id.vp);
viewPager.setAdapter(adapter);
vpTab =(SmartTabLayout)view.findViewById(R.id.vp_tab);
vpTab.setViewPager( viewPager);
}
@Override
public void show(FragmentManager manager, String tag) {
super.show(manager, tag);
}
}
在MyDialogFragment 类中。我们在onCreate()方法中 执行了setStyle()方法 来设置dialog 的样式。为什么要在这里执行这个方法。我们可以从DialogFragment 的源码中找找原因
setStyle(DialogFragment.STYLE_NO_TITLE, R.style.CustomDialog);
@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);
}
在 DialogFragmet 类中有一个 onGetLayoutInflater()方法。其中 创建了Dialog 对象,同时 在setupDiaglog中设置了 style
所以我们必须在 onGetLayoutInflater()方法前设置style 。
f.mContainer = container;
f.mView = f.performCreateView(f.performGetLayoutInflater(
f.mSavedFragmentState), container, f.mSavedFragmentState);
这是在FragmentManager 类中找到的代码,可以看到 getLayoutInflater 在 CreateView 之前,所以我们不能再 onCreateView()中设置是style 而在Fragment的生命周期 中 onCreate()在 onCreateView()之前调用。
接下来我们就是要设置我们要的dialog 的宽高 了。默认创建的 dialog 会在中间位置,两边会留边。而我们习惯在 onCreateView() 中
Window window = getDialog().getWindow();
getDialog().setCanceledOnTouchOutside(true);
window.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
window.setWindowAnimations(R.style.dialogWindowAnim);
WindowManager.LayoutParams wlp = window.getAttributes();
wlp.dimAmount=0f;
wlp.width = WindowManager.LayoutParams.MATCH_PARENT ;
wlp.height =WindowManager.LayoutParams.MATCH_PARENT ;
window.setAttributes(wlp);
直接给window 设置宽高。因为在我们自定义 Dialog 是 ,new Dialog()之后我们就是这么操作的,发现在Dialog 的时候没什么问题,可是到了这里却没有效果了,这是为什么呢。我们还是继续去源码中查看
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);
}
在DialogFragment 中的 onActivityCreated 中 我们发现, mDialog.setContentView 这行代码,我们知道Android 中实现window 这个类的就是PhoneWindow 类。而 我们平时在onCreate 方法中调用的setContentView 最终调用的是 PhoneWindow 中的setContentView 方法
PhoneWindow.java
@Override
public void setContentView(int layoutResID) {
if (mContentParent == null) {
installDecor();
} else {
mContentParent.removeAllViews();
}
mLayoutInflater.inflate(layoutResID, mContentParent);
final Callback cb = getCallback();
if (cb != null) {
cb.onContentChanged();
}
}
我们看到了 其中执行了installDecor();
private void installDecor() {
mForceDecorInstall = false;
if (mDecor == null) {
mDecor = generateDecor(-1);
mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
mDecor.setIsRootNamespace(true);
if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
}
} else {
mDecor.setWindow(this);
}
}
这里我们看到当mDecor为null 的时候 则调用generateDecor方法完成DecorView的初始化。
而我们在结合Dialog 类来看。
@Override
public void onWindowAttributesChanged(WindowManager.LayoutParams params) {
if (mDecor != null) {
mWindowManager.updateViewLayout(mDecor, params);
}
}
当我们 给window设置Attributes 时候会回调 onWindowAttributesChanged 方法,而 在这个方法中,如果 mDecor 为null 的话是不会update我们的参数的。所以从上面我们知道 在 DialogFragment 中,dialog 窗口 被创建是在 onActivityCreate中,在此DecorView才被实例化。而我们要设置宽高的参数,必须在 DecorView实例化之后,不然是没有效果的。
以上是我的分析,如果有其他见解欢迎留言讨论。
demo 下载地址
https://download.csdn.net/download/u010324235/10698524
https://github.com/fyhsgsgssg/DialogFragment