11

When I try to insert Compose in overlay(draw over other apps) with XML I get this exception:

java.lang.IllegalStateException: ViewTreeLifecycleOwner not found from androidx.constraintlayout.widget.ConstraintLayout{d596746 V.E...... ......ID 0,0-0,0}

But without overlay(in activity) it works normal. Does anyone know how to resolve? I already updated AppCompat library to 1.3.0

My XML code:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/black">
    <androidx.compose.ui.platform.ComposeView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/compose_view"/>
</androidx.constraintlayout.widget.ConstraintLayout>

My Overlay code:

mParams = WindowManager.LayoutParams(
    WindowManager.LayoutParams.WRAP_CONTENT,
    WindowManager.LayoutParams.WRAP_CONTENT,
    WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
    WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
    PixelFormat.TRANSLUCENT
)
layoutInflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
mView = layoutInflater.inflate(R.layout.power_overlay, null)
mParams!!.gravity = Gravity.CENTER
mWindowManager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
mWindowManager.addView(mView, mParams)
Renattele Renattele
  • 1,626
  • 2
  • 15
  • 32
  • 2
    See https://stackoverflow.com/q/65755763/115145 and https://stackoverflow.com/a/66447195/115145 for an example of using Compose outside of a activity or fragment (in that case, an IME). – CommonsWare Jul 24 '21 at 17:12
  • I think you mean ConstraintLayout in the first line, instead of Compose; consider removing the Android-jetpack-compose tag, please – Richard Onslow Roper Jul 25 '21 at 06:05
  • 1
    Update androidx.appcompat:appcompat to latest version 1.3.0 – Muthuraman Sundararaj Jul 26 '21 at 04:37
  • @MuthuramanSundararaj already did this. – Renattele Renattele Jul 26 '21 at 12:37
  • java.lang.IllegalStateException: means your trying to do something when its not ready, thats why it works when you remove it, you need to find out the appropriate moment to do this. can you try in onResume ? – Zaid Zakir Jul 27 '21 at 08:08
  • Does this answer your question? [ViewTreeLifecycleOwner not found from DecorView@2da7146\[MyActivity\]](https://stackoverflow.com/questions/66382502/viewtreelifecycleowner-not-found-from-decorview2da7146myactivity) – Mahozad Sep 18 '21 at 07:34

4 Answers4

8

For me it was because I had not included the appcompat library and my activity inherited from Activity instead of AppCompatActivity. The problem resolved by adding the library:

implementation("androidx.appcompat:appcompat:1.3.1")

and inheriting from AppCompatActivity:

class MyActivity: AppCompatActivity() {
  ...
}
Mahozad
  • 18,032
  • 13
  • 118
  • 133
6

For me, upgrade androidx.appcompat:appcompat from 1.0.0 to 1.4.1, the problem solved.

The fragment:

class Xxx : Fragment() {

override fun onCreateView(
    inflater: LayoutInflater,
    container: ViewGroup?,
    savedInstanceState: Bundle?
): View? {

    val view = inflater.inflate(R.layout.fragment_xxx, container, false)

    view.findViewById<ComposeView>(R.id.compose_view).apply {
        setViewCompositionStrategy(
            ViewCompositionStrategy.DisposeOnLifecycleDestroyed(viewLifecycleOwner)
        )
        setContent {
            // compose
        }
    }
    return view
}

The xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/white"
    android:orientation="vertical">

    <androidx.compose.ui.platform.ComposeView
        android:id="@+id/compose_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
</LinearLayout>
Jared Burrows
  • 54,294
  • 25
  • 151
  • 185
1
  1. Ensure that your constraint layout is updated to latest version in app level build.gradle file.

dependencies { ...    
 implementation 'androidx.constraintlayout:constraintlayout:1.3.x'
  1. To be sure, search & Replace ALL your xml tag names

<androidx.constraintlayout.ConstraintLayout>

 //with ->

<androidx.constraintlayout.widget.ConstraintLayout>

 

in every place CTRL + SHIFT + R _

  1. And inside gradle.properties add these:

 android.enableJetifier=true
 android.useAndroidX=true
  1. Clear out/Invalidate Caches and Restart Android Studio.

FileInvalidate Caches / RestartInvalidate and Restart

Transformer
  • 6,963
  • 2
  • 26
  • 52
1

I wrote this code and it works.

class AndroidComposeDialog<T>(
    private val activity: T,
    private val content: @Composable () -> Unit
) : Dialog(activity) where T : Context, T : androidx.lifecycle.LifecycleOwner, T : ViewModelStoreOwner, T : SavedStateRegistryOwner, T : OnBackPressedDispatcherOwner {


    private fun initViewTreeOwners() {
        val window = window ?: return
        ViewTreeLifecycleOwner.set(window.decorView, activity)
        ViewTreeViewModelStoreOwner.set(window.decorView, activity)
        window.decorView.setViewTreeSavedStateRegistryOwner(activity)
        window.decorView.setViewTreeOnBackPressedDispatcherOwner(activity)
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        //here i init view tree owner that will be used by compose view
        initViewTreeOwners()
        //here i hide background of dialog
        window?.setBackgroundDrawable(ColorDrawable(android.graphics.Color.TRANSPARENT))
        val view = ComposeView(context).apply {
            setContent(content)
        }
        setContentView(view)
    }
}


// A function that returns an android dialog based on entered content
@Composable
fun androidDialog(content: @Composable () -> Unit): AndroidComposeDialog<*>? {
    val context = LocalContext.current
    val activity = context as? AppCompatActivity
    val componentActivity = context as? ComponentActivity
    val dialog = remember(activity, content.hashCode()) {
        if (activity != null)
            AndroidComposeDialog(activity = activity, content = content)
        else if (componentActivity != null)
            AndroidComposeDialog(activity = componentActivity, content = content)
        else null
    }
    return dialog
}

and now it's ready to use

@Composable
fun ShowDialog() {
    val dialog = androidDialog {
        Box(
            modifier = Modifier
                .size(100.dp)
                .clip(RoundedCornerShape(25.dp))
                .background(Color.Blue)
        )
    }
    Box(modifier = Modifier.fillMaxSize()) {
        Button(onClick = {
            dialog?.show()
        }, modifier = Modifier.align(Alignment.Center)) {
            Text(text = "show dialog")
        }
    }
}