Jetpack Compose: пользовательский объект значка векторного набора, похожий на встроенные «Значки».По умолчанию `

#android #kotlin #android-jetpack-compose #kotlin-android-extensions #android-vectordrawable

#Android #kotlin #android-jetpack-compose #kotlin-android-extensions #android-vectordrawable

Вопрос:

Похоже, единственный способ загрузить пользовательские значки из векторных ресурсов Android в res папку — это сделать это в @Composable функции, используя vectorResource(R.drawable.myVectorName) метод.

Это здорово и все, но мне нравится синтаксис выборки VectorAssets для Icon(asset: VectorAsset) класса, который выглядит так Icon(Icons.Default.Plus) .

Похоже vectorResource() , что метод использует вызываемый внутренний метод loadVectorResource() , и методы, которые он использует для чтения фактического XML-файла, составляющего векторный файл активов, также являются внутренними.

Как бы мне создать объект, подобный MyAppIcons.Default.SomeIcon в Jetpack Compose?

Редактировать

Итак, я вроде как нашел решение. Тем не менее, было бы неплохо создать собственное расширение / перегрузку встроенной Icon() функции, но я не уверен, есть ли правильный способ сделать это.

Ответ №1:

из ресурсов в 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
)
  

Ответ №2:

Оказывается, я не использовал свой мозг. Ответ довольно прост.

Суть в том, что 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)
  

Я надеюсь, что Google скоро добавит это переопределение, что позволит нам передавать идентификаторы ресурсов.

Ответ №3:

Существует способ загрузки ресурсов с помощью Icon(Icons.Default.Plus) . Вам нужно создать свойство extesion

 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)
}
  

но это плохая идея иметь composable с побочным эффектом. Итак, я жду, когда кто-нибудь найдет способ преобразовать SVG в VectorAsset класс Kotlin или получить VectorAsset объект вне составной функции.

Ответ №4:

Я пошел по другому пути и извлек логику из исходного кода 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 установленным значением android:pathData из XML-файла, доступного для рисования. Вот пример:

 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")
    }
}
  

введите описание изображения здесь