BlurView
BlurView copied to clipboard
BlurView breaks Compose recomposition
- 2.0.3
- All devices / Android 12, Poco X3 Pro
I'm trying to display a notification with a blur on top of all views. When a notification is shown, Compose stops updating, does not respond to clicks. I wrote a small sample where this can be reproduced: https://github.com/zakrodionov/ComposeBugStuckComposition
NotificationView.kt
class NotificationView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : FrameLayout(context, attrs, defStyleAttr) {
private val view: View = View.inflate(context, R.layout.view_notification, this)
private val blurView = view.findViewById<BlurView>(R.id.blurView)
init {
val decorView = getActivityDecorView()
val rootView = decorView?.findViewById(android.R.id.content) as ViewGroup
val windowBackground = decorView.background
blurView.setupWith(rootView, RenderScriptBlur(context)) // or RenderEffectBlur
.setFrameClearDrawable(windowBackground)
.setBlurRadius(2f)
}
private fun getActivityDecorView(): View? {
var ctx = context
var i = 0
while (i < 4 && ctx != null && ctx !is Activity && ctx is ContextWrapper) {
ctx = ctx.baseContext
i++
}
return if (ctx is Activity) {
ctx.window.decorView
} else {
null
}
}
}
ComposeFragment.kt
private fun showNotification() {
val viewGroup =
requireActivity().window?.decorView?.findViewById(android.R.id.content) as? ViewGroup
val view =
NotificationView(requireContext()).apply {
setOnClickListener { viewGroup?.removeView(it) }
}
val params =
FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.WRAP_CONTENT
)
params.gravity = Gravity.TOP or Gravity.CENTER
viewGroup?.addView(view, params)
view.bringToFront()
}
view_notification.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="300dp"
android:orientation="vertical">
<eightbitlab.com.blurview.BlurView
android:id="@+id/blurView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:backgroundTint="@color/purple_200"
app:blurOverlayColor="#A353536C">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:gravity="center"
android:text="Notification title"
android:textSize="30sp" />
</eightbitlab.com.blurview.BlurView>
</LinearLayout>
Fascinating. I haven't tested this kind of scenario, but a BlurView embedded in a Compose hierarchy should work, if that suits your needs. I'm assuming this breaks because of the software rendering that BlurView performs on a Compose hierarchy, but I'm not entirely sure how to debug or fix it.
Thanks for the quick response! The problem is very strange and the Compose itself seems to be to blame. Wrapping the compositing screen and specifying setLayerType(View.LAYER_TYPE_SOFTWARE, null) fixes the problem, but the performance is terrible.
@Composable
fun SoftwareLayerComposable(
modifier: Modifier = Modifier,
content: @Composable () -> Unit,
) {
AndroidView(
factory = { context ->
ComposeView(context).apply {
setLayerType(View.LAYER_TYPE_SOFTWARE, null)
}
},
update = { composeView ->
composeView.setContent(content)
},
modifier = modifier,
)
}
Yeah, I wouldn't consider setting a software layer even if it fixes the problem
Hi, any updates on this breaking compose recomposition problem? Thanks
@anthony-fresneau-kiplin no updates. I'm not investigating it though. The only way to fix it on my side is to move to hardware rendering for UI snapshots, which comes with a whole bunch of caveats because of the API constraints.
You can try to report it to Compose team, but not sure if they'd want to investigate it either.
I tried handling it using a combination of View and Compose and it works. I built the BlurView inside a View and used ComposeView to handle all the other views below or inside it.
I tried handling it using a combination of View and Compose and it works. I built the BlurView inside a View and used ComposeView to handle all the other views below or inside it.
Can you elaborate on that @ruby-turn2cloud?
I'm facing the same issue but not really getting anywhere :eyes:
@williankl I separate compose part and blur part like this:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.compose.ui.platform.ComposeView
android:id="@+id/content_compose_view"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<eightbitlab.com.blurview.BlurView
android:id="@+id/blur_view"
android:layout_width="match_parent"
android:layout_height="100dp"
app:blurOverlayColor="#66FFFFFF"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<androidx.compose.ui.platform.ComposeView
android:id="@+id/compose_view_inside_blur_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</eightbitlab.com.blurview.BlurView>
</androidx.constraintlayout.widget.ConstraintLayout>
Yup, I've tried this but still isn't working for me, unfortunately. Any other ideas or news?
I reported this bug to Compose team, but they might just dismiss it and say I'm trying to do something stupid