androidopengl-esandroid-jetpack-composeandroid-view

How to render OpenGL alongside Jetpack Compose UI without covering other elements


I'm trying to render an OpenGL view with a Jetpack Compose UI displaying below it. I took example code from https://developer.android.com/develop/ui/compose/migrate/interoperability-apis/views-in-compose, but OpenGL is drawing over the Compose elements.

The first screenshot shows the OpenGL renderer over whatever elements Compose is displaying. The second screenshot shows what it looks like when I remove all other composable elements from the composable.

How do I get OpenGL to respect other Jetpack Compose elements? I'd be the happiest if I could shrink the OpenGL window with Compose, but if I could render elements on top of it that would be fine.

OpenGL covering UI elements And here's

class MyGLSurfaceView(context: Context) : GLSurfaceView(context) {

    private val renderer: MyGLRenderer

    init {
        setEGLContextClientVersion(2)
        renderer = MyGLRenderer()
        setRenderer(renderer)
    }
}
@Composable
fun CustomView() {
    AndroidView(
        modifier = Modifier.fillMaxSize(),
        factory = { context ->
            // Creates view
            MyGLSurfaceView(context).apply { }
        },
        update = { view ->
            // View's been inflated or state read in this block has been updated
            // Add logic here if necessary
        }
    )
}

@Composable
fun ContentExample() {
    Column(Modifier.fillMaxSize()) {
        CustomView()
        Text("Look at this CustomView!")
        Button(onClick = { }) {
            Text("Test button")
        }
    }
}
class MyGLRenderer : GLSurfaceView.Renderer {

    override fun onSurfaceCreated(unused: GL10, config: EGLConfig) {
        // Set the background frame color
        GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f)
    }

    override fun onDrawFrame(unused: GL10) {
        // Redraw background color
        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT)
    }
    override fun onSurfaceChanged(unused: GL10, width: Int, height: Int) {
        GLES20.glViewport(0, 0, width, height)
    }
}

Solution

  • Well, you do use Modifier.fillMaxSize() on the AndroidView...

    If you want to use a relative size then you should let the calling composable handle that, otherwise it is hard to find out what actually happens to your layouts. You should pass a Modifier parameter to CustomView to facilitate that.

    Also you may need to apply the clipToBounds() modifier to ensure the AndroidView does not draw outside its size.

    Your CustomView should look something like this:

    @Composable
    fun CustomView(modifier: Modifier = Modifier) {
        AndroidView(
            modifier = modifier.clipToBounds(),
            ...
        )
    }
    

    Then you can use it like this:

    Column(Modifier.fillMaxSize()) {
        CustomView(
            modifier = Modifier
                .fillMaxWidth()
                .fillMaxHeight(0.5f)
        )
    
        Text(...)
    
        Button(...) 
    }