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的知識來管理對話框了。我們看看之前是怎麼運用對話框對象的
|
|
如果這個時候屏幕方向發生變化,就會導致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中
|
|
在onCreat中
|
|
使用DialogFragment來管理對話框就不會有這種問題了,代碼也少了很多的邏輯處理。
當你旋轉屏幕的時候,fragmentManager會自定管理DialogFragment的生命週期,
如果當前已經顯示在屏幕上了,那麼旋轉屏幕後夜會自動顯示,下面就是在屏幕旋轉時的log輸出。
1234567891011121314151617181920212223 4-1917:45:41.289: D/==========(16156): MyDialogFragment : onAttach04-1917:45:41.299: D/==========(16156): MyDialogFragment : onCreate04-1917:45:41.299: D/==========(16156): MyDialogFragment : onCreateView04-1917:45:41.309: D/==========(16156): MyDialogFragment : onStart04-1917:45:50.619: D/==========(16156): MyDialogFragment : onStop04-1917:45:50.619: D/==========(16156): third activity on destroy04-1917:45:50.619:D/==========(16156): MyDialogFragment : onDestroyView04-1917:45:50.619: D/==========(16156): MyDialogFragment : onDetach04-1917:45:50.639: D/==========(16156): MyDialogFragment : onAttach04-1917:45:50.639: D/==========(16156): MyDialogFragment : onCreate04-1917:45:50.659: D/==========(16156): MyDialogFragment : onCreateView04-1917:45:50.659: D/==========(16156): MyDialogFragment : onStart
Ok,當然你可以不以為然,你說我的應用就是豎著用的,旋轉屏幕畢竟是小概率事件,
誰會開著對話框旋轉來旋轉去啊。那麼相信下面的好處你一定不能否定吧。
我們之前用Dialog的時候,在activity中必須要建立這個對象,
而且一般我們都是需要給它放監聽器的,比如下面的代碼:
|
|
你會發現這麼長的代碼很破壞activity中的邏輯性,有木有!!!
在activity中我們處理的各種控件的顯示和邏輯,但對於dialog這種不屬於activity並且建立和處理邏輯都自成一體的東西,
我們為什麼要在activity中建立呢?而且為了方便重用,
我們在實際過程中基本都會建立一個dialog的工具類來做處理,
所以為什麼不用DialogFragment來實現呢?如果通過它來實現,我們就能很方便的進行管理對話框。
此外,當旋轉屏幕和按下後退鍵時可以更好的管理其聲明周期,它和Fragment有著基本一致的聲明周期。且DialogFragment也允許開發者把Dialog作為內嵌的組件進行重用,
類似Fragment(可以在大屏幕和小屏幕顯示出不同的效果)。
有可能我們在大屏幕上就不需要彈出一個對話框了,直接內嵌在activity界面中顯示即可。這點也很贊!
DialogFragment的最簡單用法
使用DialogFragment很簡單,甚至比用Fragment還簡單,因為在api中已經實現了fragment切換對象了。
建立一個fragment對象
|
|
我們建立了一個fragment,讓他繼承了DialogFragment,在onCreatView中通過佈局文件建立了一個view,這和fragment完全一致。
佈局文件如下:
|
|
在activity中啟用這個dialog
|
|
很像Dialog吧,也是支持鍊式編程的。這裡面的參數:
① 一個fragmentManager,在低版本中用getSupport來獲取;
② 一個tag(String)通過這個tag可以告訴fragment是誰啟動了它,
當然這僅僅是這個tag的一種使用方式啦。在fragment中可以通過getTag()方法來獲取這個tag這裡多說一句,在一年前我還一直說要兼容要兼容,不兼容的demo是很不負責任的,
但是現在來看,低版本的用戶真的很少很少了,而且這些低版本的用戶已經不能是我們的主流用戶了,
所以在2014年末,我可以負責任的說,可以不用兼容2.x的系統了。我之前寫過很多兼容的文章,actionbar啊,
對話框的兼容啊,但現在都變得無所謂了,其實任何事物的發展都是如此。很多之前很重要的技術,
在新的發展中已經慢慢變得無足輕重了,但我們之前為之付出的東西卻不是無價值的。
一個原因是為自己之前的工作找到價值,一種是在那段時光中我們慢慢體會到了很多東西,
這些東西就是我們的閱歷也是一種談資。
好,閒話少敘,下面是運行效果:
有人會說,上面的那個空白的title好醜,我想去掉。當然可以,這就是fragment的好處,用這個方法:
getDialog().requestWindowFeature(Window.FEATURE_NO_TITLE);
|
|
現在它變成了這個樣子:
所以你可以看到,任何改變都是要付出代價的,如果你還是像之前一樣用match_parent來製定控件的寬度,
那麼就是這種結果。可以說那個title欄就是一個房梁,
支撐著對話框的寬度,沒了它就只能自適應了。解決辦法就是自定義控件的寬度,寫個幾百dp啥的,沒任何技術難度。
注意:
如果你的DialogFragment是Activity的內部類,必須將DialogFragment定義為靜態的。否則會報錯!!!
|
|
DialogFragment啟動、終止過程分析
之前說了,我們沒有像fragment那樣建立一個fragment加載對象進行fragment的加載,也沒有commit,但卻能使用dialogFragment對象,這是為什麼呢?
我們先來回顧下fragment是怎麼使用的。
① 建立FragmentManager對象,用來管理fragment
② 建立fragmentTransaction對象,用來添加和fragment
③ 提交fragment切換(commit)
|
|
替換fragment的方法
|
|
現在,我們通過源碼來分析下DialogFragment的啟動方式
我們在使用它的時候沒有去用fragmentTransaction對象,也沒有執行add,也沒有執行commit,
僅僅提供了一個fragmentManager,那麼它是怎麼被添加的呢?我們知道這個對話框是用show方法顯示的,
那麼就來看看這個方法吧。
DialogFragment源碼:
1.show()
|
|
真相大白,api自動給你實現了一個fragment切換的對象,而且在show的時候就已經add了fragment,所以沒有任何問題~
在add方法中沒有提供容器的id,所以表示是加載到當前activity中的,在添加後也的確調用了commit
方法3
show()的另一種形式
|
|
上面的show方法傳入的是一個fragmentTransaction對象,這個也很容易理解。
我們之前傳入fragmentManager對象的目的就是生成這個fragmentTransaction對象,
這回我們可以在傳入一個已經配置好的fragmentTransaction對象,大大增加了可定制性。
所以api的製訂也是大神們心血的結晶啊。
dimiss()
|
|
我們知道瞭如果一個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的點擊事件——關閉對話框
|
|
補充:實現信息保存
在activity橫豎屏切換的時候,dialog現在可以自動重建了,如果你在editText中輸入了信息,在重建的時候會不會保留之前的呢?在4.2和4.4中對話框人性化的自定保存了之前輸入的內容,我們無須手動處理。但如果你測試的手機被奇葩的定制了,那就乖乖的保存數據吧。
|
|
通過onCreateDialog()來快捷的建立對話框
我們上面建立的對話框都是用自定義佈局的,難道我們之前學過的dialog知識都沒用了麼?
我們如果沒自定義對話框的需求,怎麼辦?就沒有一種快一點的方式來建立對話框麼?
快用onCreatDialog吧!!!這個回調方法是DialogFragment獨有的,通過它返回的是一個Dialog對象,
這個對象就會被顯示到屏幕上。千萬別同時使用onCreatView和onCreatDialog方法,
他們僅僅是為了完成同樣一個目的的兩條路而已。
PS:從生命週期的順序而言,先執行onCreateDialog(),後執行onCreateView()
我在onCreatDialog建立一個警告對話框的builder,通過這個builder的create()方法來生成一個AlertDialog對象,因為AlertDialog是Dialog的子類,所以可以直接返回給Dialog。
這裡可以用其他不同對話框的builder,代碼類似,
只不過就是通過builder的creat()方法返回的是不同的對象而已。builder模式也是蠻巧妙的~
|
|
顯示效果:
看到了麼,這裡的標題欄終於有用了,原來那個標題欄是為了給我們在這裡用的啊~
注意:
① 因為這裡創建的是一個dialog,所以用的onclickListener自然是對話框中的listener了。
② 千萬別在構建對話框對象的時候順手寫了show()方法,我們現在是在fragment中初始化一個對話框,
真正讓他顯示的時候是在activity中用這個dialogFragment對象顯示的。
如果這裡寫了show方法不會報錯,但是會出現兩個對話框!
那麼,我們能不能在這裡自定義對話框呢?當然可以啦,
本身Dialog.builder就提供了自定義view的方法,和之前用Dialog一樣自定義下viwe就搞定了。
|
|
這裡貼下我在另一篇文章的自定義對話框view的代碼片段:
詳細看這裡:http://www.cnblogs.com/tianzhijiexian/p/3867731.html
|
|
DialogFragment與Activity之前進行通信
思路很簡單,就是定義一個傳輸數據的接口,強制activity實現這個接口,
在fragment需要傳遞數據的時候去調用這個接口的方法,
activity就能在這個方法中得到相應的數據了。這點在之前的fragment傳遞數據中已經介紹過了,
可以參考這篇文章:
http://www.cnblogs.com/tianzhijiexian/p/3888330.html
在真正項目中,fragment的編寫並不需要了解activity的各類方法,
好的編程風格是將fragment所涉及的方法以接口的方式封裝起來,我在此寫一個例子來說明一下。
寫一個接口——DataCallback
|
|
activity實現這個接口
|
|
在DialogFragment中使用這個接口
在DialogFragment中使用這個接口,並且用instanceof來看啟動它的activity是否實現了這個接口,如果沒實現就拋出異常。這樣我們就能保證在大型項目中不會出現忘記實現這個接口的問題了。
|
|
此外fragment也可以通過fragment管理器,通過tag,獲取其他fragment實例,
從而進行fragment之間的通信。當然從編程思想的角度看,fragment之間的過多進行交叉調用,不利於程序的管控。
用DialogFragment實現再次彈窗
有時候我們可能有這樣的需求,點擊對話框中的一個按鈕後又彈出一個對話框,這個該怎麼做呢?
首先在點擊事件中將這個對話框在屏幕上移除,然後把這個fragment壓棧,
最後建立一個新的dialogFragment對象,show出來。我們雖然讓這個fragment在屏幕上消失,
但還是可以通過fragment管理器到回退棧中找到它。
二次彈窗的代碼:
|
|
利用Fragment的特性,為不同屏幕做適配
如果我們想在大屏幕上顯示對話框,而小屏幕中直接把對話框的內容放在activity中顯示呢?
其實也很簡單,本身這個dialogFragment就是一個fragment,所以完全有fragment的特性,你可以用fragmentTranscation將其放到任何佈局中,你也可以用show()方法把它當作dialog顯示出來。
接下來就剩下一個問題了,判斷屏幕大小。
在默認的values下新建一個bools.xml
|
|
然後,在res下新建一個values-large,在values-large下再新建一個bools.xml,
通過加載不同的value就能知道是大屏還是小屏幕啦
|
|
在代碼中進行判斷
|
|
參考
http://blog.csdn.net/huangyabin001/article/details/30053835