DustinChu Blog

詳細解讀DialogFragment

DialogFragment

版權聲明:轉載請保留原文連結及作者
http://www.cnblogs.com/tianzhijiexian/p/4161811.html

相信看這篇文章的人都應該知道android中的Dialog了吧
Dialog詳解:http://www.cnblogs.com/tianzhijiexian/p/3867731.html
隨著Fragment這個類的引入,Google官方推薦大家使用DialogFragment來代替傳統的Dialog,那麼是不是說我們之前學習的Dialog知識都沒有用處了呢?非也,新的fragment是來方便大家更好的管理和重用Dialog,之前的知識其實都是可以拿來就用的,僅僅需要少許的改變。

Dialog和DialogFragment的區別和優劣

新來的DialogFragment讓dialog也變成了碎片,相比之前來說應該做了很多優化和處理,對於程序員來看對話框現在更加具體了,就是一個activity上的一個fragment,
我們也可以用fragment的知識來管理對話框了。我們看看之前是怎麼運用對話框對象的

1
2
3
4
AlertDialog dialog = new AlertDialog.Builder( this )
.setTitle( "Dialog" )
.setMessage( "thisis a dialog" )
.show();

如果這個時候屏幕方向發生變化,就會導致Activity重建,然後之前顯示的對話框就不見了。查看log可以發現這個錯誤:

04-1917:30:06.999: E/WindowManager(14495): Activitycom.example.androidtest.MainActivity has leaked windowcom.android.internal.policy.impl.PhoneWindow$DecorView{42ca3c18 V.E…..R……. 0,0-1026,414} that was originally added here

當然我們也可以無視這個錯誤,因為程序不會因此崩潰(看來android本身就已經預料到這種情況了)。

如果我們想要在旋轉屏幕的時候也能保證這個對話框顯示就需要做一定的處理了,
在activity要銷毀前設立一個標誌,看這時對話框是否是顯示狀態,
如果是那麼activity在下次建立時直接顯示對話框。

在onSaveInstanceState中

1
2
3
4
5
6
7
@Override
protected void onSaveInstanceState(Bundle outState) {
super .onSaveInstanceState(outState);
if (dialog != null && dialog.isShowing()) {
outState.putBoolean( "DIALOG_SHOWN", true );
}
}

在onCreat中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Override
protected void onCreate(Bundle savedInstanceState) {
super .onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (savedInstanceState != null ) {
boolean isShown = savedInstanceState.getBoolean("DIALOG_SHOWN" );
if (isShown) {
AlertDialog dialog = new AlertDialog.Builder( this ).setTitle("Dialog" )
.setMessage( "thisis a dialog" ).show();
}
}
……
}

使用DialogFragment來管理對話框就不會有這種問題了,代碼也少了很多的邏輯處理。
當你旋轉屏幕的時候,fragmentManager會自定管理DialogFragment的生命週期,
如果當前已經顯示在屏幕上了,那麼旋轉屏幕後夜會自動顯示,下面就是在屏幕旋轉時的log輸出。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
4-1917:45:41.289: D/==========(16156): MyDialogFragment : onAttach
04-1917:45:41.299: D/==========(16156): MyDialogFragment : onCreate
04-1917:45:41.299: D/==========(16156): MyDialogFragment : onCreateView
04-1917:45:41.309: D/==========(16156): MyDialogFragment : onStart
04-1917:45:50.619: D/==========(16156): MyDialogFragment : onStop
04-1917:45:50.619: D/==========(16156): third activity on destroy
04-1917:45:50.619:D/==========(16156): MyDialogFragment : onDestroyView
04-1917:45:50.619: D/==========(16156): MyDialogFragment : onDetach
04-1917:45:50.639: D/==========(16156): MyDialogFragment : onAttach
04-1917:45:50.639: D/==========(16156): MyDialogFragment : onCreate
04-1917:45:50.659: D/==========(16156): MyDialogFragment : onCreateView
04-1917:45:50.659: D/==========(16156): MyDialogFragment : onStart

