還在用Dialog嗎——DialogFragment帶你體驗完美高效率
DialogFragment帶你體驗完美高效率
最近研究了DialogFragment形式的dialog,發現有很多優勢,與普通的dialog一起做了一些比較和總結。
效果如下:
最近學習彈框時發現有三種類型的可供使用,
PopupWindow、dialog,DialogFragment。
比如說需求:
只攔截自身所佔空間部分的事件,其餘空間的點選事件不處理
可以根據改變View的佈局排列方式,View是否設定底部背景及居中方式
雖然在功能上 PopupWindow 更符合需要,dialog也能做到,但是使用 DialogFragment 程式碼更簡潔、更方便封裝功能模組。
DialogFragment
基於Fragment的DialogFragment。
從程式碼的編寫角度看,Dialog使用起來要更為簡單
Android 官方推薦使用 DialogFragment 來代替 Dialog ,可以讓它具有更高的可複用性(降低耦合)和更好的便利性(很好的處理螢幕翻轉的情況)。
DialogFragment果然有一個非常好的特性(在手機配置變化,導致Activity需要重新建立時
- 例如旋屏,基於DialogFragment的對話方塊將會由FragmentManager自動重建,然而基於Dialog實現的對話方塊則沒有這樣的能力)。
下面是兩段例項程式碼:
他們使用的介面都一樣:(dialog.xml)
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/ic_launcher" /> </LinearLayout>
1.基於Dialog實現的對話方塊
public class MainActivity extends Activity {
private Button clk;
private Dialog dialog;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
clk = (Button) findViewById(R.id.clk);
dialog = new Dialog(this);
dialog.setContentView(R.layout.dialog);
clk.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
dialog.show();
}
});
}
}
當我們點選按鈕時,會彈出對話方塊(內容為android logo),當我們旋轉屏幕後,Activity重新建立,整個Activity的介面沒有問題,而對話方塊消失了。
除此之外,其實還有一個問題,就是在logcat中會看到異常資訊:Android..leaked .. window,這是因為在Activity結束之前,Android要求所有的Dialog必須要關閉。
我們旋屏後,Activity會被重建,而上面的程式碼邏輯並沒有考慮到對話方塊的狀態以及是否已關閉。
優化dialog程式碼修改為:
public class MainActivity extends Activity {
private Button clk;
private Dialog dialog;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
clk = (Button) findViewById(R.id.clk);
dialog = new Dialog(this);
dialog.setContentView(R.layout.dialog);
clk.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
dialog.show();
}
});
//使用者恢復對話方塊的狀態
if(savedInstanceState != null && savedInstanceState.getBoolean("dialog_show"))
clk.performClick();
}
/**
* 用於儲存對話方塊的狀態以便恢復
*/
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
if(dialog != null && dialog.isShowing())
outState.putBoolean("dialog_show", true);
else
outState.putBoolean("dialog_show", false);
}
/**
* 在Activity銷燬之前,確保對話方塊以關閉
*/
@Override
protected void onDestroy() {
super.onDestroy();
if(dialog != null && dialog.isShowing())
dialog.dismiss();
}
}
2. 基於DialogFragment的對話方塊
與上面的對話方塊使用同樣的介面佈局,此處僅僅展現一個簡單對話方塊,因此只重寫了onCreateView方法
public class MyDialogFragment extends DialogFragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.dialog, container, false);
return v;
}
}
public class MainActivity extends FragmentActivity {
private Button clk;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
clk = (Button) findViewById(R.id.clk);
clk.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
MyDialogFragment mdf = new MyDialogFragment();
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
mdf.show(ft, "df");
}
});
}
}
這兩段程式碼可以實現第一種方式的同樣功能,此處我們並沒有去關心對話方塊的重建,以及Activity銷燬前對話方塊是否已關閉,這一切都是由FragmentManager來管理。
其實DialogFragment還擁有fragment的優點,即可以在一個Activity內部實現回退(因為FragmentManager會管理一個回退棧)
建立:
建立 DialogFragment 有兩種方式:
覆寫其 onCreateDialog 方法
- *應用場景*:一般用於建立替代傳統的 Dialog 對話方塊的場景,UI 簡單,功能單一。
方法
- *應用場景*:一般用於建立替代傳統的 Dialog 對話方塊的場景,UI 簡單,功能單一。
覆寫其 onCreateView 方法
- *應用場景*:
一般用於建立複雜內容彈窗或全屏展示效果的場景,UI 複雜,功能複雜,一般有網路請求等非同步操作。
- *應用場景*:
覆寫其 onCreateDialog
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
//為了樣式統一和相容性,可以使用 V7 包下的 AlertDialog.Builder
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();
}
自定義 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();
}
處理螢幕翻轉
如果使用傳統的 Dialog ,需要我們手動處理螢幕翻轉的情況
但使用 DialogFragment 的話,則不需要我們進行任何處理, FragmentManager 會自動管理 DialogFragment 的生命週期。
無標題欄
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
LayoutInflater inflater = getActivity().getLayoutInflater();
View view = inflater.inflate(R.layout.fragment_dialog, null);
Dialog dialog = new Dialog(getActivity(), R.style.CustomDialog);
// 關閉標題欄,setContentView() 之前呼叫
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
dialog.setContentView(view);
dialog.setCanceledOnTouchOutside(true);
return dialog;
}
對於在onCreateView裡
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
/**
* setStyle() 的第一個引數有四個可選值:
* STYLE_NORMAL|STYLE_NO_TITLE|STYLE_NO_FRAME|STYLE_NO_INPUT
* 其中 STYLE_NO_TITLE 和 STYLE_NO_FRAME 可以關閉標題欄
* 每一個引數的詳細用途可以直接看 Android 原始碼的說明
*/
setStyle(DialogFragment.STYLE_NO_TITLE, R.style.CustomDialog);
}
實現全屏(寬/高度全屏)
// 設定寬度為屏寬、位置靠近螢幕底部
Window window = dialog.getWindow();
window.setBackgroundDrawableResource(R.color.transparent);
WindowManager.LayoutParams wlp = window.getAttributes();
wlp.gravity = Gravity.BOTTOM;
wlp.width = WindowManager.LayoutParams.MATCH_PARENT;
wlp.height = WindowManager.LayoutParams.WRAP_CONTENT;
window.setAttributes(wlp);
部分程式碼如下:
/**
* 類功能描述:</br>
* DialogFragment主測試類
* @author 於亞豪
* 部落格地址: http://blog.csdn.net/androidstarjack
* 公眾號: 終端研發部
* @version 1.0 </p> 修改時間:</br> 修改備註:</br>
*/
public class MainActivity extends AppCompatActivity {
private Dialog dialog;
@Bind(R.id.btn01)
Button btn01;
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
MyShouDialogFramgment mdf;
MyShouDialogFramgment2 mdf2 = new MyShouDialogFramgment2();
FragmentTransaction ft2 = getSupportFragmentManager().beginTransaction();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
dialog = new Dialog(this);
dialog.setContentView(R.layout.fragment_dialog);
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
ft2.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
mdf = new MyShouDialogFramgment();
//使用者恢復對話方塊的狀態
if(savedInstanceState != null && savedInstanceState.getBoolean("dialog_show"))
btn01.performClick();
}
/**
* 用於儲存對話方塊的狀態以便恢復
*/
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
if(dialog != null && dialog.isShowing())
outState.putBoolean("dialog_show", true);
else
outState.putBoolean("dialog_show", false);
}
/**
* 在Activity銷燬之前,確保對話方塊以關閉
*/
@Override
protected void onDestroy() {
super.onDestroy();
if(dialog != null && dialog.isShowing())
dialog.dismiss();
}
@OnClick({R.id.btn01,R.id.btn02,R.id.btn03})
public void onClick(View view){
switch (view.getId()){
case R.id.btn01:
dialog.show();
break;
case R.id.btn02:
mdf.show(ft, "df");
//可以拓展
break;
case R.id.btn03:
mdf2.show(ft2, "df");
//可以拓展
break;
}
}
}
*MyShouDialogFramgment2.Java如下*
public class MyShouDialogFramgment2 extends DialogFragment {
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
return super.onCreateView(inflater, container, savedInstanceState);
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
//為了樣式統一和相容性,可以使用 V7 包下的 AlertDialog.Builder
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
LayoutInflater inflater = getActivity().getLayoutInflater();
View view = inflater.inflate(R.layout.fragment_dialog, null);
Button btn_cancle = (Button) view.findViewById(R.id.btn_cancle);
Button btn_ok = (Button) view.findViewById(R.id.btn_ok);
final Dialog dialog = new Dialog(getActivity(), R.style.activity_DialogTransparent);
// 關閉標題欄,setContentView() 之前呼叫
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
dialog.setContentView(view);
dialog.setCanceledOnTouchOutside(true);
btn_cancle.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
dialog.dismiss();
}
});
btn_ok.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
GetToast.useString(getActivity(),"今晚開始搞事情了...");
}
});
return dialog;
}
}
總結
可以讓DialogFragment的使用像Dialog一樣的簡單、靈活,同時也保持了DialogFragment的優點,可以在任何的類中使用
- 很簡單的新增新型別的Dialog
利用Fragment的特性,為不同螢幕做
下載連線
部落格地址
#### 相信自己,沒有做不到的,只有想不到的
如果你覺得此文對您有所幫助,歡迎入群 QQ交流群 :232203809
微信公眾號:終端研發部
(這裡 學到的不僅僅是技術)