Jetpack Compose:自定义VectorAsset图标对象类似于内置的`Icons.Default`。

12
似乎从Android矢量资源中加载自定义图标的唯一方法是在res文件夹中使用@Composable函数,并使用vectorResource(R.drawable.myVectorName)方法进行操作。
这很好,但我喜欢获取Icon(asset: VectorAsset)类的VectorAssets的语法,看起来像Icon(Icons.Default.Plus)
似乎vectorResource()方法使用一个名为loadVectorResource()内部方法,以及用于读取组成矢量资源文件的实际XML文件的方法也是内部的。
在Jetpack Compose中,如何创建像MyAppIcons.Default.SomeIcon这样的对象?
编辑:
因此,我差不多找到了一个解决方案。但是,如果有一种适当的方法可以做到这一点,那么制作自己的扩展/重载内置的Icon()函数将是不错的。
4个回答

21

来源:Compose 中的资源

使用 painterResource API 来加载矢量可绘制对象或像 PNG 这样的光栅化资源格式。您不需要知道可绘制对象的类型,只需在 Image 组合或 paint 修饰符中使用 painterResource 即可。

// Files in res/drawable folders. For example:
// - res/drawable-nodpi/ic_logo.xml
// - res/drawable-xxhdpi/ic_logo.png

// In your Compose code
Icon(
    painter = painterResource(id = R.drawable.ic_logo),
    contentDescription = null // decorative element
)

15

结果表明我没有用好我的大脑。答案很简单。

要点是,Icon()是一个可组合的函数,这意味着当然可以在其中使用vectorResource()

因此,正确的方法并不秘密......它就是制作自己的MyAppIcon()组件,调用 vectorResource() 然后返回一个普通的 Icon(),就像这样:

正确的方法

@Composable
fun MyAppIcon(
    resourceId: Int,
    modifier: Modifier = Modifier,
    tint: Color = AmbientContentColor.current
) {
    Icon(
        asset = vectorResource(id = resourceId),
        modifier = modifier,
        tint = tint
    )
}

接下来您可以在其他地方创建对象,例如:

object MyAppIcons {
    val SomeIcon = R.drawable.someIcon
    val AnotherIcon = R.drawable.anotherIcon
}

当你将这两个组合在一起时,可以像这样使用它:

MyAppIcon(MyAppIcons.SomeIcon)

我希望谷歌很快就能添加这个覆盖,让我们可以传递资源ID。


1

我选择了另一种方法,从Jetpack Compose源代码中提取逻辑,将XML SVG路径字符串转换为ImageVector。最终,我得出了以下结果:

fun makeIconFromXMLPath(
    pathStr: String,
    viewportWidth: Float = 24f,
    viewportHeight: Float = 24f,
    defaultWidth: Dp = 24.dp,
    defaultHeight: Dp = 24.dp,
    fillColor: Color = Color.White,
): ImageVector {
    val fillBrush = SolidColor(fillColor)
    val strokeBrush = SolidColor(fillColor)

    return ImageVector.Builder(
        defaultWidth = defaultWidth,
        defaultHeight = defaultHeight,
        viewportWidth = viewportWidth,
        viewportHeight = viewportHeight,
    ).run {
        addPath(
            pathData = addPathNodes(pathStr),
            name = "",
            fill = fillBrush,
            stroke = strokeBrush,
        )
        build()
    }
}

你只需调用此函数并将pathStr设置为来自drawable XML文件的android:pathData的值即可。以下是一个例子:

val AppleIcon by lazy { makeAppleIcon() }

// by Austin Andrews, found on https://materialdesignicons.com/
private fun makeAppleIcon(): ImageVector {
    return makeIconFromXMLPath(
        pathStr = "M20,10C22,13 17,22 15,22C13,22 13,21 12,21C11,21 11,22 9,22C7,22 2,13 4,10C6,7 9,7 11,8V5C5.38,8.07 4.11,3.78 4.11,3.78C4.11,3.78 6.77,0.19 11,5V3H13V8C15,7 18,7 20,10Z"
    )
}

@Preview
@Composable
fun AppleIconPreview() {
    Surface { 
        Icon(AppleIcon, "Apple")
    }
}

enter image description here


1

有一种方法可以使用Icon(Icons.Default.Plus)来加载资源。您需要创建一个扩展属性。

val androidx.compose.material.icons.Icons.Filled.FiveG : VectorAsset
    get() {

    }

但我不知道如何在组合函数之外获取VectorAsset。当然,你可以像这样做。
val androidx.compose.material.icons.Icons.Filled.FiveG : VectorAsset
    get() {
        return Assets.FiveG
    }

object Assets {
    lateinit var FiveG: VectorAsset
}

@Composable
fun initializeAssets() {
    Assets.FiveG = vectorResource(R.drawable.ic_baseline_5g_24)
}

但是在组合函数中使用带有副作用的组件是一个不好的想法。因此,我正在等待有人找到一种将SVG转换为VectorAsset Kotlin类或在组合函数之外获取VectorAsset对象的方法。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接