Jetpack Compose:自定义 VectorAsset Icon 对象,类似于内置 `Icons.Default`

问题描述 投票:0回答:5

看起来从

res
文件夹中的 Android 矢量资源加载自定义图标的唯一方法是使用
@Composable
方法在
vectorResource(R.drawable.myVectorName)
函数中执行此操作。

这很棒,但我喜欢为

VectorAssets
类获取
Icon(asset: VectorAsset)
的语法,它看起来像
Icon(Icons.Default.Plus)

看起来

vectorResource()
方法使用了一个名为 loadVectorResource()
internal
方法,并且它用来读取构成矢量资源文件的实际 XML 文件的方法也是内部的。

我将如何在 Jetpack Compose 中创建像

MyAppIcons.Default.SomeIcon
这样的对象?

编辑

所以,我已经找到了解决方案。但是,最好对内置

Icon()
函数进行自己的扩展/重载,但我不确定是否有正确的方法来做到这一点。

android kotlin android-jetpack-compose kotlin-android-extensions android-vectordrawable
5个回答
23
投票

来自 Compose 中的资源

使用

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

// 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()
is 是一个可组合函数,这意味着当然可以在那里使用 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)

我希望 Google 尽快添加此覆盖,允许我们传入资源 ID。


3
投票

我沿着另一条路线,从 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
设置为可绘制 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
对象。


0
投票

对我有用的方法是向字段添加一个

@Composable
getter,如下所示:

object Icons {
    val Close: VectorIcon
       @Composable get(): ImageVector.vectorResource(R.drawable.close)
}

然后可以按照与 compose 框架的其余部分一致的方式使用它们,例如:

Icon(Icons.Close, contentDescription = "Close")
最新问题
© www.soinside.com 2019 - 2024. All rights reserved.