在Android上使用SVG图标的最佳实践是什么?

84

我即将创建我的第一个Android原生应用程序(因此不是基于浏览器的),并寻找一些关于图标创建/提供的良好实践。由于它应该支持多个设备/分辨率,我认为最好使用SVG来创建它们。至少有这个库:http://code.google.com/p/svg-android/ ,承诺在Android上提供对SVG的支持。

到目前为止,我还没有找到描述使用此或另一个库作为在设备上呈现SVG图标的手段的资源,因此我有点不愿使用它。到目前为止我看到的最好的方法是使用SVG作为预渲染png基础图标的源格式,并提供不同分辨率。

所以我的问题是:直接在设备上使用SVG图标而无需进行png预渲染步骤是否是一个好选择(是否起作用),如果是,为什么似乎没有人使用这种方法?

12个回答

49
从棒棒糖(API 21)开始,Android 定义了 VectorDrawable 类,用于定义基于矢量图形的可绘制对象。Android Studio 1.4 添加了“Vector Asset Studio”,使它们更易于使用,包括 SVG 导入功能和一个新的 Gradle 插件,用于在构建时为 API 20 及更早版本生成 VectorDrawable 图标的 PNG 版本。还有 第三方工具可将 SVG 转换为 VectorDrawables。请注意,尽管可以在 XML 中定义矢量可绘制对象,但文件格式不是 SVG,也不能成功转换所有 SVG 文件。简单的图形如图标应该可以正常工作。
如果你仍然需要自己生成PNG文件,你需要生成你的图标在各种分辨率下。为了方便生成这些PNG文件,我设计图标时使用SVG,然后使用Inkscape导出到各种大小,它是免费且跨平台的。它有一些很好的图标设计功能,包括图标预览视图(见下文),并且可以生成清晰的PNG文件。

enter image description here


3
很遗憾,SVG导入功能非常基本。 - Robin
2
Android团队在Android Studio 1.4中发布了矢量资产的向后兼容支持。我已经更新了答案以反映这一点。http://android-developers.blogspot.com/2015/09/android-studio-14.html - Austyn Mahoney
教程链接丢失/指向了本页面。 - V-master

32

对于早于Lollipop版本的Android,您最好的做法是使用工具将SVG转换为您感兴趣的大小的PNG。现有的Android SVG支持并不涵盖您可能在SVG文件中找到的所有内容,即使涵盖了,由于操作系统不支持直接使用SVG来制作图标。

从Lollipop(API 21)开始,请参见如何在Android上使用SVG图标的最佳实践?。感谢@MarkWhitaker和@AustynMahoney指出这一点。


1
Androidify使用了这个库,看起来非常全面。该库似乎能够生成图片和可绘制对象。是否可能利用它创建一个自定义图标类? - user462982
1
如果你正在使用的库可以很好地处理SVG,那么你可以将其用于按钮,但不能通过标准的Android API实现;你需要创建一个自定义按钮视图。大约12-18个月前,我使用了你提供的库,当时它在处理我在Inkscape中创建或从各种地方下载的许多SVG时遇到了问题。也许它的支持已经改善了,但我建议在为其编写大量自定义代码之前,先使用你计划使用的确切SVG进行测试。 - mah
@ywarnier 我已经三年没有跟上Android的发展了,所以我不知道有什么变化...但是你提出的这个答案不再适用的理由并不符合——问题明确不涉及浏览器,而是本地Android使用(其应用程序图标)。现在,如果操作系统本身支持SVG作为其图标和/或通用应用程序显示资源,我会同意你的评论。 - mah
2
这个答案现在绝对不正确。https://dev59.com/42kw5IYBdhLWcg3wyNgV#29229005 - Austyn Mahoney
1
过时的答案 - bendaf
显示剩余6条评论

31

这是我们用来将SVG文件转换为多个分辨率的工具。例如,要生成启动图标:svg2png -w48 icon.svg

#!/bin/bash -e
# Transforms a SVG into a PNG for each platform
# Sizes extracted from
# http://developer.android.com/design/style/iconography.html

[ -z $2 ] && echo -e "ERROR: filename and one dimension (-w or -h) is required, for example:\nsvg2png -w48 icon.svg\n" && exit 1;
FILENAME=$2
DEST_FILENAME=`echo $2 | sed s/\.svg/\.png/`
FLAG=`echo $1 | cut -c1-2`
ORIGINAL_VALUE=`echo $1 | cut -c3-`

if [ "$FLAG" != "-w" ] && [ "$FLAG" != "-h" ]; then
    echo "Unknown parameter: $FLAG" 
    exit 1
fi

