BlurView icon indicating copy to clipboard operation
BlurView copied to clipboard

Using with Jetpack Compose

Open LorenzoFerri opened this issue 4 years ago • 16 comments

Hi, I would like to use this library in a Jetpack Compose project however I'm not finding any success. Could you provide a simple Composable component that with an AndroidView or a AndroidViewBinding wrap your library? Thank you

LorenzoFerri avatar Oct 12 '21 15:10 LorenzoFerri

Not sure if I understand what problem you're having exactly. Why can't you wrap it into AndroidView yourself?

Dimezis avatar Oct 13 '21 12:10 Dimezis

Hi, thank you for the reply. I'm trying to create a blurred effect in the TopBar of my app. The app is entirely written in Jetpack Compose. I tried to wrap everything like this, however I did not understand what should I pass to the method setupWith since I don't have a rootView (or I don't know how to get it).

@Composable
fun TopBar() {
    AndroidView(
        modifier = Modifier
            .fillMaxWidth()
            .height(56.dp),
        factory = { context ->
            BlurView(context).apply {
                this.setupWith(this) // <- Here
                    .setBlurAlgorithm(RenderScriptBlur(context))
                    .setBlurRadius(20f)
                    .setBlurAutoUpdate(true)
            }
        }
    )
}

If it's helpful, since it's a TopBar, the content behind is scrollable. Thank you again.

LorenzoFerri avatar Oct 13 '21 12:10 LorenzoFerri

If your composable is written within the Activity, just get a reference to it and do

val decorView = activity.window.decorView
val rootView = decorView.findViewById<ViewGroup>(android.R.id.content)

If you don't have a reference to the Activity, you can cast the context to Activity, though that's a bit hacky and I'm not sure if the preview in AS will be rendered correctly.

You can also just pass the Activity reference to your TopBar Composable

Dimezis avatar Oct 13 '21 13:10 Dimezis

I tried both with casting and passing the MainActivity to my TopBar, but in both cases the application crashes with this error:

E/AndroidRuntime: FATAL EXCEPTION: main
    Process: io.dachain.dachain, PID: 3146
    java.lang.IllegalStateException: Recording currently in progress - missing #endRecording() call?
        at android.graphics.RenderNode.beginRecording(RenderNode.java:372)
        at android.graphics.RenderNode.beginRecording(RenderNode.java:391)
        at androidx.compose.ui.platform.RenderNodeApi29.record(RenderNodeApi29.android.kt:151)
        at androidx.compose.ui.platform.RenderNodeLayer.updateDisplayList(RenderNodeLayer.android.kt:243)
        at androidx.compose.ui.platform.AndroidComposeView.dispatchDraw(AndroidComposeView.android.kt:677)
        at android.view.View.draw(View.java:22353)
        at android.view.View.draw(View.java:22223)
        at android.view.ViewGroup.drawChild(ViewGroup.java:4516)
        at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4277)
        at android.view.View.draw(View.java:22221)
        at android.view.ViewGroup.drawChild(ViewGroup.java:4516)
        at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4277)
        at android.view.View.draw(View.java:22353)
        at eightbitlab.com.blurview.BlockingBlurController.updateBlur(BlockingBlurController.java:122)
        at eightbitlab.com.blurview.BlockingBlurController.draw(BlockingBlurController.java:164)
        at eightbitlab.com.blurview.BlurView.draw(BlurView.java:51)
        at android.view.View.updateDisplayListIfDirty(View.java:21226)
        at android.view.View.draw(View.java:22081)
        at android.view.ViewGroup.drawChild(ViewGroup.java:4516)
        at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4277)
        at android.view.View.draw(View.java:22353)
        at androidx.compose.ui.platform.AndroidViewsHandler.drawView(AndroidViewsHandler.android.kt:72)
        at androidx.compose.ui.platform.AndroidComposeView.drawAndroidView(AndroidComposeView.android.kt:481)
        at androidx.compose.ui.viewinterop.AndroidViewHolder$layoutNode$1$coreModifier$1.invoke(AndroidViewHolder.android.kt:235)
        at androidx.compose.ui.viewinterop.AndroidViewHolder$layoutNode$1$coreModifier$1.invoke(AndroidViewHolder.android.kt:232)
        at androidx.compose.ui.draw.DrawBackgroundModifier.draw(DrawModifier.kt:101)
        at androidx.compose.ui.node.ModifiedDrawNode.performDraw(ModifiedDrawNode.kt:102)
        at androidx.compose.ui.node.LayoutNodeWrapper.draw(LayoutNodeWrapper.kt:244)
        at androidx.compose.ui.node.DelegatingLayoutNodeWrapper.performDraw(DelegatingLayoutNodeWrapper.kt:68)
        at androidx.compose.ui.node.LayoutNodeWrapper.draw(LayoutNodeWrapper.kt:244)
        at androidx.compose.ui.node.DelegatingLayoutNodeWrapper.performDraw(DelegatingLayoutNodeWrapper.kt:68)
        at androidx.compose.ui.node.LayoutNodeWrapper.draw(LayoutNodeWrapper.kt:244)
        at androidx.compose.ui.node.ModifiedLayoutNode.performDraw(ModifiedLayoutNode.kt:98)
        at androidx.compose.ui.node.LayoutNodeWrapper.draw(LayoutNodeWrapper.kt:244)
        at androidx.compose.ui.node.ModifiedLayoutNode.performDraw(ModifiedLayoutNode.kt:98)
        at androidx.compose.ui.node.LayoutNodeWrapper$invoke$1.invoke(LayoutNodeWrapper.kt:255)
        at androidx.compose.ui.node.LayoutNodeWrapper$invoke$1.invoke(LayoutNodeWrapper.kt:254)
        at androidx.compose.runtime.snapshots.Snapshot$Companion.observe(Snapshot.kt:1776)
        at androidx.compose.runtime.snapshots.SnapshotStateObserver.observeReads(SnapshotStateObserver.kt:123)
        at androidx.compose.ui.node.OwnerSnapshotObserver.observeReads$ui_release(OwnerSnapshotObserver.kt:75)
        at androidx.compose.ui.node.LayoutNodeWrapper.invoke(LayoutNodeWrapper.kt:254)
        at androidx.compose.ui.node.LayoutNodeWrapper.invoke(LayoutNodeWrapper.kt:54)
        at androidx.compose.ui.platform.RenderNodeApi29.record(RenderNodeApi29.android.kt:156)
        at androidx.compose.ui.platform.RenderNodeLayer.updateDisplayList(RenderNodeLayer.android.kt:243)
        at androidx.compose.ui.platform.AndroidComposeView.dispatchDraw(AndroidComposeView.android.kt:677)
        at android.view.View.draw(View.java:22353)
        at android.view.View.draw(View.java:22223)
        at android.view.ViewGroup.drawChild(ViewGroup.java:4516)
        at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4277)
        at android.view.View.draw(View.java:22221)
        at android.view.ViewGroup.drawChild(ViewGroup.java:4516)
        at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4277)
        at android.view.View.draw(View.java:22353)
        at eightbitlab.com.blurview.BlockingBlurController.updateBlur(BlockingBlurController.java:122)
        at eightbitlab.com.blurview.BlockingBlurController$1.onPreDraw(BlockingBlurController.java:56)
        at android.view.ViewTreeObserver.dispatchOnPreDraw(ViewTreeObserver.java:1093)
        at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:3089)
        at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1952)
        at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:8171)
        at android.view.Choreographer$CallbackRecord.run(Choreographer.java:972)
        at android.view.Choreographer.doCallbacks(Choreographer.java:796)
        at android.view.Choreographer.doFrame(Choreographer.java:731)
        at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:957)
        at android.os.Handler.handleCallback(Handler.java:938)
        at android.os.Handler.dispatchMessage(Handler.java:99)
        at android.os.Looper.loop(Looper.java:223)
        at android.app.ActivityThread.main(ActivityThread.java:7656)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)
