本文已經收錄到個人 Github 我的博客,歡迎大佬們光臨寒舍:android
個人 GIthub 博客ios
你還在用 Dialog
嗎?git
你還在常常煩惱於屏幕翻轉的時候,Dialog
的各類奇葩狀況嗎?github
你想下降耦合嗎?web
若是你有其中的一個煩惱,那麼恭喜你,碰見了 DialogFragment
,他恰巧就解決了上面所說的問題,若是感興趣的話,隨筆者來看下吧!網絡
Android 官方推薦使用 DialogFragment
來代替 Dialog
,可讓它具備更高的可複用性(下降耦合)和更好的便利性(很好的處理屏幕翻轉的狀況)。多線程
而建立 DialogFragment
有兩種方式:異步
onCreateDialog
方法
通常用於建立替代傳統的
Dialog
對話框的場景,UI 簡單,功能單一,不適用於使用了多線程(例如網絡請求)的狀況下(由於不能正確的獲取當前Fragment
的狀態,會產生空指針異常)編輯器
onCreateView
方法
通常用於建立複雜內容彈窗或全屏展現效果的場景,UI 複雜,功能複雜,通常有網絡請求等異步操做ide
法一:
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; } 複製代碼
這種狀況,標題內容上面的白色部分,實際上是默認的標題欄,若是須要的話,能夠設置隱藏標題欄(將在下文說到)
若是使用傳統的 Dialog
,須要咱們手動處理屏幕翻轉的狀況,但使用 DialogFragment
的話,則不須要咱們進行任何處理,FragmentManager
會自動管理 DialogFragment
的生命週期。
在基本用法裏代碼註釋有設置主題的地方,下面詳細說下兩種方法下設置無標題欄的方式:
法一:
@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); } 複製代碼
經常使用的形式大可能是寬度上和屏幕同樣寬,高度自適應,下面直接看代碼:
法一:
@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; } 複製代碼
文章一開始簡單總結了法一 和法二的應用場景,這裏說明下:
Dialog
提供了很是方便的建立方式,可是在使用了多線程(例如網絡請求)的狀況下,不能正確的獲取當前
Fragment
的狀態,會產生空指針異常
View
,更便於應對複雜
UI
的場景
使用回調的方式
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); } }); 複製代碼
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); } 複製代碼
mBtn.setOnClickListener(new View.OnClickListener() {
@Override public void onClick(View view) { IOSDialogFragment fragment = new IOSDialogFragment(); //第二個參數是 tag fragment.show(getSupportFragmentManager(), "android"); } }); 複製代碼
直接對
DecorView
設置onTouchListener
window.getDecorView().setOnTouchListener(new View.OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) { if (event.getAction() == MotionEvent.ACTION_UP) { //彈框消失的動畫執行相關代碼 .... .... } return true; } }); 複製代碼
終於看完了鴨!累死鴨了!若是還有什麼不是很清楚的話,能夠看下筆者寫的示例 Demo
若是文章對您有一點幫助的話,但願您能點一下贊,您的點贊,是我前進的動力
本文參考連接:
本文使用 mdnice 排版