# PARAMETERS: {multiplier} {destination folder}
function export {
  VALUE=$(echo "scale=0; $ORIGINAL_VALUE*$1" | bc -l)
  CMD="inkscape $FLAG$VALUE --export-background-opacity=0 --export-png=src/main/res/$2/$DEST_FILENAME src/main/svg/$FILENAME > /dev/null"
  echo $CMD
  eval $CMD
} 

export 1 drawable-mdpi
export 1.5 drawable-hdpi
export 2 drawable-xhdpi
export 3 drawable-xxhdpi
export 4 drawable-xxxhdpi

很好知道,脚本不错,非常有用。我可以避免将SVG导入GIMP或PS中。我认为你应该添加一些关于如何使用它的提示(如果文件夹不存在,则会失败)。 - j.c

16

大家好!自从安卓支持23.2库以来,我们可以在API 7及以下版本使用SVG了!

如果你只想向后兼容到棒棒糖(API 21),请查看Mark Whitaker的答案,但如果你想向下兼容,你需要将以下内容添加到你的build.gradle中:

// Gradle Plugin 2.0+ (if you using older version check the library announcement link)
android {  
    defaultConfig {  
        vectorDrawables.useSupportLibrary = true  
    }  
}  

请记住:

  • 在ImageView中,您需要使用app:srcCompat属性而不是android:src
  • 无法在StateListDrawables或其他XML Drawables中使用svg,请改为以编程方式创建。
  • 不能使用android:background属性或View.setBackgroundResource()函数,应改用View.setBackground()
  • 在通知中无法使用svg。

我已经做了这个,但只有在API级别> 21的设备上才能看到SVG。这里出了什么问题? - Morteza Rastgoo
1
你在SVG中使用颜色属性吗?你可以创建一个新问题,然后在那里插入SVG和你的build.gradle文件吗? - bendaf
1
@Morteza,如果这个答案对你仍然不起作用,请查看以下答案:https://dev59.com/klsW5IYBdhLWcg3w8q_2#36661438 - bendaf

8

自从nacho-coloma的回答帮助了我,我采用了他出色的脚本,并使其在日常使用中稍微容易一些。

首先:

  1. 在您的res目录旁边创建一个名为drawable-svg的目录。
  2. 将您的svg文件和此脚本放置在drawable-svg中。
  3. 使脚本可执行。
  4. 运行它。在Ubuntu中,您可以在Nautilus中双击它并在终端中运行它。

以后当您获得新的svg文件时:

  1. 将新的svg文件放置在drawable-svg中,然后再次运行脚本。

默认情况下,它会做你想要的事情:将每个svg文件缩放为png文件,并将它们放入../res/drawable-mdpi../res/drawable-hdpi等中。

该脚本接受两个参数:

  1. 要缩放的svg文件模式,默认值:*.svg
  2. 放置的基本目录,默认值为../res/(即上述设置中的res目录)。

您可以通过像这样将单个svg缩放为png在当前目录中进行实验:

$ ./svg2png test.svg .

或者简单地处理所有图像:

$ ./svg2png

我猜你可以将drawable-svg放在res目录中,但我还没有研究最终APK中包含什么。此外,我的svg文件名中有-,这是Android不喜欢的,我的脚本负责将png文件重命名为Android上有效的名称。
我使用ImageMagick进行转换,它比Inkscape稍微标准一些(尽管我喜欢这种方法)。脚本中包括两种方法以供参考。
以下是脚本内容:
#!/bin/bash

