DialogFragment 的特點是具有dialog 的 效果,同時又可以擁有Fragment 的生命週期,因此我們可以像管理Fragment 一樣管理DialogFragment 。在 Android 中我們已經Dialog 類了,爲什麼還要增加一個DialogFragment 。在使用過程中DialogFragment 有事什麼樣子的呢,我們通過一個demo 來介紹。
設計一個從底部彈出的彈出框,同時 彈出框中的包汗tab頁。那麼我們需要在彈出框佈局中加入 Viewpager+ Fragment 的設計。對於複雜的 dialog 我們可以用 DialogFragment 來做。
效果如圖:
fragment 佈局文件
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<LinearLayout
android:id="@+id/contentView"
android:layout_width="match_parent"
android:layout_height="300dp"
android:orientation="vertical"
android:layout_marginLeft="10dp"
android:layout_marginBottom="10dp"
android:layout_marginRight="10dp"
android:layout_marginTop="150dp"
android:background="@drawable/bg_list"
android:layout_alignParentBottom="true"
>
<com.ogaclejapan.smarttablayout.SmartTabLayout
android:id="@+id/vp_tab"
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_gravity="center"
android:background="@color/transparent"
app:stl_indicatorAlwaysInCenter="false"
app:stl_indicatorWithoutPadding="false"
app:stl_indicatorInFront="false"
app:stl_indicatorInterpolation="linear"
app:stl_indicatorGravity="bottom"
app:stl_indicatorColor="@color/colorAccent"
app:stl_indicatorThickness="2dp"
app:stl_indicatorWidth="auto"
app:stl_indicatorCornerRadius="1px"
app:stl_overlineColor="#4D000000"
app:stl_overlineThickness="0dp"
app:stl_underlineColor="@color/line"
app:stl_underlineThickness="0dp"
app:stl_dividerColor="@color/line"
app:stl_dividerThickness="0dp"
app:stl_defaultTabBackground="@android:color/transparent"
app:stl_defaultTabTextAllCaps="false"
app:stl_defaultTabTextColor="#ffffff"
app:stl_defaultTabTextSize="12sp"
app:stl_defaultTabTextMinWidth="0dp"
app:stl_distributeEvenly="true"
app:stl_clickable="true"
app:stl_titleOffset="24dp"
app:stl_drawDecorationAfterTab="false"
/>
<TextView
android:layout_width="match_parent"
android:layout_height="1px"
android:background="@color/line"
/>
<android.support.v4.view.ViewPager
android:id="@+id/vp"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</LinearLayout>
</RelativeLayout>
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 下載地址