前言
因为业务需求需要做全屏展示,比较理想的效果就是布局延伸到状态栏底部,并且隐藏底部导航栏。
为什么不直接用自带的弹窗Dialog?
用自带的弹窗Dialog弹出显示的时候会把状态栏和底部导航栏顶出来,这个时候需要在onWindowFocusChanged进行隐藏,就会有显示又隐藏的动画,显得不是很美观。
实现原理
其实是利用了Compose渲染机制,修改state之后会进行重组,并且界面可以叠加渲染的原理,渲染在前面的界面在底部,后渲染的在顶部。那么后渲染的界面加上半透明灰色背景,布局中间编写弹窗界面即可实现与自带Dialog一致的弹窗效果。由于是在decorView上面绘制的,不会引起状态栏变化。
怎么实现?
编写State类
sealed class UiState {
object Main: UiState()
object Loading: UiState()
data class Success(val text: Text): UiState()
object Error: UiState()
}
在ViewModel添加StateFlow,使用热流Flow来收集状态数据
private val _uiState = MutableStateFlow<UiState>(UiState.Main)
val uiState: StateFlow<UiState> = _uiState
fun setState(state: UiState) {
_uiState.value = state
}
在Compose组件内收集Flow数据流:
MainContainer() // 主页布局Compose
// 根据state叠加显示弹窗,需要放在主页布局后面
when (viewModel.uiState.collectAsState().value) {
is UiState.Success -> {
showSuccessDialog()
}
is UiState.Loading -> {
showLoadingDialog()
}
is UiState.Error -> {
showErrorDialog()
}
else -> {
}
}
通过控制状态来展示不同的弹窗
viewModel.setState(UiState.Success)
实现沉浸式全屏
onCreate添加以下代码:
window.apply {
WindowCompat.setDecorFitsSystemWindows(this, false)
setFlags(
WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS,
WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS
)
decorView.systemUiVisibility =
(View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION)
}
View系可以吗?
decorView.addView()也可以实现类似效果,不过需要自行控制view的显示和隐藏。自定义弹窗View,然后addView上去即可。
val view = View(this@FullDialogActivity)
view.layoutParams = ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)
view.background = ContextCompat.getDrawable(this@FullDialogActivity, R.color.purple_200)
(decorView as ViewGroup).addView(view)