scalesvg ()
{
    svgfile="$1"
    pngdir="$2"
    pngscale="$3"
    qualifier="$4"

    svgwidthxheight=$(identify "$svgfile" | cut -d ' ' -f 3)
    svgwidth=${svgwidthxheight%x*}
    svgheight=${svgwidthxheight#*x}

    pngfile="$(basename $svgfile)" # Strip path.
    pngfile="${pngfile/.svg/.png}" # Replace extension.
    pngfile="${pngfile/[^A-Za-z0-9._]/_}" # Replace invalid characters.
    pngfile="$pngdir/$qualifier/$pngfile" # Prepend output path.

    if [ ! -d $(dirname "$pngfile") ]; then
        echo "WARNING: Output directory does not exist: $(dirname "$pngfile")"
        #echo "Exiting"
        #exit 1
        echo "Outputting here instead: $pngfile"
        pngfile="$qualifier-${svgfile/.svg/.png}"
    fi

    pngwidth=$(echo "scale=0; $svgwidth*$pngscale" | bc -l)
    pngheight=$(echo "scale=0; $svgheight*$pngscale" | bc -l)
    pngdensity=$(echo "scale=0; 72*$pngscale" | bc -l) # 72 is default, 

    echo "$svgfile ${svgwidth}×${svgheight}px -> $pngfile ${pngwidth}×${pngheight}px @ $pngdensity dpi"

    convert -background transparent -density $pngdensity "$svgfile" "$pngfile"
    #inkscape -w${pngwidth} --export-background-opacity=0 --export-png="$pngfile" "$svgfile" > /dev/null
    #convert "$svgfile" -background transparent -scale ${pngwidth}x${pngheight} "$pngfile"
}



svgfiles="$1"
svgfiles="${svgfiles:=*.svg}" # Default to input all *.svg in current dir.

pngdir="$2"
pngdir="${pngdir:=../res}" # Default to place output pngs to ../res, ie. ../res/drawable-hdpi etc.

for svgfile in $svgfiles; do
    echo "Scaling $svgfile ..."
    scalesvg "$svgfile" "$pngdir" 0.75 drawable-ldpi
    scalesvg "$svgfile" "$pngdir" 1    drawable-mdpi
    scalesvg "$svgfile" "$pngdir" 1.5  drawable-hdpi
    scalesvg "$svgfile" "$pngdir" 2    drawable-xhdpi
    scalesvg "$svgfile" "$pngdir" 3    drawable-xxhdpi
    scalesvg "$svgfile" "$pngdir" 4    drawable-xxxhdpi
done

echo -n "Done."
read # I've made it wait for Enter -- convenient when run from Nautilus.

6

5

我已经做了这个,但只有在API级别> 21的设备上才能看到SVG。这里出了什么问题? - Morteza Rastgoo
@MortezaRastgoo 你能否把你的代码放在某个地方展示一下吗?没有代码我很难猜测。对我来说它运行得很好。 - lordmegamax

3

如果需要将SVG图标缩放到很多不同的大小,那么在设备上直接使用它们并不是一个好的选择,这通常是你想首先使用矢量格式的原因。一个大的图标永远不会优雅地缩小,因为电脑显示器由像素组成。所以矢量图像的线条可能会被对齐“在像素之间”,从而创建模糊的边框。此外,大图标需要比小图标更多的细节,而小图标需要很少的细节。一个详细的图标在非常小的尺寸下看起来并不好,而一个简单的图标在缩放到非常大的尺寸时也不好看。我最近读了一篇由专业的UI设计师撰写的优秀文章:关于那些矢量图标


你可以通过 shape-rendering 在一定程度上控制这些事情。不过支持度比较稀少。 - Joey
谢谢@Joey,这很好知道。然而,即使如此,您也无法指定如果图像被渲染得足够小,则应完全删除元素。 - ZeroOne
4
应用程序中图标大小的差异通常是由目标设备的不同像素密度引起的 - 因此它们在视觉上大致相同。因此,它们实际上不需要根据大小进行更多或更少的细节处理,至少在这种情况下不需要。我目前正在使用 SVG 作为工作中两个 Android 应用程序中图标的源格式,您唯一需要注意的是将边缘与基线大小(mdpi)的像素边界对齐。Android 还建议图标在基线大小上至少使用两个像素用于线条。 - Joey
1
我使用Inkscape以所需的分辨率呈现它们;由于我们的艺术家已经绘制了矢量图标,这是最简单的方法。它还允许在运行时生成不透明度变化的颜色(例如启用/禁用图标对),这是SVG的一个巨大优势。当缩小最大的PNG时,差异很小,但微妙可见(因为您也在缩小抗锯齿)。 - Joey
1
你只需要为超低分辨率提供一个版本,而其他所有分辨率只需要提供一个版本。相比之下,现在为Android提供5-6个不同的位图是多余的。此外,为什么要限制支持的图像格式:让用户决定他们喜欢哪种格式。 - velis
显示剩余4条评论

2
我刚开始使用Victor,这是Trello开源的一个库,在构建时将SVG文件转换为各种所需分辨率的PNG文件。
优点:
  • 每次更改或添加图标时,您不必运行脚本或工具来创建各种PNG文件。(当您添加新的svg文件或重命名现有文件时,需要在Android Studio中点击“重新构建”)
  • 源代码中没有PNG文件,因此更加清晰。
缺点:
  • 到目前为止,唯一的缺点是Android Studio尚未识别XML中生成的资源,因此您的XML文件会出现一些红色警告,并且您的基于SVG的可绘制对象没有自动完成。但是它可以正常构建,这个问题应该会在未来的Android Studio版本中得到解决。
如果您使用http://materialdesignicons.com/生成的SVG,请确保要么下载整个文件,要么从“查看SVG”选项卡中复制。

2

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