Android 官方推薦 : DialogFragment 怎樣創建對話框
1、 概述
DialogFragment在android 3.0時被引入。是一種特殊的Fragment,用於在Activity的內容之上展示一個模態的對話框。典型的用於:展示警告框,輸入框,確認框等等。
在DialogFragment產生之前,我們創建對話框:一般采用AlertDialog和Dialog。注:官方不推薦直接使用Dialog創建對話框。
2、 好處與用法
使用DialogFragment來管理對話框,當旋轉屏幕和按下後退鍵時可以更好的管理其聲明周期,它和Fragment有著基本一致的聲明周期。且DialogFragment也允許開發者把Dialog作為內嵌的組件進行重用,類似Fragment(可以在大屏幕和小屏幕顯示出不同的效果)。上面會通過例子展示這些好處~
使用DialogFragment至少需要實現onCreateView或者onCreateDIalog方法。onCreateView即使用定義的xml布局文件展示Dialog。onCreateDialog即利用AlertDialog或者Dialog創建出Dialog。
3、 重寫onCreateView創建Dialog
a)布局文件,我們創建一個設置名稱的布局文件:
[html]view plaincopy
-
- android:layout_width="wrap_content"
- android:layout_height="wrap_content">
-
- android:id="@+id/id_label_your_name"
- android:layout_width="wrap_content"
- android:layout_height="32dp"
- android:gravity="center_vertical"
- android:text="Yourname:"/>
-
- android:id="@+id/id_txt_your_name"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_toRightOf="@id/id_label_your_name"
- android:imeOptions="actionDone"
- android:inputType="text"/>
-
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentRight="true"
- android:layout_below="@id/id_txt_your_name"
- android:text="ok"/>
-
-
b)繼承DialogFragment,重寫onCreateView方法
[java]view plaincopy
- packagecom.example.zhy_dialogfragment;
-
- importandroid.app.DialogFragment;
- importandroid.os.Bundle;
- importandroid.view.LayoutInflater;
- importandroid.view.View;
- importandroid.view.ViewGroup;
-
- publicclassEditNameDialogFragmentextendsDialogFragment
- {
-
-
- @Override
- publicViewonCreateView(LayoutInflaterinflater,ViewGroupcontainer,
- BundlesavedInstanceState)
- {
- Viewview=inflater.inflate(R.layout.fragment_edit_name,container);
- returnview;
- }
-
- }
c)測試運行:
Main方法中調用:
[java]view plaincopy
- publicvoidshowEditDialog(Viewview)
- {
- EditNameDialogFragmenteditNameDialog=newEditNameDialogFragment();
- editNameDialog.show(getFragmentManager(),"EditNameDialog");
- }
效果圖:
可以看到,對話框成功創建並顯示出來,不過默認對話框有個討厭的標題,我們怎麼去掉呢:可以在onCreateView中調用getDialog().requestWindowFeature(Window.FEATURE_NO_TITLE);即可去掉。即:
[java]view plaincopy
- publicclassEditNameDialogFragmentextendsDialogFragment
- {
-
- @Override
- publicViewonCreateView(LayoutInflaterinflater,ViewGroupcontainer,
- BundlesavedInstanceState)
- {
- getDialog().requestWindowFeature(Window.FEATURE_NO_TITLE);
- Viewview=inflater.inflate(R.layout.fragment_edit_name,container);
- returnview;
- }
-
- }
效果圖:
很完美的去掉了討厭的標題。
4、 重寫onCreateDialog創建Dialog
在onCreateDialog中一般可以使用AlertDialog或者Dialog創建對話框,不過既然google不推薦直接使用Dialog,我們就使用AlertDialog來創建一個登錄的對話框。
a)布局文件
[html]view plaincopy
-
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:orientation="vertical">
-
- android:layout_width="match_parent"
- android:layout_height="64dp"
- android:background="#FFFFBB33"
- android:contentDescription="@string/app_name"
- android:scaleType="center"
- android:src="@drawable/title"/>
-
- android:id="@+id/id_txt_username"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginBottom="4dp"
- android:layout_marginLeft="4dp"
- android:layout_marginRight="4dp"
- android:layout_marginTop="16dp"
- android:hint="inputusername"
- android:inputType="textEmailAddress"/>
-
- android:id="@+id/id_txt_password"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginBottom="16dp"
- android:layout_marginLeft="4dp"
- android:layout_marginRight="4dp"
- android:layout_marginTop="4dp"
- android:fontFamily="sans-serif"
- android:hint="inputpassword"
- android:inputType="textPassword"/>
-
-
b)繼承DialogFragment重寫onCreateDialog方法
[java]view plaincopy
- packagecom.example.zhy_dialogfragment;
-
- importandroid.app.AlertDialog;
- importandroid.app.Dialog;
- importandroid.app.DialogFragment;
- importandroid.content.DialogInterface;
- importandroid.os.Bundle;
- importandroid.view.LayoutInflater;
- importandroid.view.View;
- importandroid.view.ViewGroup;
- importandroid.widget.EditText;
-
- publicclassLoginDialogFragmentextendsDialogFragment
- {
-
- @Override
- publicDialogonCreateDialog(BundlesavedInstanceState)
- {
- AlertDialog.Builderbuilder=newAlertDialog.Builder(getActivity());
- //Getthelayoutinflater
- LayoutInflaterinflater=getActivity().getLayoutInflater();
- Viewview=inflater.inflate(R.layout.fragment_login_dialog,null);
- //Inflateandsetthelayoutforthedialog
- //Passnullastheparentviewbecauseitsgoinginthedialoglayout
- builder.setView(view)
- //Addactionbuttons
- .setPositiveButton("Signin",
- newDialogInterface.OnClickListener()
- {
- @Override
- publicvoidonClick(DialogInterfacedialog,intid)
- {
- }
- }).setNegativeButton("Cancel",null);
- returnbuilder.create();
- }
- }
c)調用
[java]view plaincopy
- publicvoidshowLoginDialog(Viewview)
- {
- LoginDialogFragmentdialog=newLoginDialogFragment();
- dialog.show(getFragmentManager(),"loginDialog");
- }
效果圖:

