手把手帶你玩轉 DialogFragment


思維導圖

 

一、為什么要學習 DialogFragment

你還在用 Dialog 嗎?你還在經常煩惱於屏幕翻轉的時候,Dialog 的各種奇葩情況嗎?你想降低耦合嗎?如果你有其中的一個煩惱,那么恭喜你,遇見了 DialogFragment ,他恰巧就解決了上面所說的問題,如果感興趣的話,隨筆者來看下吧!

 

二、背景

Android 官方推薦使用 DialogFragment 來代替 Dialog ,可以讓它具有更高的可復用性(降低耦合)和更好的便利性(很好的處理屏幕翻轉的情況)。而創建 DialogFragment 有兩種方式:

「法一:覆寫其 onCreateDialog 方法」

一般用於創建替代傳統的 Dialog 對話框的場景,UI 簡單,功能單一,不適用於使用了多線程(例如網絡請求)的情況下(因為不能正確的獲取當前 Fragment 的狀態,會產生空指針異常)

「法二:覆寫其 onCreateView 方法」

一般用於創建復雜內容彈窗或全屏展示效果的場景,UI 復雜,功能復雜,一般有網絡請求等異步操作

 

三、應用

3.1 基本用法是什么

法一:

a.創建一個簡單的 Dialog 並返回它即可

@Override public Dialog onCreateDialog(Bundle savedInstanceState) {     AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());     // 設置主題的構造方法     // AlertDialog.Builder builder = new AlertDialog.Builder(getActivity(), R.style.CustomDialog);     builder.setTitle("注意:")            .setMessage("是否退出應用?")            .setPositiveButton("確定", null)            .setNegativeButton("取消", null)            .setCancelable(false);            //builder.show(); // 不能在這里使用 show() 方法     return builder.create(); } 

 

b.你也可以使用自定義 View 來創建:

@Override public Dialog onCreateDialog(Bundle savedInstanceState) {     AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());     // 設置主題的構造方法     // AlertDialog.Builder builder = new AlertDialog.Builder(getActivity(), R.style.CustomDialog);     LayoutInflater inflater = getActivity().getLayoutInflater();       View view = inflater.inflate(R.layout.fragment_dialog, null);       builder.setView(view)      // Do Someting,eg: TextView tv = view.findViewById(R.id.tv);     return builder.create(); }

 

PS:創建 Dialog 的方式有多種,比如下面這種,使用時略有差異,需要自己注意:

@Override public Dialog onCreateDialog(Bundle savedInstanceState) {     LayoutInflater inflater = getActivity().getLayoutInflater();     View view = inflater.inflate(R.layout.fragment_dialog, null);     Dialog dialog = new Dialog(getActivity());     // 設置主題的構造方法     // Dialog dialog = new Dialog(getActivity(), R.style.CustomDialog);     dialog.setContentView(view);     // Do Someting     return dialog; }

 

這種情況,標題內容上面的白色部分,其實是默認的標題欄,如果需要的話,可以設置隱藏標題欄(將在下文說到)

3.2 如何處理屏幕翻轉

如果使用傳統的 Dialog ,需要我們手動處理屏幕翻轉的情況,但使用 DialogFragment 的話,則不需要我們進行任何處理,FragmentManager 會自動管理 DialogFragment 的生命周期。

3.3 如何隱藏標題欄

在基本用法里代碼注釋有設置主題的地方,下面詳細說下兩種方法下設置無標題欄的方式:法一:

@NonNull @Override public Dialog onCreateDialog(Bundle savedInstanceState) {     LayoutInflater inflater = Objects.requireNonNull(getActivity()).getLayoutInflater();     @SuppressLint("InflateParams") View view = inflater.inflate(R.layout.fragment_i_o_s_dialog, null);     Dialog dialog = new Dialog(getActivity());     // 關閉標題欄,setContentView() 之前調用     dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);     dialog.setContentView(view);     dialog.setCanceledOnTouchOutside(true);     return dialog; } 

法二:

@Override public void onCreate(@Nullable Bundle savedInstanceState) {  super.onCreate(savedInstanceState);  setStyle(DialogFragment.STYLE_NO_TITLE, 0); }

3.4 如何實現全屏

常用的形式大多是寬度上和屏幕一樣寬,高度自適應,下面直接看代碼:

法一:

@Override public Dialog onCreateDialog(Bundle savedInstanceState) {     LayoutInflater inflater = getActivity().getLayoutInflater();     View view = inflater.inflate(R.layout.fragment_dialog, null);     Dialog dialog = new Dialog(getActivity(), 0);     dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);     dialog.setContentView(view);     dialog.setCanceledOnTouchOutside(true);     //Do something     // 設置寬度為屏寬、位置靠近屏幕底部     Window window = dialog.getWindow();     //設置了窗口的背景色為透明,這一步是必須的     // <color name="transparent">#50000000</color>     window.setBackgroundDrawableResource(R.color.transparent);     WindowManager.LayoutParams wlp = window.getAttributes();     wlp.gravity = Gravity.BOTTOM;     //設置窗口的寬度為 MATCH_PARENT,效果是和屏幕寬度一樣大     wlp.width = WindowManager.LayoutParams.MATCH_PARENT;     wlp.height = WindowManager.LayoutParams.WRAP_CONTENT;     window.setAttributes(wlp);     return dialog; } 

法二:

@Override public void onCreate(@Nullable Bundle savedInstanceState) {     super.onCreate(savedInstanceState);     setStyle(DialogFragment.STYLE_NO_TITLE, 0); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {         getDialog().setCanceledOnTouchOutside(true);         View rootView = inflater.inflate(R.layout.fragment_dialog, container, false);         //Do something         // 設置寬度為屏寬、靠近屏幕底部。         final Window window = getDialog().getWindow();      //這步是必須的         window.setBackgroundDrawableResource(R.color.transparent);      //必要,設置 padding,這一步也是必須的,內容不能填充全部寬度和高度         window.getDecorView().setPadding(0, 0, 0, 0);         WindowManager.LayoutParams wlp = window.getAttributes();         wlp.gravity = Gravity.BOTTOM;         wlp.width = WindowManager.LayoutParams.MATCH_PARENT;         wlp.height = WindowManager.LayoutParams.WRAP_CONTENT;         window.setAttributes(wlp);         return rootView; } 

3.5 應用場景的區別是什么

文章一開始簡單總結了法一 和法二的應用場景,這里說明下:

法一:為簡單的替代 Dialog 提供了非常方便的創建方式,但是在使用了多線程(例如網絡請求)的情況下,不能正確的獲取當前 Fragment 的狀態,會產生空指針異常法二:則沒有如上空指針的問題,而且,其創建方式默認使用了自定義 View,更便於應對復雜 UI 的場景

3.6 如何與 Activity 進行交互?

使用回調的方式

a.在 DialogFragment 中:

public interface OnDialogListener {     void onDialogClick(String person); } private OnDialogListener mlistener; public void setOnDialogListener(OnDialogListener dialogListener){     this.mlistener = dialogListener; } 

在 DialogFragment 的點擊事件中:

public OnDialogListener mlistener; @Override public void onClick(View view) {     switch (view.getId()) {         case R.id.tv1:             mlistener.onDialogClick("1");             dismiss();             break;         case R.id.tv2:             mlistener.onDialogClick("2");             dismiss();             break;         case R.id.tv3:             mlistener.onDialogClick("3");             dismiss();             break;         case R.id.tv4:             mlistener.onDialogClick("4");             dismiss();             break;     } } 

b.在 Activity 中

dialogFragment.setOnDialogListener(new PersonDialogFragment.OnDialogListener() {     @Override     public void onDialogClick(String person) {         ToastUtil.showToast(person);     } }); 

####3.7 如何結合動畫使用 a.設置從下到上彈出的動畫

private void slideToUp(View view) {     Animation slide = new TranslateAnimation(Animation.RELATIVE_TO_SELF, 0, Animation.RELATIVE_TO_SELF, 0, Animation.RELATIVE_TO_SELF, 1.0f, Animation.RELATIVE_TO_SELF, 0);     slide.setDuration(400);     slide.setFillEnabled(true);     slide.setFillAfter(true);     view.startAnimation(slide); } 

b.設置從上到下彈出的動畫

private boolean isAnimation = false;//用來判斷是否多次點擊。防止多次執行 public void slideToDown(View view) {     Animation slide = new TranslateAnimation(             Animation.RELATIVE_TO_SELF, 0.0f,             Animation.RELATIVE_TO_SELF, 0.0f,              Animation.RELATIVE_TO_SELF, 0.0f,             Animation.RELATIVE_TO_SELF, 1.0f);     slide.setDuration(400);     slide.setFillEnabled(true);     slide.setFillAfter(true);     view.startAnimation(slide);     slide.setAnimationListener(new Animation.AnimationListener() {         @Override         public void onAnimationStart(Animation animation) {         }         @Override         public void onAnimationEnd(Animation animation) {             //用來判斷是否多次點擊。防止多次執行             isAnimation = false;             //彈框消失             IOSDialogFragment.this.dismiss();         }         @Override         public void onAnimationRepeat(Animation animation) {         }     }); } 

c.封裝從上到下彈出的動畫

加上判斷是否多次點擊。防止多次執行

private void dialogFinish() {     if (isAnimation) {         return;     }     isAnimation = true;     slideToDown(rootView); }

3.8 如何在 Activity 彈出 DialogFragment ?

mBtn.setOnClickListener(new View.OnClickListener() {     @Override     public void onClick(View view) {         IOSDialogFragment fragment = new IOSDialogFragment();         //第二個參數是 tag         fragment.show(getSupportFragmentManager(), "android");     } });

3.9 如何點擊空白處時關閉的時候,還能使用動畫?

直接對 DecorView 設置 onTouchListener

window.getDecorView().setOnTouchListener(new View.OnTouchListener() {     public boolean onTouch(View v, MotionEvent event) {         if (event.getAction() == MotionEvent.ACTION_UP) {             //彈框消失的動畫執行相關代碼             ....             ....                  }         return true;     } }); 

四、結語

終於看完了鴨!累死鴨了!如果還有什么不是很清楚的話,可以看下筆者寫的示例 Demo https://github.com/LoveLifeEveryday/TestDialogFragment

如果文章對您有一點幫助的話,希望您能點一下贊,您的點贊,是我前進的動力推薦閱讀:

Flutter在游戲開發的表現及跨平台帶來的優勢JVM 初級面試題【譯】Flutter vs React Native vs Native:深度性能比較Flutter動畫入門—— 雙環Loading的實現錦囊篇|一文摸懂LeakCanary

作者 | 許朋友愛玩

地址 | juejin.im/post/6854573211854733320

覺得不錯的話,點個關注謝謝~

點擊鏈接《Java面試BAT通關手冊》,覆蓋了Java核心技術、JVM、Java並發、SSM、微服務、數據庫、數據結構等等。


免責聲明!

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



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