I/Process: Sending signal. PID: 3146 SIG: 9

My updated code looks like this:

@Composable
fun TopBar(activity: MainActivity) {
    val decorView = activity.window.decorView
    val rootView = decorView.findViewById<ViewGroup>(android.R.id.content)
    val windowBackground = decorView.background;
    AndroidView(
        modifier = Modifier
            .fillMaxWidth()
            .height(56.dp),
        factory = { context ->
            BlurView(context).apply {
                this.setupWith(rootView)
                    .setFrameClearDrawable(windowBackground)
                    .setBlurAlgorithm(RenderScriptBlur(context))
                    .setBlurRadius(20f)
                    .setBlurAutoUpdate(true)
            }
        }
    )
}

LorenzoFerri avatar Oct 13 '21 13:10 LorenzoFerri

Then I guess the BlurView is not compatible with Composable hierarchies. I'm slightly interested in why it happens, so I might check it out later, but most likely this won't be fixed

Dimezis avatar Oct 13 '21 13:10 Dimezis

@LorenzoFerri If you are interested Modifier.blur() is available in Compose 1.1.0-alpha03 on Android 12

rabross avatar Nov 09 '21 20:11 rabross

@LorenzoFerri If you are interested Modifier.blur() is available in Compose 1.1.0-alpha03 on Android 12

