DialogFragment運(yùn)行原理及使用方法詳解
思維導(dǎo)圖
一、為什么要學(xué)習(xí) DialogFragment
你還在用 Dialog 嗎?你還在經(jīng)常煩惱于屏幕翻轉(zhuǎn)的時候,Dialog 的各種奇葩情況嗎?你想降低耦合嗎?如果你有其中的一個煩惱,那么恭喜你,遇見了 DialogFragment ,他恰巧就解決了上面所說的問題,如果感興趣的話,隨筆者來看下吧!
二、背景
Android 官方推薦使用 DialogFragment 來代替 Dialog ,可以讓它具有更高的可復(fù)用性(降低耦合)和更好的便利性(很好的處理屏幕翻轉(zhuǎn)的情況)。而創(chuàng)建 DialogFragment 有兩種方式:
「法一:覆寫其 onCreateDialog 方法」
一般用于創(chuàng)建替代傳統(tǒng)的 Dialog 對話框的場景,UI 簡單,功能單一,不適用于使用了多線程(例如網(wǎng)絡(luò)請求)的情況下(因?yàn)椴荒苷_的獲取當(dāng)前 Fragment 的狀態(tài),會產(chǎn)生空指針異常)
「法二:覆寫其 onCreateView 方法」
一般用于創(chuàng)建復(fù)雜內(nèi)容彈窗或全屏展示效果的場景,UI 復(fù)雜,功能復(fù)雜,一般有網(wǎng)絡(luò)請求等異步操作
三、應(yīng)用
3.1 基本用法是什么
法一:
a.創(chuàng)建一個簡單的 Dialog 并返回它即可
@Override public Dialog onCreateDialog(Bundle savedInstanceState) { AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); // 設(shè)置主題的構(gòu)造方法 // AlertDialog.Builder builder = new AlertDialog.Builder(getActivity(), R.style.CustomDialog); builder.setTitle("注意:") .setMessage("是否退出應(yīng)用?") .setPositiveButton("確定", null) .setNegativeButton("取消", null) .setCancelable(false); //builder.show(); // 不能在這里使用 show() 方法 return builder.create(); }
b.你也可以使用自定義 View 來創(chuàng)建:
@Override public Dialog onCreateDialog(Bundle savedInstanceState) { AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); // 設(shè)置主題的構(gòu)造方法 // 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:創(chuàng)建 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()); // 設(shè)置主題的構(gòu)造方法 // Dialog dialog = new Dialog(getActivity(), R.style.CustomDialog); dialog.setContentView(view); // Do Someting return dialog; }
這種情況,標(biāo)題內(nèi)容上面的白色部分,其實(shí)是默認(rèn)的標(biāo)題欄,如果需要的話,可以設(shè)置隱藏標(biāo)題欄(將在下文說到)
3.2 如何處理屏幕翻轉(zhuǎn)
如果使用傳統(tǒng)的 Dialog ,需要我們手動處理屏幕翻轉(zhuǎn)的情況,但使用 DialogFragment 的話,則不需要我們進(jìn)行任何處理,F(xiàn)ragmentManager 會自動管理 DialogFragment 的生命周期。
3.3 如何隱藏標(biāo)題欄
在基本用法里代碼注釋有設(shè)置主題的地方,下面詳細(xì)說下兩種方法下設(shè)置無標(biāo)題欄的方式:法一:
@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()); // 關(guān)閉標(biāo)題欄,setContentView() 之前調(diào)用 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 如何實(shí)現(xiàn)全屏
常用的形式大多是寬度上和屏幕一樣寬,高度自適應(yīng),下面直接看代碼:
法一:
@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 // 設(shè)置寬度為屏寬、位置靠近屏幕底部 Window window = dialog.getWindow(); //設(shè)置了窗口的背景色為透明,這一步是必須的 // <color name="transparent">#50000000</color> window.setBackgroundDrawableResource(R.color.transparent); WindowManager.LayoutParams wlp = window.getAttributes(); wlp.gravity = Gravity.BOTTOM; //設(shè)置窗口的寬度為 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 // 設(shè)置寬度為屏寬、靠近屏幕底部。 final Window window = getDialog().getWindow(); //這步是必須的 window.setBackgroundDrawableResource(R.color.transparent); //必要,設(shè)置 padding,這一步也是必須的,內(nèi)容不能填充全部寬度和高度 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 應(yīng)用場景的區(qū)別是什么
文章一開始簡單總結(jié)了法一 和法二的應(yīng)用場景,這里說明下:
法一:為簡單的替代 Dialog 提供了非常方便的創(chuàng)建方式,但是在使用了多線程(例如網(wǎng)絡(luò)請求)的情況下,不能正確的獲取當(dāng)前 Fragment 的狀態(tài),會產(chǎn)生空指針異常法二:則沒有如上空指針的問題,而且,其創(chuàng)建方式默認(rèn)使用了自定義 View,更便于應(yīng)對復(fù)雜 UI 的場景
3.6 如何與 Activity 進(jìn)行交互?
使用回調(diào)的方式
a.在 DialogFragment 中:
public interface OnDialogListener { void onDialogClick(String person); } private OnDialogListener mlistener; public void setOnDialogListener(OnDialogListener dialogListener){ this.mlistener = dialogListener; }
在 DialogFragment 的點(diǎn)擊事件中:
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 如何結(jié)合動畫使用 a.設(shè)置從下到上彈出的動畫
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.設(shè)置從上到下彈出的動畫
private boolean isAnimation = false;//用來判斷是否多次點(diǎn)擊。防止多次執(zhí)行 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) { //用來判斷是否多次點(diǎn)擊。防止多次執(zhí)行 isAnimation = false; //彈框消失 IOSDialogFragment.this.dismiss(); } @Override public void onAnimationRepeat(Animation animation) { } }); }
c.封裝從上到下彈出的動畫
加上判斷是否多次點(diǎn)擊。防止多次執(zhí)行
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(); //第二個參數(shù)是 tag fragment.show(getSupportFragmentManager(), "android"); } });
3.9 如何點(diǎn)擊空白處時關(guān)閉的時候,還能使用動畫?
直接對 DecorView 設(shè)置 onTouchListener
window.getDecorView().setOnTouchListener(new View.OnTouchListener() { public boolean onTouch(View v, MotionEvent event) { if (event.getAction() == MotionEvent.ACTION_UP) { //彈框消失的動畫執(zhí)行相關(guān)代碼 .... .... } return true; } });
四、結(jié)語
終于看完了鴨!累死鴨了!如果還有什么不是很清楚的話,可以看下筆者寫的示例 Demo
https://github.com/LoveLifeEveryday/TestDialogFragment
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Android ListView實(shí)現(xiàn)圖文列表顯示
這篇文章主要為大家詳細(xì)介紹了Android ListView實(shí)現(xiàn)圖文列表顯示,具有一定的參考價值,感興趣的小伙伴們可以參考一下2019-01-01Android控件之ScrollView用法實(shí)例分析
這篇文章主要介紹了Android控件之ScrollView用法,以完整實(shí)例形式較為詳細(xì)的分析了ScrollView控件滾動顯示的實(shí)現(xiàn)技巧,需要的朋友可以參考下2015-09-09Android實(shí)現(xiàn)拍照、選擇相冊圖片并裁剪功能
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)拍照、選擇相冊圖片并裁剪功能的相關(guān)資料,具有一定的參考價值,感興趣的小伙伴們可以參考一下2016-12-12Android實(shí)現(xiàn)購物車整體頁面邏輯詳解
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)購物車的整體頁面邏輯,具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-11-11Android Room數(shù)據(jù)庫自動升級與遷移的策略
在 Android 應(yīng)用開發(fā)中,Room 是 Google 提供的一個輕量級數(shù)據(jù)庫框架,用于簡化與 SQLite 的交互,本文將介紹 Room 數(shù)據(jù)庫升級的幾種場景和常見的處理方法,包括手動遷移和自動遷移的策略,需要的朋友可以參考下2024-09-09Android如何判斷一個點(diǎn)在不在多邊形區(qū)域內(nèi)
這篇文章主要為大家詳細(xì)介紹了Android判斷一個點(diǎn)在不在多邊形區(qū)域內(nèi)的方法,具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-05-05Android?nonTransitiveRClass資源沖突問題淺析
這篇文章主要介紹了Android?nonTransitiveRClass資源沖突問題,別搞錯了,nonTransitiveRClass不能解決資源沖突,需要的朋友們下面隨著小編來一起學(xué)習(xí)吧2022-12-12Android中將一個圖片切割成多個圖片的實(shí)現(xiàn)方法
有種場景,我們想將一個圖片切割成多個圖片。比如我們在開發(fā)一個拼圖的游戲,就首先要對圖片進(jìn)行切割2013-05-05