Javier
Javier

Reputation: 1695

Jetpack compose avoid unnecessary Image bitmap recomposition

I'm trying to emulate a Music player, and everything works, however using the layout inspector I can see that my image is being recompose every time that the "slider" value changes this is part of my code:

val albumArt by viewModel.albumArt.collectAsStateWithLifecycle() // collect a bitmap val playbackPosition by viewModel.playbackPosition.collectAsStateWithLifecycle() // this cause a recomposition, it executes every second that is the duration of te song

@Composable
fun MusicAlbumArt(
    albumArt: Bitmap?,
    modifier: Modifier
) {

    val painter = rememberAsyncImagePainter(
        albumArt,
        contentScale = ContentScale.Crop
    )

    AnimatedContent(targetState = painter,
        transitionSpec = {
            fadeIn() with fadeOut()
        }) {

        Image(
            painter = painter,
            contentDescription = null,
            modifier = modifier,
            contentScale = ContentScale.Crop
        )
    }

}

and this is the main composable:

@Composable
private fun MusicWidget(
playbackPosition: Float, 
albumArt: Bitmap?,
){
BoxWithConstraints {
        val width by remember { mutableStateOf(this.maxWidth) }
        val height by remember { mutableStateOf(this.maxHeight) }

     MusicAlbumArt(
                        modifier = Modifier.fillMaxSize(),
                        musicViewState = musicViewState,
                        albumArt = albumArt
                    )
// 
MusicSlider(modifier = Modifier.fillMaxWidth()
}

The playbackPosition executes every second to update the "song duration" but analyzing this in the layout inspector the AnimatedContent is skipped however the Image is recomposed every time, I can't find the wayt to avoid recomposition on the image. What could be the possible options?

Upvotes: 1

Views: 919

Answers (1)

SVP
SVP

Reputation: 2892

Kind of difficult to tell what's going on without seeing more of the code but I would design a slot-based layout to fix it.

Something like:

@Composable
fun MusicWidget(
    modifier: Modifier = Modifier,
    albumArt: @Composable () -> Unit,
    slider: @Composable () -> Unit
) {
    BoxWithConstraints() {
        albumArt()
        slider()
    }
}

then in your parent:

@Composable
fun Parent(
    modifier: Modifier = Modifier,
    viewModel: MusicViewModel
) {
    val albumArt by viewModel.albumArt.collectAsStateWithLifecycle()
    val playbackPosition by viewModel.playbackPosition.collectAsStateWithLifecycle()

    MusicWidget(
        albumArt = {
            MusicAlbumArt(
                albumArt = albumArt
            )
        },
        slider = {
            MusicSlider(
                position = playbackPosition
            )
        }
    )
}

Since compose function children are placed inside the lambdas of the slot api, they will only be recomposed if their input changes.

Upvotes: 0

Related Questions