Ok,當然你可以不以為然,你說我的應用就是豎著用的,旋轉屏幕畢竟是小概率事件,
誰會開著對話框旋轉來旋轉去啊。那麼相信下面的好處你一定不能否定吧。

我們之前用Dialog的時候,在activity中必須要建立這個對象,
而且一般我們都是需要給它放監聽器的,比如下面的代碼:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Override
protected void onCreate(Bundle savedInstanceState) {
super .onCreate(savedInstanceState);
setContentView(R.layout.guid_main); new AlertDialog.Builder(GuideActivity. this ).setTitle("用戶申明" )
.setMessage(getResources().getString(R.string.statement))
.setPositiveButton( "我同意", new positiveListener())
.setNegativeButton( "不同意", new negativeListener())
.setCancelable( false )
.show();
} private class positiveListener implements DialogInterface.OnClickListener {
@Override
public void onClick(DialogInterface dialog, int which) {
prefs.setIsFirstTime( false );
}
}
private class negativeListener implements DialogInterface.OnClickListener {
@Override
public void onClick(DialogInterface dialog, int which) {
Util.virtualHome(GuideActivity. this );
}
}

你會發現這麼長的代碼很破壞activity中的邏輯性,有木有!!!
在activity中我們處理的各種控件的顯示和邏輯,但對於dialog這種不屬於activity並且建立和處理邏輯都自成一體的東西,
我們為什麼要在activity中建立呢?而且為了方便重用,
我們在實際過程中基本都會建立一個dialog的工具類來做處理,
所以為什麼不用DialogFragment來實現呢?如果通過它來實現,我們就能很方便的進行管理對話框。

此外,當旋轉屏幕和按下後退鍵時可以更好的管理其聲明周期,它和Fragment有著基本一致的聲明周期。且DialogFragment也允許開發者把Dialog作為內嵌的組件進行重用,
類似Fragment(可以在大屏幕和小屏幕顯示出不同的效果)。
有可能我們在大屏幕上就不需要彈出一個對話框了,直接內嵌在activity界面中顯示即可。這點也很贊!

DialogFragment的最簡單用法

使用DialogFragment很簡單,甚至比用Fragment還簡單,因為在api中已經實現了fragment切換對象了。

建立一個fragment對象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.kale.dialogfragmenttest;
import android.app.DialogFragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
public class MyDialogFragment extends DialogFragment{
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
System.out.println( "tag = "+ getTag()); // tag which is from acitivity which started this fragment
return inflater.inflate(R.layout.dialog, null );
}
}

我們建立了一個fragment,讓他繼承了DialogFragment,在onCreatView中通過佈局文件建立了一個view,這和fragment完全一致。

佈局文件如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<? 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"
android:padding ="16dp" >
< TextView
android:id ="@+id/textView1"
android:layout_width ="wrap_content"
android:layout_height ="wrap_content"
android:text ="Large Text"
android:textAppearance ="?android:attr/textAppearanceLarge" />
< EditText
android:layout_marginTop ="10dp"
android:id ="@+id/editText1"
android:layout_width ="match_parent"
android:layout_height ="wrap_content"
android:ems ="10" >
< requestFocus />
</ EditText >
< Button
android:id ="@+id/button1"
android:layout_marginTop ="10dp"
android:layout_gravity ="center_horizo​​ntal"
android:layout_width ="wrap_content"
android:layout_height ="wrap_content"
android:text ="Button" />
</ LinearLayout >

在activity中啟用這個dialog

1
2
3
4
5
6
7
8
@Override
protected void onCreate(Bundle savedInstanceState) {
super .onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new DialogFragmentTest()
.show(getFragmentManager(), "dialog_fragment" );
}

很像Dialog吧,也是支持鍊式編程的。這裡面的參數:

