Osama Omar
Osama Omar

Reputation: 119

How to set an ImageVector from resource?

I'm trying to set an icon for bottom navigation like so:

sealed class Screen(val route: String, val label: String, val icon: ImageVector) {
    object Home : Screen("home", "Home",R.drawable.outline_home_black_24)
    object History : Screen("history", "History", R.drawable.outline_history_black_24)
}

But it says I need to switch the parameter to Int. Help is appreciated, thanks. :)

Upvotes: 4

Views: 11359

Answers (5)

Yahor Urbanovich
Yahor Urbanovich

Reputation: 773

You can convert your xml resource into Compose ImageVector using Valkyrie

and set it into Icon

Icon(imageVector = YourIcon, contentDescription = null)

Upvotes: 1

Zoran
Zoran

Reputation: 2228

You should use ImageVector.vectorResource function like this:

import androidx.compose.ui.res.vectorResource


sealed class Screen(val route: String, val label: String, val icon: ImageVector) {
     object Home : Screen("home", "Home",ImageVector.vectorResource(R.drawable.outline_home_black_24))
     object History : Screen("history", "History", ImageVector.vectorResource(R.drawable.outline_history_black_24))
}

Upvotes: 10

Ygor Frazão
Ygor Frazão

Reputation: 143

Not quite the answer for the question in place, but, maybe can help someone in need...

If you are using Jetpack Compose and want flexibility between using ImageVector, as Icons.Filled.Favorite and using a drawable resource, you can achieve this by using a helper class, like the following:

class IconResource private constructor(
    @DrawableRes private val resID: Int?,
    private val imageVector: ImageVector?
) {

    @Composable
    fun asPainterResource(): Painter {
        resID?.let {
            return painterResource(id = resID)
        }
        return rememberVectorPainter(image = imageVector!!)
    }

    companion object {
        fun fromDrawableResource(@DrawableRes resID: Int): IconResource {
            return IconResource(resID, null)
        }

        fun fromImageVector(imageVector: ImageVector?): IconResource {
            return IconResource(null, imageVector)
        }
    }
}

With that class you can create your sealed class accordingly:

sealed class Screen(val route: String, @StringRes val resourceId: Int, icon: IconResource) {
    object Home : Screen(
        "home", R.string.something,
        IconResource.fromDrawableResource(R.drawable.outline_home_black_24)
    )

    object History : Screen(
        "history", R.string.something,
        IconResource.fromImageVector(Icons.Filled.Favorite)
    )
}

Then, just call in your composable:

navigationItems.forEach { screen ->
    BottomNavigationItem(icon = {
        Icon(
            screen.icon.asPainterResource(),
            contentDescription = null
        )
    }

    ...
}

UPDATE

I've released a lib with that implementation, if you need it, can be found here: https://github.com/ygorluizfrazao/compose-resources

Hope it helps.

Upvotes: 8

Decline
Decline

Reputation: 352

Try this

sealed class Screen(val route: String, val label: String, val icon: ImageVector) {
    object Home : Screen("home", "Home", painterResource(id = R.drawable.outline_home_black_24))
    object History : Screen("history", "History", painterResource(id = R.drawable.outline_history_black_24))
}

Upvotes: 2

Robin
Robin

Reputation: 1345

Yes. Refrences like R.drawable.outline_home_black_24 is not the actual ImageVector but Int references to help get them in code. To get the actual image you should use something like ContextCompat.getDrawable(context, R.drawable.***) to get the actual Drawable file. This means that the correct usage should be

sealed class Screen(val route: String, val label: String, @DrawableRes val icon: Int)

The extra annotation throws a warning if a drawable resource which is something like R.drawable.*** is not passed

Upvotes: 3

Related Questions