第二篇 解決DialogFragment狀態(tài)欄亮色問(wèn)題 - 簡(jiǎn)書 (jianshu.com)
前言
很多人抱怨原生的Dialog不好用,自定義方式比較復(fù)雜,這里介紹一下如何使用官方推薦的DialogFragment
使用方法
首先要意識(shí)到DialogFragment的本質(zhì)就是一個(gè)Fragment,實(shí)現(xiàn)DialogInterface,能夠?yàn)殚_發(fā)者提供更多便捷的操作。
- 初始化
和Fragment相同,重寫onCreateView加載布局
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.custom_layout, container, false)
}
其中,R.layou.custom_layout是自己定義的布局文件
- 顯示與隱藏
和Dialog一樣,使用show()方法顯示,但是在DialogFragment中,show()方法需要傳入一個(gè)FragmentManager保證DialogFragment能夠被正確的添加到Activity中。在使用show()方法時(shí),建議重寫該方法并判斷isAdded
dialogFragment.show(supportFragmentManager) // In Activity
dialogFragment.show(parentFragmentManager) // In Fragment
隱藏DialogFragment,如果使用dismiss()方法會(huì)導(dǎo)致某些不可預(yù)期的錯(cuò)誤,很多網(wǎng)友也分析過(guò),建議使用dismissAllowingStateLoss()替代
默認(rèn)背景
系統(tǒng)自帶的Dialog是有背景的,通常自定義的時(shí)候會(huì)嚴(yán)重影響美觀,需要加上這兩句話用來(lái)去掉默認(rèn)的背景:
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
dialog?.requestWindowFeature(Window.FEATURE_NO_TITLE)
dialog?.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
}
如果想調(diào)整背景陰影的透明度,使用dimAmount,數(shù)值從0.0 ~ 1.0,數(shù)字越小越透明,默認(rèn)值為0.6
override fun onResume() {
dialog?.window?.attributes?.dimAmount = 0.6f
}
全屏顯示
網(wǎng)上查閱了很多資料,大部分都說(shuō)要使用style的方式,效果很不錯(cuò),但是我測(cè)試的時(shí)候,dimAmount和動(dòng)畫無(wú)法使用了,我目前使用以下方法解決全屏的問(wèn)題,雖然不夠完美,但目前來(lái)看是最優(yōu)的方式:
override fun onResume() {
super.onResume()
dialog?.window?.setLayout(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
)
}
UI刷新
這是一個(gè)令人頭疼的問(wèn)題,Fragment創(chuàng)建的時(shí)候我們并不知道UI什么時(shí)候能繪制完成,如果在創(chuàng)建DialogFragment實(shí)例就傳參刷新UI,大概率會(huì)導(dǎo)致崩潰,既然本質(zhì)是Fragment,就可以按照對(duì)付Fragment的思路去對(duì)付DialogFragment
- 創(chuàng)建一個(gè)抽象類,繼承
DialogFragment
abstract class BaseDialog : DialogFragment{ }
- 使用LiveData,綁定生命周期
val dialogData = MutableLiveData<Any?>()
- 創(chuàng)建一個(gè)供外部調(diào)用的方法,傳入我們需要的數(shù)據(jù)
fun updateData(data: Any?) {
dialogData.postValue(data)
}
- 創(chuàng)建數(shù)據(jù)回調(diào)(Listener方式)
private var dataListener: ((Any?) -> Unit)? = null
override fun onViewCreated(view: View, savedInstanceState: Bundle?){
super.onViewCreated(view, savedInstanceState)
dialogData.observe(viewLifecycleOwner) { dataListener?.invoke(it) }
}
fun setOnDataListener(listener: (Any?) -> Unit) {
this.dataListener = listener
}
//子類中調(diào)用監(jiān)聽
setOnDataListener { data -> // TODO 根據(jù)data的數(shù)據(jù)類型執(zhí)行不同的UI邏輯 }
- 創(chuàng)建數(shù)據(jù)回調(diào)(Abstract方式)
abstract fun onUpdate(data: Any?)
override fun onViewCreated(view: View, savedInstanceState: Bundle?){
super.onViewCreated(view, savedInstanceState)
dialogData.observe(viewLifecycleOwner) { onUpdate?.invoke(it) }
}
//在子類中重寫onUpdate方法
override fun onUpdate(data:Any?){
//TODO 根據(jù)data的數(shù)據(jù)類型執(zhí)行不同的UI邏輯
}
小結(jié)
DialogFragment乍一看使用起來(lái)挺麻煩,但其本質(zhì)就是一個(gè)Fragment,我們基本可以使用對(duì)待Fragment的思路去處理問(wèn)題。
本人能力有限,學(xué)識(shí)淺薄,只能描述自己遇到的一些問(wèn)題,如果有寫的不好的地方歡迎批評(píng)。
補(bǔ)充
注意一點(diǎn):如果要在DialogFragment中嵌套一個(gè)Fragment,需要使用childFragmentManager來(lái)添加新的Fragment
工作中要涉及到萬(wàn)惡的舊版本(Api <= 22),設(shè)置全屏?xí)@示狀態(tài)欄,需要修改View的屬性:
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
val window = dialog?.window
if (window != null) {
val uiOptions = View.SYSTEM_UI_FLAG_LAYOUT_STABLE or
View.SYSTEM_UI_FLAG_FULLSCREEN or
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
dialog?.window?.decorView?.systemUiVisibility = uiOptions
}
}
如果是使用 Api30及其以上,這里會(huì)出現(xiàn)很多@Deprecated刪除線,不要緊張,新的Api只能在新版中使用,這里可能Studio沒有處理。用以上方式可以消除狀態(tài)欄,但是,彈出dialog的時(shí)候,狀態(tài)欄會(huì)閃一下馬上消失,暫時(shí)沒有更完美的解決方法,如果有請(qǐng)留言,感激不盡。