① 一個fragmentManager,在低版本中用getSupport來獲取;
② 一個tag(String)通過這個tag可以告訴fragment是誰啟動了它,
當然這僅僅是這個tag的一種使用方式啦。在fragment中可以通過getTag()方法來獲取這個tag

這裡多說一句,在一年前我還一直說要兼容要兼容,不兼容的demo是很不負責任的,
但是現在來看,低版本的用戶真的很少很少了,而且這些低版本的用戶已經不能是我們的主流用戶了,
所以在2014年末,我可以負責任的說,可以不用兼容2.x的系統了。我之前寫過很多兼容的文章,actionbar啊,
對話框的兼容啊,但現在都變得無所謂了,其實任何事物的發展都是如此。很多之前很重要的技術,
在新的發展中已經慢慢變得無足輕重了,但我們之前為之付出的東西卻不是無價值的。
一個原因是為自己之前的工作找到價值,一種是在那段時光中我們慢慢體會到了很多東西,
這些東西就是我們的閱歷也是一種談資。

好,閒話少敘,下面是運行效果:

有人會說,上面的那個空白的title好醜,我想去掉。當然可以,這就是fragment的好處,用這個方法:

getDialog().requestWindowFeature(Window.FEATURE_NO_TITLE);

1
2
3
4
5
6
7
8
9
10
public class MyDialogFragment extends DialogFragment{
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
System.out.println( "tag = "+ getTag());
getDialog().requestWindowFeature(Window.FEATURE_NO_TITLE);
return inflater.inflate(R.layout.dialog, null );
}
}

現在它變成了這個樣子:

所以你可以看到,任何改變都是要付出代價的,如果你還是像之前一樣用match_parent來製定控件的寬度,
那麼就是這種結果。可以說那個title欄就是一個房梁,
支撐著對話框的寬度,沒了它就只能自適應了。解決辦法就是自定義控件的寬度,寫個幾百dp啥的,沒任何技術難度。

注意:

如果你的DialogFragment是Activity的內部類,必須將DialogFragment定義為靜態的。否則會報錯!!!

1
2
3
4
5
6
7
8
9
public static class DialogFragmentTest extends DialogFragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// TODO自動生成的方法存根
return inflater.inflate(R.layout.dialog, null );
}
}

DialogFragment啟動、終止過程分析

之前說了,我們沒有像fragment那樣建立一個fragment加載對象進行fragment的加載,也沒有commit,但卻能使用dialogFragment對象,這是為什麼呢?

我們先來回顧下fragment是怎麼使用的。

① 建立FragmentManager對象,用來管理fragment

② 建立fragmentTransaction對象,用來添加和fragment

③ 提交fragment切換(commit)

1
2
3
4
5
6
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction ft = fragmentManager.beginTransaction();
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
ft.add(R.id.container_fragment, new MyFragment())
.commit();

替換fragment的方法

1
2
3
4
getSupportFragmentManager().beginTransaction().
replace(R.id.container_fragment02, fragment)
.addToBackStack( null )
.commit();

現在,我們通過源碼來分析下DialogFragment的啟動方式

我們在使用它的時候沒有去用fragmentTransaction對象,也沒有執行add,也沒有執行commit,
僅僅提供了一個fragmentManager,那麼它是怎麼被添加的呢?我們知道這個對話框是用show方法顯示的,
那麼就來看看這個方法吧。

DialogFragment源碼:

1.show()

1
2
3
4
5
6
7
public void show(FragmentManager manager, String tag){
mDismissed = false ;
mShownByMe = true ;
FragmentTransaction ft = manager.beginTransaction(); // creat a fragmentTransaction
ft.add( this , tag); // add fragment with tag
ft.commit();
}

真相大白,api自動給你實現了一個fragment切換的對象,而且在show的時候就已經add了fragment,所以沒有任何問題~

在add方法中沒有提供容器的id,所以表示是加載到當前activity中的,在添加後也的確調用了commit
方法3

show()的另一種形式

