Android開發 DialogFragment對話框詳解


前言

  在聊DialogFragment之前,我們看看以往我們在Android里實現一個對話框一般有這幾種方式:

  •   Dialog 繼承重寫Dialog實現一個自定義的Dialog
  •   AlertDialog Android原生提供的對話框(底層是繼承Dialog實現)
  •   PopupWindow 用彈出懸浮框,實現對話框。這種對話框可以用在指定位置顯示,一般用於一些非常小的按鍵彈窗。怎么實現可以參考我的博客:https://www.cnblogs.com/guanxinjing/p/10156153.html

  這3種彈窗對話框都有一個問題,就是與activity的生命周期不是捆綁的,得時刻注意在activity后台之后關閉Dialog。所以,后面google推薦使用DialogFragment來取代它們。DialogFragment本質其實是Fragment,有Fragment的生命周期並且與創建它的activity有捆綁,在google推出了Jetpack系列后,配合Jetpack系列LiveData與navigation在使用上比一般的Dialog安全更多,並且在數據傳遞上也非常簡單,配合navigation架構管理起來也十分簡單明晰。

  如果你未接觸過不了解Jetpack系列,可以參考我的博客:https://www.cnblogs.com/guanxinjing/category/1550385.html   了解完Jetpack系列,你就可以明白google為什么推出這種對話框了。

  下面我們就根據2個最簡單demo和與一些使用特例,來介紹DialogFragment的使用。

  

Dialog創建DialogFragment的簡單Demo

  DialogFragment有2種方法創建我們需要的對話框內容,其中就有以Dialog來創建內容方式。

繼承重寫DialogFragment: 

public class MyDialog1 extends DialogFragment {

    @NonNull
    @Override
    public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
        //創建對話框,我們需要返回dialog
        AlertDialog.Builder dialog = new AlertDialog.Builder(getContext());
        dialog.setTitle("測試Dialog");
        dialog.setMessage("DialogFragment");
        return dialog.create();
    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        //此方法在視圖已經創建后返回的,但是這個view 還沒有添加到父級中,我們在這里可以重新設定view的各個數據
    }
}

在activity里顯示對話框:

        mBtnTest.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                myDialog = new MyDialog1();
                myDialog.show(getSupportFragmentManager(),"myDialog");
            }
        });

效果圖:

 

布局View創建DialogFragment的簡單Demo

  DialogFragment另一種創建內容方法,導入一個View

public class MyDialog extends DialogFragment {

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.set_wifi_password_dialog_layout, container, false);
        return view;
    }

    @Override
    public void onViewCreated(@NonNull final View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        /*
        此方法在視圖View已經創建后返回的,但是這個view 還沒有添加到父級中。
        我們在這里可以重新設定view的各個數據,但是不能修改對話框最外層的ViewGroup的布局參數。
        因為這里的view還沒添加到父級中,我們需要在下面onStart生命周期里修改對話框尺寸參數
         */

    }

    @Override
    public void onStart() {
        /*
            因為View在添加后,對話框最外層的ViewGroup並不知道我們導入的View所需要的的寬度。 所以我們需要在onStart生命周期里修改對話框尺寸參數
         */
        WindowManager.LayoutParams params = getDialog().getWindow().getAttributes();
        params.width = ViewGroup.LayoutParams.MATCH_PARENT;
        getDialog().getWindow().setAttributes((WindowManager.LayoutParams) params);
        super.onStart();
    }
}

顯示對話框的代碼跟上面的demo一樣,就不重復貼出來了,看看效果圖:

改變對話框的顯示位置

public class MyDialog2 extends DialogFragment {
    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.set_wifi_password_dialog_layout, container, false);
        return view;
    }

    @Override
    public void onViewCreated(@NonNull final View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

    }

    @Override
    public void onStart() {
        WindowManager.LayoutParams params = getDialog().getWindow().getAttributes();
        params.width = ViewGroup.LayoutParams.MATCH_PARENT;
        params.gravity = Gravity.BOTTOM; //將對話框放到布局下面,也就是屏幕下方
        getDialog().getWindow().setAttributes((WindowManager.LayoutParams) params);
        super.onStart();
    }
}

效果圖:

將對話框的寬或者高鋪滿屏幕

設置對話框鋪滿屏幕有2種方式:

第一種  需要在styles.xml文件里,添加一個沒有內邊距的style,如下

  在上面的出現在屏幕下方的對話框中,依然與屏幕有小段距離,那個其實是dialog自帶的padding內邊距屬性導致的。這種方式可以設置只在寬度上鋪滿屏幕,但是高度上依然留有一定的內邊距。

 

    <style name="dialogFullScreen" parent="Theme.AppCompat.Dialog">
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
        <item name="android:padding">0dp</item>
        <item name="android:windowBackground">@android:color/white</item>
        <item name="android:textColor">@android:color/black</item>
    </style>

第二種 需要在styles.xml文件里,設置 android:windowFullscreen 屬性:

    <style name="dialogFullScreen" parent="Theme.AppCompat.Dialog">
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
        <item name="android:windowBackground">@android:color/white</item>
        <item name="android:textColor">@android:color/black</item>
        <item name="android:windowFullscreen">true</item>
    </style>

以上2種互為互補,都可以實現需要的效果

然后依然是重寫DialogFragment

public class MyDialog3 extends DialogFragment {

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.set_wifi_password_dialog_layout, container, false);
        return view;
    }

    @Override
    public void onViewCreated(@NonNull final View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

    }

    @Override
    public void onStart() {
        WindowManager.LayoutParams params = getDialog().getWindow().getAttributes();
        params.width = ViewGroup.LayoutParams.MATCH_PARENT;//設置寬度為鋪滿
        params.gravity = Gravity.BOTTOM;
        getDialog().getWindow().setAttributes((WindowManager.LayoutParams) params);
        super.onStart();
    }
}

