手把手帶你玩轉 DialogFragment

前言

本文已經收錄到個人 Github 我的博客,歡迎大佬們光臨寒舍:android

個人 GIthub 博客ios

思惟導圖

1、爲何要學習 DialogFragment

你還在用 Dialog 嗎?git

你還在常常煩惱於屏幕翻轉的時候,Dialog 的各類奇葩狀況嗎?github

你想下降耦合嗎?web

若是你有其中的一個煩惱,那麼恭喜你,碰見了 DialogFragment ,他恰巧就解決了上面所說的問題,若是感興趣的話,隨筆者來看下吧!網絡

2、背景

Android 官方推薦使用 DialogFragment 來代替 Dialog ,可讓它具備更高的可複用性(下降耦合)和更好的便利性(很好的處理屏幕翻轉的狀況)。多線程

而建立 DialogFragment 有兩種方式:異步

  • 法一:覆寫其 onCreateDialog 方法

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

  • 法二:覆寫其 onCreateView 方法

通常用於建立複雜內容彈窗全屏展現效果的場景,UI 複雜功能複雜通常有網絡請求等異步操做ide

3、應用

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;  } });  複製代碼

4、結語

終於看完了鴨!累死鴨了!若是還有什麼不是很清楚的話,能夠看下筆者寫的示例 Demo


若是文章對您有一點幫助的話,但願您能點一下贊,您的點贊,是我前進的動力

本文參考連接:

本文使用 mdnice 排版

相關文章
相關標籤/搜索