1
2
3
4
5
6
7
8
public int show(FragmentTransaction transaction, String tag) {
mDismissed = false ;
mShownByMe = true ;
transaction.add( this , tag);
mViewDestroyed = false ;
mBackStackId = transaction.commit();
return mBackStackId;
}

上面的show方法傳入的是一個fragmentTransaction對象,這個也很容易理解。
我們之前傳入fragmentManager對象的目的就是生成這個fragmentTransaction對象,
這回我們可以在傳入一個已經配置好的fragmentTransaction對象,大大增加了可定制性。
所以api的製訂也是大神們心血的結晶啊。

dimiss()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
**
* Dismiss the fragment and its dialog. If the fragment was added to the
* back stack, all back stack state up to and including this entry will
* be popped. Otherwise, a new transaction will be committed to remove
* the fragment.
*/
public void dismiss() {
dismissInternal( false );
}
void dismissInternal( boolean allowStateLoss) {
if (mDismissed) {
return ;
}
mDismissed = true ;
mShownByMe = false ;
if (mDialog != null ) {
mDialog.dismiss();
mDialog = null ;
}
mViewDestroyed = true ;
if (mBackStackId >= 0 ) {
getFragmentManager().popBackStack(mBackStackId,
FragmentManager.POP_BACK_STACK_INCLUSIVE);
mBackStackId = -1 ;
} else {
FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.remove( this );
if (allowStateLoss) {
ft.commitAllowingStateLoss();
} else {
ft.commit();
}
}
}

我們知道瞭如果一個DialogFragment關閉的時候會檢查堆棧裡面有沒有其他的對象,
如果有就pop出來,如果沒有就直接remove和commit。也就是說:如果back stack堆棧有該Dialog,
將其pop出來,否則ft.remove(this); ft.commit();。估計pop的操作也包含ft.remove()和ft.commit()。
調用dismiss()會觸發onDismiss()回調函數。

跟踪狀態,如下:

通過onCreateView()來建立對話框佈局

上面的例子中我們已經在onCreateView()建立的對話框佈局,這時fragment中建立佈局的傳統寫法,
很適合用於自定義的對話框,我們可以修改任何的東西,包括對話框的style。
上面的例子中我們已經乾掉了對話框上面title的區域,而我們也沒發現可以設置標題的方法,
感覺上面那個標題欄就是個標題黨,毫無意義(之後會說到這塊區域的用處) 。

我們在onCreat中可以設置對話框的風格和各種屬性,但是千萬別設置關於view的東西,
因為這時候對話框還沒建立呢,有關於view的東西在onCreatView中去設置吧,
這裡我簡單設置了一個button的點擊事件——關閉對話框

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class MyDialogFragment extends DialogFragment{
@Override
public void onCreate(Bundle savedInstanceState) {
super .onCreate(savedInstanceState);
// 如果setCancelable()中參數為true,若點擊dialog覆蓋不到的activity的空白或者按返回鍵,
// 則進行cancel,狀態檢測依次onCancel()和onDismiss()。如參數為false,則按空白處或返回鍵無反應。缺省為true
setCancelable( true );
// 可以設置dialog的顯示風格
// setStyle(style,theme);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// getDialog().requestWindowFeature(Window.FEATURE_NO_TITLE);
View rootView = inflater.inflate(R.layout.dialog, null );
Button btn = (Button)rootView.findViewById(R.id.button);
btn.setOnClickListener( new OnClickListener() {
@Override
public void onClick(View v) {
// TODO自動生成的方法存根
dismiss();
}
});
return rootView;
}
}

補充:實現信息保存

在activity橫豎屏切換的時候,dialog現在可以自動重建了,如果你在editText中輸入了信息,在重建的時候會不會保留之前的呢?在4.2和4.4中對話框人性化的自定保存了之前輸入的內容,我們無須手動處理。但如果你測試的手機被奇葩的定制了,那就乖乖的保存數據吧。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class MyDialogFragment extends DialogFragment {
EditText editText;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.dialog, null );
editText = (EditText) rootView.findViewById(R.id.editText);
if (savedInstanceState != null ) {
CharSequence text = savedInstanceState.getCharSequence( "input data" );
editText.setText(text == null ? "" : text);
}
return rootView;
}
@Override
public void onSaveInstanceState(Bundle outState) {
outState.putCharSequence( "input data" , editText.getText());
super .onSaveInstanceState(outState);
}
}

通過onCreateDialog()來快捷的建立對話框

我們上面建立的對話框都是用自定義佈局的,難道我們之前學過的dialog知識都沒用了麼?
我們如果沒自定義對話框的需求,怎麼辦?就沒有一種快一點的方式來建立對話框麼?
快用onCreatDialog吧!!!這個回調方法是DialogFragment獨有的,通過它返回的是一個Dialog對象,
這個對象就會被顯示到屏幕上。千萬別同時使用onCreatView和onCreatDialog方法,
他們僅僅是為了完成同樣一個目的的兩條路而已。

PS:從生命週期的順序而言,先執行onCreateDialog(),後執行onCreateView()

我在onCreatDialog建立一個警告對話框的builder,通過這個builder的create()方法來生成一個AlertDialog對象,因為AlertDialog是Dialog的子類,所以可以直接返回給Dialog。
這裡可以用其他不同對話框的builder,代碼類似,
只不過就是通過builder的creat()方法返回的是不同的對象而已。builder模式也是蠻巧妙的~

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class MyDialogFragment extends DialogFragment implements android.content.DialogInterface.OnClickListener{
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
Builder builder = new AlertDialog.Builder(getActivity());
builder.setTitle( "用戶申明" )
.setMessage(getResources().getString(R.string.hello_world))
.setPositiveButton( "我同意", this )
.setNegativeButton( "不同意", this )
.setCancelable( false );
//.show(); // show cann't be use here
return builder.create();
}
@Override
public void onClick(DialogInterface dialog, int which) {
// TODO自動生成的方法存根
}
}

顯示效果:

看到了麼,這裡的標題欄終於有用了,原來那個標題欄是為了給我們在這裡用的啊~

注意:

① 因為這裡創建的是一個dialog,所以用的onclickListener自然是對話框中的listener了。

② 千萬別在構建對話框對象的時候順手寫了show()方法,我們現在是在fragment中初始化一個對話框,
真正讓他顯示的時候是在activity中用這個dialogFragment對象顯示的。
如果這裡寫了show方法不會報錯,但是會出現兩個對話框!

那麼,我們能不能在這裡自定義對話框呢?當然可以啦,
本身Dialog.builder就提供了自定義view的方法,和之前用Dialog一樣自定義下viwe就搞定了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Override
public Dialog onCreateDialog(Bundle savedInstanceState)
{
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
// Get the layout inflater
LayoutInflater inflater = getActivity().getLayoutInflater();
View view = inflater.inflate(R.layout.fragment_login_dialog, null );
// Inflate and set the layout for the dialog
// Pass null as the parent view because its going in the dialog layout
builder.setView(view) // set your own view
// Add action buttons
.setPositiveButton("Sign in" ,
new DialogInterface.OnClickListener()
{
@Override
public void onClick(DialogInterface dialog, int id)
{
}
}).setNegativeButton( "Cancel", null );
return builder.create();
}

這裡貼下我在另一篇文章的自定義對話框view的代碼片段:

詳細看這裡:http://www.cnblogs.com/tianzhijiexian/p/3867731.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
/**
* 自定義視圖對話框
*
* @param title
*/
public void viewDialog(String title) {
// LayoutInflater是用來找layout文件夾下的xml佈局文件,並且實例化
LayoutInflater factory = LayoutInflater.from(mContext);
// 把activity_login中的控件定義在View中
View view = factory.inflate(R.layout.dialog_layout, null );
// 將LoginActivity中的控件顯示在對話框中
// 獲取用戶輸入的“用戶名”,“密碼”
// 注意:view.findViewById很重要,因為上面factory.inflate(R.layout.activity_login,
// null)將頁面佈局賦值給了view了
TextView titleTv = (TextView) view
.findViewById(R.id.dialog_textView_id);
titleTv.setText(title);
Button btn = (Button) view.findViewById(R.id.dialog_logout_button_id);
btn.setOnClickListener( new View.OnClickListener() {
@Override
public void onClick(View arg0) {
showToast( "按下自定義視圖的按鈕了~" );
}
});
builder
// 設定顯示的View
.setView(view);
// 設置dialog是否為模態,false表示模態,true表示非模態
// ab.setCancelable(false);
// 對話框的創建、顯示,這裡顯示的位置是在屏幕的最下面,但是很不推薦這個種做法,因為距底部有一段空隙
AlertDialog dialog = builder.create();
Window window = dialog.getWindow();
window.setGravity(Gravity.BOTTOM); // 此處可以設置dialog顯示的位置
window.setWindowAnimations(R.style.myAnimationstyle); // 添加動畫
dialog.show();
}

DialogFragment與Activity之前進行通信

思路很簡單,就是定義一個傳輸數據的接口,強制activity實現這個接口,
在fragment需要傳遞數據的時候去調用這個接口的方法,
activity就能在這個方法中得到相應的數據了。這點在之前的fragment傳遞數據中已經介紹過了,

可以參考這篇文章:
http://www.cnblogs.com/tianzhijiexian/p/3888330.html

在真正項目中,fragment的編寫並不需要了解activity的各類方法,
好的編程風格是將fragment所涉及的方法以接口的方式封裝起來,我在此寫一個例子來說明一下。

寫一個接口——DataCallback

1
2
3
4
5
6
package com.kale.dialogfragmenttest;
public interface DataCallback {
public void getData(String data);
}

activity實現這個接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class MainActivity extends Activity implements DataCallback{
@Override
protected void onCreate(Bundle savedInstanceState) {
super .onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new MyDialogFragment()
.show(getFragmentManager(), "dialog_fragment" );
}
@Override
public void getData(String data) {
// TODO自動生成的方法存根
System.out.println("data = "+ data);
}
}

在DialogFragment中使用這個接口

在DialogFragment中使用這個接口,並且用instanceof來看啟動它的activity是否實現了這個接口,如果沒實現就拋出異常。這樣我們就能保證在大型項目中不會出現忘記實現這個接口的問題了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
import android.app.Activity;
import android.app.AlertDialog;
import android.app.AlertDialog.Builder;
import android.app.Dialog;
import android.app.DialogFragment;
import android.content.DialogInterface;
import android.os.Bundle ;
public class MyDialogFragment extends DialogFragment implements
android.content.DialogInterface.OnClickListener {
@Override
public void onAttach(Activity activity) {
// onAttach()是合適的早期階段進行檢查MyActivity是否真的實現了接口。
// 採用接口的方式,dialog無需詳細了解MyActivity,只需了解其所需的接口函數,這是真正項目中應採用的方式。
if (!(activity instanceof DataCallback)) {
throw new IllegalStateException("fragment所在的Activity必須實現Callbacks接口" );
}
super .onAttach(activity);
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
Builder builder = new AlertDialog.Builder(getActivity());
builder.setTitle( "用戶申明" )
.setMessage(getResources().getString(R.string.hello_world))
.setPositiveButton( "我同意", this ).setNegativeButton("不同意", this )
.setCancelable( false );
// show();
return builder.create();
}
@Override
public void onClick(DialogInterface dialog, int which) {
// TODO自動生成的方法存根
DataCallback callback = (DataCallback) getActivity();
callback.getData( "test" );
}
}

此外fragment也可以通過fragment管理器,通過tag,獲取其他fragment實例,
從而進行fragment之間的通信。當然從編程思想的角度看,fragment之間的過多進行交叉調用,不利於程序的管控。

用DialogFragment實現再次彈窗

有時候我們可能有這樣的需求,點擊對話框中的一個按鈕後又彈出一個對話框,這個該怎麼做呢?
首先在點擊事件中將這個對話框在屏幕上移除,然後把這個fragment壓棧,
最後建立一個新的dialogFragment對象,show出來。我們雖然讓這個fragment在屏幕上消失,
但還是可以通過fragment管理器到回退棧中找到它。

二次彈窗的代碼:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
FragmentTransaction ft = getFragmentManager().beginTransaction();
/*
* 如果不執行remove(),對話框即不會進入onDismiss()狀態。會被顯示在新的對話框下方,是可見的。
* 主要考慮美觀的問題,如果下面的對話框大於上面的對話框就很難看了。對於Dialog,container為0或者null。
*/
ft.remove( this );
/*
* 將當前的PromptDialogFragment加入到回退堆棧,當用戶按返回鍵,或者通過按幫助框的Close按鈕dismiss幫助框是,
* 重新顯示提示框。對於back stack的處理,系統具有一定的智能。例如:執行兩次addToStackStack(),實際不會重複壓棧。
* 有例如:註釋掉remove()語句,即提示框不消失,而是在幫助框的下面。
* 但是在實驗中發現是否有addToBackStack()都不會結果有影響,系統能夠分析到對象存在,不需要壓棧。沒有去查源代碼,
* 猜測通過mBackStackId比對來進行智能處理。
*/
ft.addToBackStack( null );
new OhterDialogFragment()
.show(getFragmentManager(), "dialog_fragment");

利用Fragment的特性,為不同屏幕做適配

如果我們想在大屏幕上顯示對話框,而小屏幕中直接把對話框的內容放在activity中顯示呢?

其實也很簡單,本身這個dialogFragment就是一個fragment,所以完全有fragment的特性,你可以用fragmentTranscation將其放到任何佈局中,你也可以用show()方法把它當作dialog顯示出來。
接下來就剩下一個問題了,判斷屏幕大小。

在默認的values下新建一個bools.xml

1
2
3
4
<? xml version="1.0" encoding="utf-8" ?>
< resources >
< bool name ="large_layout" > false </ bool >
</ resources >

然後,在res下新建一個values-large,在values-large下再新建一個bools.xml,
通過加載不同的value就能知道是大屏還是小屏幕啦

1
2
3
4
<? xml version="1.0" encoding="utf-8" ?>
< resources >
< bool name ="large_layout" > true </ bool >
</ resources >

在代碼中進行判斷

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public void showDialogInDifferentScreen(View view)
{
FragmentManager fragmentManager = getFragmentManager();
EditNameDialogFragment newFragment = new EditNameDialogFragment();
boolean mIsLargeLayout = getResources().getBoolean(R.bool.large_layout) ;
Log.e( "TAG", mIsLargeLayout+"" );
if (mIsLargeLayout ) {
// The device is using a large layout, so show the fragment as a
// dialog
newFragment.show(fragmentManager, "dialog" );
} else {
// The device is smaller, so show the fragment fullscreen
FragmentTransaction transaction = fragmentManager.beginTransaction();
// For a little polish, specify a transition animation
transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
// To make it fullscreen , use the 'content' root view as the
// container for the fragment, which is always the root view for the activity
transaction.replace(R.id.id_ly, newFragment).commit();
}
}

參考
http://blog.csdn.net/huangyabin001/article/details/30053835

http://blog.csdn.net/lmj623565791/article/details/37815413

http://www.cnblogs.com/tianzhijiexian/p/4161811.html

文章標題:詳細解讀DialogFragment

文章作者:Dustinchu

發布時間:2017年01月20日 - 22:01

最後更新:2018年05月03日 - 21:05

原始連結:https://dustinchu.github.io/2017/01/20/詳細解讀DialogFragment/

許可協議: 屬名-非商業性使用-禁止編譯 4.0 國際 轉載請保留原文連結及作者。