I tried that Modifier, but it doesn’t achieve what I need, blurring only the composable itself and not what's behind it.

LorenzoFerri avatar Nov 10 '21 10:11 LorenzoFerri

@LorenzoFerri Did you get this to work using Compose? I am stuck with the same issue!

ComposeDesigner avatar Jan 27 '22 11:01 ComposeDesigner

I've spent some time trying to achieve the blurring effect in Compose using this library and anything else that I was able to find, but I haven't found any solution. So, currently, migrating my project to Compose is put on hold indefinitely.

jakub-cosmose avatar Jan 27 '22 11:01 jakub-cosmose

I haven't found a solution either. But I noticed that using @LorenzoFerri's code stops throwing an error if I don't use a Modifier in the AndroidView section however the blur itself is pretty weird and not the desired effect. So I wonder if it has something to do with that. I'd love to find a solution to this.

ComposeDesigner avatar Jan 27 '22 12:01 ComposeDesigner

It would be really great if BlurView can be used in Jetpack Compose :')

tengyeekong avatar May 11 '22 08:05 tengyeekong

Any updates?

HasanHaghniya avatar May 14 '22 05:05 HasanHaghniya

@SecretKeeper I've opened an issue here - https://issuetracker.google.com/issues/232625913 I don't think there's anything reasonable I can do to fix this crash on my side. The Compose's behavior doesn't look right to me in this case.

Star it if you'd like to help

Dimezis avatar May 15 '22 10:05 Dimezis

I tried moving the setupWith into update block and it works, but it only renders for the first time.. Screenshot 2022-05-15 at 11 27 40 PM

tengyeekong avatar May 15 '22 15:05 tengyeekong

I tried moving the setupWith into update block and it works

Unfortunately, it's probably some (un)lucky quirk of your particular setup, because there's a fundamental problem preventing it from working correctly. Also, it still crashes for me even with this delayed setup

Dimezis avatar May 15 '22 15:05 Dimezis

Version 2.0.2 should fix this problem, but I'm not sure it's going to be working in every scenario. But you can try and let me know how it went

Dimezis avatar Jul 18 '22 18:07 Dimezis

Hi @Dimezis, sorry to comment on the closed issue, but I found this is relavent to my question, I am curious about what if the root view is composable? Basically I have a composable that is ready to be used, I just want its background to be blured, I tried to embed BlurView into AndroidView, but how to integrate composable in this case? Could you shed some lights, thanks!


@Composable
fun HomeBottomBar(){
// Composable goes here
}