然后是重點,在創建DialogFragment對話框的時候添加我們的style。

        mBtnTest.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                MyDialog3 myDialog = new MyDialog3();
                myDialog.setStyle(DialogFragment.STYLE_NORMAL, R.style.dialogFullScreen);//添加上面創建的style
                myDialog.show(getSupportFragmentManager(),"aa");
            }
        });

下方的效果圖里,我們就獲得了一個在寬度上鋪滿屏幕的對話框,舉一反三在設置高度上也是一樣的:

 

 

設置點擊外部空白處不會關閉對話框

方式一

  在前面創建的對話框里,在點擊外部后依然會關閉對話框,我們有時候有些重要消息並不希望用戶可以點擊外部可以取消。

這個屬性一樣在styles.xml,創建style里添加 <item name="android:windowCloseOnTouchOutside">false</item>

    <style name="dialogFullScreen" parent="Theme.AppCompat.Dialog">
        <item name="android:padding">0dp</item>
        <item name="android:windowBackground">@android:color/white</item>
        <item name="android:textColor">@android:color/black</item>
        <item name="android:windowCloseOnTouchOutside">false</item>
    </style>

方式二

        //getDialog().setCancelable(false);//這個會屏蔽掉返回鍵
        getDialog().setCanceledOnTouchOutside(isCanceledOnTouchOutside());

 

設置在彈出對話框后同時彈出軟鍵盤

 我只需要兩步,1.將需要輸入內容的EditText設置為焦點 2.設置軟鍵盤可見

public class MyDialog3 extends DialogFragment {

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.set_wifi_password_dialog_layout, container, false);
        return view;
    }

    @Override
    public void onViewCreated(@NonNull final View view, @Nullable Bundle savedInstanceState) {
        EditText editPassword = view.findViewById(R.id.edit_password);
        editPassword.requestFocus();//設置焦點
        getDialog().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);//設置輸入盤可見
        super.onViewCreated(view, savedInstanceState);
    }

    @Override
    public void onStart() {
        WindowManager.LayoutParams params = getDialog().getWindow().getAttributes();
        params.width = ViewGroup.LayoutParams.MATCH_PARENT;
        params.gravity = Gravity.BOTTOM;
        getDialog().getWindow().setAttributes((WindowManager.LayoutParams) params);
        super.onStart();
    }
}

效果圖:

在Fragment里啟動對話框

與activity里一樣沒啥區別,唯一的區別就是你打算依然用老套的onActivityResult來向下傳值,那么你就需要設置一個目標Fragment在下面的代碼里setTargetFragment()方法就是起到這個作用的,在下面的代碼里我們用MyDialog1 啟動了 MyDialog2。

DialogFragment其實就是Fragment,所以我這里就偷懶一下,直接用對話框啟動對話框了。。。不在單獨寫一個Fragment

MyDialog1.Java

public class MyDialog1 extends DialogFragment {

    @NonNull
    @Override
    public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
        AlertDialog.Builder dialog = new AlertDialog.Builder(getContext());
        dialog.setTitle("測試Dialog");
        dialog.setMessage("啟動另外一個對話框");
        dialog.setPositiveButton("啟動", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                MyDialog2 myDialog2 = new MyDialog2();
                myDialog2.setTargetFragment(MyDialog1.this, 300);
                myDialog2.setStyle(DialogFragment.STYLE_NORMAL, R.style.dialogFullScreen);
                myDialog2.show(getFragmentManager(), "myDialog2");

            }
        });

        return dialog.create();
    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
    }

    @Override
    public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        //這里可以返回 MyDialog2  Fragment的數據
    }
}

MyDialog2.Java

public class MyDialog2 extends DialogFragment {
    private static final String TAG = "MyDialog";
    private ViewTreeObserver.OnGlobalLayoutListener onGlobalLayoutListener;


    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.set_wifi_password_dialog_layout, container, false);
        return view;
    }

    @Override
    public void onViewCreated(@NonNull final View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
    }

    @Override
    public void onStart() {
        WindowManager.LayoutParams params = getDialog().getWindow().getAttributes();
        params.width = ViewGroup.LayoutParams.MATCH_PARENT;
        params.gravity = Gravity.BOTTOM;
        getDialog().getWindow().setAttributes((WindowManager.LayoutParams) params);
        super.onStart();
    }
}

設置圓角與實際不符合的問題

實際上是你設置的背景圖片,被Dialog自帶的背景遮蓋了,導致圓角無法顯示。所以設置一下透明背景就可以了。

注意設置DecorView的背景與設置Window的背景是有區別的,區別如下:

設置DecorView背景

    @Override
    public void onStart() {
        WindowManager.LayoutParams params = getDialog().getWindow().getAttributes();
        params.width = ViewGroup.LayoutParams.MATCH_PARENT;
        getDialog().getWindow().setAttributes(params);
        getDialog().getWindow().getDecorView().setBackground(new ColorDrawable(Color.TRANSPARENT));
super.onStart(); }

效果圖:

 

設置Window的背景

 

    @Override
    public void onStart() {
        WindowManager.LayoutParams params = getDialog().getWindow().getAttributes();
        params.width = ViewGroup.LayoutParams.MATCH_PARENT;
        getDialog().getWindow().setAttributes(params);
        getDialog().getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
        super.onStart();
    }

 

 

 

 

改變Window背景的透明度

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        WindowManager.LayoutParams layoutParams = getWindow().getAttributes();
        layoutParams.dimAmount = 0f;//調整透明度
        getWindow().setAttributes(layoutParams);

    }

 

 


 

end


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM