I want to display a Bitmap image in a SurfaceView ...
# compose-android
c
I want to display a Bitmap image in a SurfaceView wrapped in AndroidView. This is what I have, but it just shows as black. Am I doing something wrong? I am a noob at SurfaceView and Canvas, so maybe I'm doing something dumb.
Copy code
AndroidView(
    factory = { ctx ->
      SurfaceView(ctx).apply {
        val bitmap: Bitmap = BitmapFactory.decodeResource(resources, R.drawable.compose_ui_logo)
        val canvas = Canvas()
        canvas.drawBitmap(bitmap, 0F, 0F, Paint())
        draw(canvas)
      }
    })
If it helps... my end goal is to show a bitmap, and animate it across the screen, or make it bounce or something. So far, I'm stuck here though. 😂
f
to set some context, why use AndroidView and not display the bitmap directly in compose?
i
SurfaceView is not going to animate well at all, due to how it works
c
The real reason is that I have to use some old third party camera dependency which renders to a SurfaceView. All of that works, but because its a third party camera attachment. i have to use surface view. the reason for my code above is that i want to have if (BuildConfig.DEBUG){ MyFakeCameraView() } else { MyThirdPartyCameraView() }
and I really want to build a FakeVameraView that is with a SurfaceView because I found a bug with how SurfaceView works (filed here: https://issuetracker.google.com/issues/285718058) so, my hope is that I can have a surface view that animates that'll repro that bug above, and use that in my debug/emulator builds (which dont have access to the external camera hardware)
SurfaceView is not going to animate well at all, due to how it works
nice to know. in my case, i think it'll be fine. just want something to be able to tell if theres movement.
r
You create a Canvas with no target
You’re supposed to use SurfaceView.lockCanvs
Animations can work well on more recent versions of android
But why use a surface view when you are faking the camera view?
c
The "camera view" from this third party library uses surface view to render? This is the 3rd party lib i have to use https://github.com/shanerodrigues/compose-uvc-camera/blob/main/app/src/main/java/com/example/camerauvctest/MainActivity.kt#L189
but anyway. my goal for now is still going to be to draw a bitmap on the screen with a surface view + android view. Lets see if lockCanvas does the trick
r
I understand the SurfaceView is needed for the camera but why use it to fake the camera?
c
oh. because we caught an issue late with the camera not re-starting properly (like the issuetracker I linked above) because we all use emulators during day to day development. So we figure we can catch that earlier on our emulators if we use the same surfaceview like the camera.
Still coming up empty. Probably missing something basic here...
Copy code
AndroidView(
        factory = { ctx ->
            SurfaceView(ctx).apply {
                holder.addCallback(object : SurfaceHolder.Callback {
                    override fun surfaceCreated(p0: SurfaceHolder) {
                        val canvas = Canvas()
                        draw(canvas)
                        p0.lockCanvas()
                        val bitmap: Bitmap = BitmapFactory.decodeResource(resources, R.drawable.logo)
                        canvas.drawBitmap(bitmap, 0F, 0F, Paint())
                    }

                    override fun surfaceChanged(p0: SurfaceHolder, p1: Int, p2: Int, p3: Int) {
                    }

                    override fun surfaceDestroyed(p0: SurfaceHolder) {
                    }
                })
            }
        })
Okay. after some more banging on the keyboard. This sorta works. I suppose it'll get the job done. Let me know if anything looks super duper terrible, but this should be good.
Copy code
@Composable
fun FakeCam() {
    var enabled by remember { mutableStateOf(true) }
    val anim = animateFloatAsState(targetValue = if (enabled) 100f else 0f, label = "")

    LaunchedEffect(key1 = Unit, block = {
        while (true) {
            delay(1000)
            enabled = !enabled
        }
    })

    val context = LocalContext.current
    val callback = remember {
        object : SurfaceHolder.Callback {
            override fun surfaceCreated(holder: SurfaceHolder) {}
            override fun surfaceChanged(holder: SurfaceHolder, p1: Int, p2: Int, p3: Int) {
                drawBitmapToCanvas(anim.value, anim.value, context, holder)
            }

            override fun surfaceDestroyed(p0: SurfaceHolder) {}
        }
    }
    AndroidView(
        factory = { ctx ->
            SurfaceView(ctx).apply {
                holder.addCallback(callback)
            }
        }, update = { view ->
            drawBitmapToCanvas(anim.value, anim.value, context, view.holder)
        }
    )
}

fun drawBitmapToCanvas(x: Float, y: Float, context: Context, holder: SurfaceHolder) {
    val canvas = holder.lockCanvas()
    if (canvas != null) {
        canvas.drawColor(Color.BLACK)
        val bitmap: Bitmap =
            BitmapFactory.decodeResource(context.resources, R.drawable.logo)
        canvas.drawBitmap(bitmap, x, y, Paint())
        holder.unlockCanvasAndPost(canvas)
    }
}
114 Views