您的方法的问题在于,dp.dialog
它始终是在初始化null
时才创建的,因此 UI 可见性更改代码永远不会被执行。如果您同步显示对话框,它可能是非空的:FragmentManager
DialogFragment
MaterialDatePicker
dp.showNow(supportFragmentManager, null)
dp.dialog?.apply {
// anything here gets executed as the dp.dialog is not null
}
然而,这种方法的问题在于,这段代码再也不会被调用。如果对话框被重建(例如设备被旋转),该块内的任何内容都不会再执行,这将导致非全屏对话框。
现在,有一个基本问题MaterialDatePicker
:它final
不能覆盖在其他情况下可以工作的任何对话框创建者/处理程序方法。
幸运的是,有一个类FragmentLifecycleCallbacks
可以用来监听(惊喜-惊喜)片段生命周期事件。您可以使用它来捕捉在创建视图之后构建对话框的时刻(回调:)onFragmentViewCreated
。如果您在Activity
's 或Fragment
's中注册它onCreate(...)
,您的日期选择器片段(以及对话框本身)将是最新的。
因此,事不宜迟,在对不同设置进行大量实验和调整之后,以下解决方案可能会满足您的需求(这使用Activity
)。
示例项目在GitHub上可用。
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// ... setContentView(...), etc.
registerDatePickerFragmentCallbacks()
}
// need to call this every time the Activity / Fragment is (re-)created
private fun registerDatePickerFragmentCallbacks() {
val setFocusFlags = fun(dialog: Dialog, setUiFlags: Boolean) {
dialog.window?.addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE)
if (setUiFlags) {
dialog.window?.decorView?.setOnSystemUiVisibilityChangeListener { visibility ->
// after config change (e.g. rotate) the system UI might not be fullscreen
// this ensures that the UI is updated in case of this
if (visibility and View.SYSTEM_UI_FLAG_FULLSCREEN == 0) {
hideSystemUI(dialog.window?.decorView)
hideSystemUI() // this might not be needed
}
}
hideSystemUI(dialog.window?.decorView)
hideSystemUI() // this might not be needed
}
}
// inline fun that clears the FLAG_NOT_FOCUSABLE flag from the dialog's window
val clearFocusFlags = fun(dialog: Dialog) {
dialog.window?.apply {
clearFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE)
decorView.also {
if (it.isAttachedToWindow) {
windowManager.updateViewLayout(it, attributes)
}
}
}
}
supportFragmentManager.registerFragmentLifecycleCallbacks(object :
FragmentManager.FragmentLifecycleCallbacks() {
override fun onFragmentViewCreated(
fm: FragmentManager,
f: Fragment,
v: View,
savedInstanceState: Bundle?
) {
// apply this to MaterialDatePickers only
if (f is MaterialDatePicker<*>) {
f.requireDialog().apply {
setFocusFlags(this, true)
setOnShowListener {
clearFocusFlags(this)
}
}
}
}
}, false)
}
override fun onResume() {
super.onResume()
// helps with small quirks that could happen when the Activity is returning to a resumed state
hideSystemUI()
}
// this is probably already in your class
override fun onWindowFocusChanged(hasFocus: Boolean) {
super.onWindowFocusChanged(hasFocus)
if (hasFocus) hideSystemUI()
}
private fun hideSystemUI() {
hideSystemUI(window.decorView)
}
// this is where you apply the full screen and other system UI flags
private fun hideSystemUI(view: View?) {
view?.systemUiVisibility = (View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
or View.SYSTEM_UI_FLAG_LAYOUT_STABLE
or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
or View.SYSTEM_UI_FLAG_FULLSCREEN)
}
要MaterialDatePicker
使用沉浸式模式,您需要以下任一样式。第一个显示普通对话框,而第二个使用全屏对话框,这会禁用打开对话框时通常发生的背景变暗,并确保对话框显示在全屏窗口中。
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Normal dialog -->
<style name="MyCalendar" parent="ThemeOverlay.MaterialComponents.MaterialCalendar">
<!-- you can use ?attr/colorSurface to remove any blinking happening during re-creation of the dialog -->
<item name="android:navigationBarColor">?attr/colorPrimary</item>
<!-- or use translucent navigation bars -->
<!--<item name="android:windowTranslucentNavigation">true</item>-->
<item name="android:immersive">true</item>
<item name="android:windowFullscreen">true</item>
<item name="android:windowIsTranslucent">false</item>
</style>
<-- Fullscreen dialog -->
<style name="MyCalendar.Fullscreen" parent="ThemeOverlay.MaterialComponents.MaterialCalendar.Fullscreen">
<item name="android:windowIsFloating">false</item>
<item name="android:backgroundDimEnabled">false</item>
<!-- you can use ?attr/colorSurface to remove any blinking happening during re-creation of the dialog -->
<item name="android:navigationBarColor">?attr/colorPrimary</item>
<!-- or use translucent navigation bars -->
<!--<item name="android:windowTranslucentNavigation">true</item>-->
<item name="android:immersive">true</item>
<item name="android:windowFullscreen">true</item>
<item name="android:windowIsTranslucent">false</item>
</style>
</resources>
在构建对话框时,只需将此样式作为对话框的主题传递:
val datePickerBuilder = MaterialDatePicker.Builder.dateRangePicker()
// for a normal dialog
datePickerBuilder.setTheme(R.style.MyCalendar)
// for a fullscreen dialog
datePickerBuilder.setTheme(R.style.MyCalendar_Fullscreen)
这是全屏沉浸式对话框的样子:
和一个正常的沉浸式对话:
这些屏幕截图是在通常具有导航栏的模拟器上拍摄的。