#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:
Используйте 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")
}
}