可以看到通過重寫onCreateDialog同樣可以實現創建對話框,效果還是很nice的。
5、傳遞數據給Activity
從dialog傳遞數據給Activity,可以使用“fragment interface pattern”的方式,下面通過一個改造上面的登錄框來展示這種模式。
改動比較小,直接貼代碼了:
[java]view plaincopy
- packagecom.example.zhy_dialogfragment;
-
- importandroid.app.AlertDialog;
- importandroid.app.Dialog;
- importandroid.app.DialogFragment;
- importandroid.content.DialogInterface;
- importandroid.os.Bundle;
- importandroid.view.LayoutInflater;
- importandroid.view.View;
- importandroid.view.ViewGroup;
- importandroid.widget.EditText;
-
- publicclassLoginDialogFragmentextendsDialogFragment
- {
- privateEditTextmUsername;
- privateEditTextmPassword;
-
- publicinterfaceLoginInputListener
- {
- voidonLoginInputComplete(Stringusername,Stringpassword);
- }
-
- @Override
- publicDialogonCreateDialog(BundlesavedInstanceState)
- {
- AlertDialog.Builderbuilder=newAlertDialog.Builder(getActivity());
- //Getthelayoutinflater
- LayoutInflaterinflater=getActivity().getLayoutInflater();
- Viewview=inflater.inflate(R.layout.fragment_login_dialog,null);
- mUsername=(EditText)view.findViewById(R.id.id_txt_username);
- mPassword=(EditText)view.findViewById(R.id.id_txt_password);
- //Inflateandsetthelayoutforthedialog
- //Passnullastheparentviewbecauseitsgoinginthedialoglayout
- builder.setView(view)
- //Addactionbuttons
- .setPositiveButton("Signin",
- newDialogInterface.OnClickListener()
- {
- @Override
- publicvoidonClick(DialogInterfacedialog,intid)
- {
- LoginInputListenerlistener=(LoginInputListener)getActivity();
- listener.onLoginInputComplete(mUsername
- .getText().toString(),mPassword
- .getText().toString());
- }
- }).setNegativeButton("Cancel",null);
- returnbuilder.create();
- }
- }
拿到username和password的引用,在點擊登錄的時候,把activity強轉為我們自定義的接口:LoginInputListener,然後將用戶輸入的數據返回。
MainActivity中需要實現我們的接口LoginInputListener,實現我們的方法,就可以實現當用戶點擊登陸時,獲得我們的帳號密碼了:
[java]view plaincopy
- c)MainActivity
- packagecom.example.zhy_dialogfragment;
-
- importcom.example.zhy_dialogfragment.LoginDialogFragment.LoginInputListener;
-
- importandroid.app.Activity;
- importandroid.app.AlertDialog;
- importandroid.content.DialogInterface;
- importandroid.os.Bundle;
- importandroid.view.LayoutInflater;
- importandroid.view.View;
- importandroid.widget.Toast;
-
- publicclassMainActivityextendsActivityimplementsLoginInputListener
- {
-
- @Override
- protectedvoidonCreate(BundlesavedInstanceState)
- {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- }
-
-
-
- publicvoidshowLoginDialog(Viewview)
- {
- LoginDialogFragmentdialog=newLoginDialogFragment();
- dialog.show(getFragmentManager(),"loginDialog");
-
- }
-
- @Override
- publicvoidonLoginInputComplete(Stringusername,Stringpassword)
- {
- Toast.makeText(this,"帳號:"+username+",密碼:"+password,
- Toast.LENGTH_SHORT).show();
- }
-
- }
效果:

6、DialogFragment做屏幕適配
我們希望,一個對話框在大屏幕上以對話框的形式展示,而小屏幕上則直接嵌入當前的Actvity中。這種效果的對話框,只能通過重寫onCreateView實現。下面我們利用上面的EditNameDialogFragment來顯示。
EditNameDialogFragment我們已經編寫好了,直接在MainActivity中寫調用
[java]view plaincopy
- publicvoidshowDialogInDifferentScreen(Viewview)
- {
- FragmentManagerfragmentManager=getFragmentManager();
- EditNameDialogFragmentnewFragment=newEditNameDialogFragment();
-
- booleanmIsLargeLayout=getResources().getBoolean(R.bool.large_layout);
- Log.e("TAG",mIsLargeLayout+"");
- if(mIsLargeLayout)
- {
- //Thedeviceisusingalargelayout,soshowthefragmentasa
- //dialog
- newFragment.show(fragmentManager,"dialog");
- }else
- {
- //Thedeviceissmaller,soshowthefragmentfullscreen
- FragmentTransactiontransaction=fragmentManager
- .beginTransaction();
- //Foralittlepolish,specifyatransitionanimation
- transaction
- .setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
- //Tomakeitfullscreen,usethe'content'rootviewasthe
- //container
- //forthefragment,whichisalwaystherootviewfortheactivity
- transaction.replace(R.id.id_ly,newFragment)
- .commit();
- }
- }
可以看到,我們通過讀取R.bool.large_layout,然後根據得到的布爾值,如果是大屏幕則直接以對話框顯示,如果是小屏幕則嵌入我們的Activity布局中
這個R.bool.large_layout是我們定義的資源文件:
在默認的values下新建一個bools.xml
[html]view plaincopy
-
-
-
- false
-
-
然後在res下新建一個values-large,在values-large下再新建一個bools.xml
[html]view plaincopy
-
-
-
- true
-
-
最後測試:

左邊為模擬器,右邊為我的手機~~~~~
7、屏幕旋轉
當用戶輸入帳號密碼時,忽然旋轉了一下屏幕,帳號密碼不見了~~~是不是會抓狂
傳統的new AlertDialog在屏幕旋轉時,第一不會保存用戶輸入的值,第二還會報異常,因為Activity銷毀前不允許對話框未關閉。而通過DialogFragment實現的對話框則可以完全不必考慮旋轉的問題。
我們直接把上面登錄使用AlertDialog創建的登錄框,拷貝到MainActivity中直接調用:
[java]view plaincopy
- publicvoidshowLoginDialogWithoutFragment(Viewview)
- {
- AlertDialog.Builderbuilder=newAlertDialog.Builder(this);
- //Getthelayoutinflater
- LayoutInflaterinflater=this.getLayoutInflater();
-
- //Inflateandsetthelayoutforthedialog
- //Passnullastheparentviewbecauseitsgoinginthedialoglayout
- builder.setView(inflater.inflate(R.layout.fragment_login_dialog,null))
- //Addactionbuttons
- .setPositiveButton("Signin",
- newDialogInterface.OnClickListener()
- {
- @Override
- publicvoidonClick(DialogInterfacedialog,intid)
- {
- //signintheuser...
- }
- }).setNegativeButton("Cancel",null).show();
- }
下面我分別點擊兩種方式創建的登錄框,看效果圖:

可以看到,傳統的Dialog旋轉屏幕時就消失了,且後台log會報異常~~~使用DialogFragment則不受影響。
好了,關於DialogFragment的介紹結束~~~~
有任何疑問請留言
源碼點擊下載
參考文檔:
http://developer.android.com/guide/topics/ui/dialogs.html#DialogFragment
https://github.com/thecodepath/android_guides/wiki/Using-DialogFragment