@Composable
fun AndroidViewBasedCompose(){
    val context = LocalContext.current
    val decorView = context.activity.window.decorView
    val rootView = decorView.findViewById<ViewGroup>(android.R.id.content). //Where can I get this??? It's a composable in my case
    val windowBackground = decorView.background;
    AndroidView(
        modifier = Modifier
            .fillMaxWidth()
            .height(56.dp),
        factory = { context ->
            BlurView(context).apply {
                this.setupWith(rootView)
                    .setFrameClearDrawable(windowBackground)
                    .setBlurAlgorithm(RenderScriptBlur(context))
                    .setBlurRadius(20f)
                    .setBlurAutoUpdate(true)
            }
        }
    )
}

// Utility function for getting activity ref
fun Context.getActivity(): AppCompatActivity? = when (this) {
    is AppCompatActivity -> this
    is ContextWrapper -> baseContext.getActivity()
    else -> null
}

jianinz avatar Jun 30 '23 08:06 jianinz

@jianinz I'm not sure if I understand your problem.

val rootView = decorView.findViewById<ViewGroup>(android.R.id.content). //Where can I get this??? It's a composable in my case

android.R.id.content can't be a composable. It's a default ViewGroup present in every Activity.

Your setup looks fine, though I didn't test it. What's the problem exactly and what happens when you use AndroidViewBasedCompose?

Dimezis avatar Jun 30 '23 09:06 Dimezis

@Dimezis Thanks for your quick response! In the example here https://github.com/Dimezis/BlurView#how-to-use, BlurView will have any child view which will not be blured. In my case, the child view will be a composable, I wonder how to integrate that composable into BlurView?

Should I just do something like:

              Box {
                HomeBottomBar(
                )
                AndroidViewBasedCompose()
              }

jianinz avatar Jun 30 '23 10:06 jianinz

If I use code below

@Composable
fun AndroidViewBasedCompose(){
    val context = LocalContext.current
    val decorView = context.activity.window.decorView
    val rootView = decorView.findViewById<ViewGroup>(android.R.id.content)
    val windowBackground = decorView.background
    AndroidView(
        modifier = Modifier
            .fillMaxWidth()
            .height(56.dp),
        factory = { context ->
            BlurView(context).apply {
                this.setupWith(rootView)
                    .setFrameClearDrawable(windowBackground)
                    .setBlurAlgorithm(RenderScriptBlur(context)) // No longer available in the version 2.0.3
                    .setBlurRadius(20f)
                    .setBlurAutoUpdate(true)
            }
        }
    )
}
              Box {
                HomeBottomBar(
                )
                AndroidViewBasedCompose()
              }

I don't see the blured view, instead I see the mono background colored bottom bar, no blur effect takes into place

jianinz avatar Jun 30 '23 10:06 jianinz

Should I just do something like: ...

Yes, although I think the order should be

Box {
    AndroidViewBasedCompose()
    HomeBottomBar()                
}

And the HomeBottomBar has to have a transparent background for the BlurView to be visible.

Edit: On second thought, in this case, the BlurView would also include the bottom bar into its blurred content, so that might not be doable in the end.

Dimezis avatar Jun 30 '23 10:06 Dimezis

On second thought, in this case, the BlurView would also include the bottom bar into its blurred content, so that might not be doable in the end.

Yeah, just tried changing order, in the end the blur view does not show up, is there any ways to include the bottom bar into its blurred content?

jianinz avatar Jun 30 '23 10:06 jianinz

in the end the blur view does not show up

As I said, the bottom bar composable has to have a transparent background. I don't know if it's adjustable though

Dimezis avatar Jun 30 '23 10:06 Dimezis

As I said, the bottom bar composable has to have a transparent background. I don't know if it's adjustable though

I set HomeBottomBar's background color as transparent and blur view as purple, unfortunately the result is just transparent HomeBottomBar

jianinz avatar Jun 30 '23 10:06 jianinz