在Android上使用SVG图标的最佳做法是什么?


75

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

到目前为止,我还没有发现描述此库或另一个库的用法的资源,该方法可以在设备上呈现SVG图标,因此我不太愿意使用它。到目前为止,我所看到的最好的方法是使用SVG作为以不同分辨率预渲染基于png的图标的源格式。

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

Answers:


30

对于Lollipop之前的Android,您在Android上SVG的最佳做法是使用一种工具将您的SVG转换为您感兴趣的大小。现有的SVG对Android的支持并不全面“很可能会在SVG文件中找到它,即使它存在,支持也不是内置在操作系统中的,因此绝对不能将它们直接用作图标。

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


1
Androidify使用此库,并且看起来很全面。lib似乎能够产生图片和可绘制对象。可以使用它来创建一个利用此的自定义图标类吗?
user462982

如果您指的图标是启动器中显示的内容,否。Android不允许您指定生成图标的方法,它仅允许您通过其资源来标识图标。
2012年

1
如果您正在使用的库可以处理您所拥有的SVG,则可以使其适用于按钮,但不能通过标准的Android API来使用。您需要创建一个自定义按钮视图。大约12至18个月前,我使用了您链接的库,当时它与我在Inkscape中创建或从不同地方下载的许多SVG出现问题。从那时起,也许它的支持得到了改善,但是我建议您在计划编写大量自定义代码之前,使用计划使用的确切SVG对其进行测试。
2012年

2
这个答案现在肯定是不正确的。stackoverflow.com/a/29229005/116938
Austyn Mahoney

1
过时的答案
bentaf

47

从Lollipop(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


2
不幸的是,SVG导入功能非常非常基础。
罗宾(Robin)2015年

2
Android团队已在Android Studio 1.4中发布了对Vector Assets的向后兼容性支持。我已经更新了答案以反映这一点。android-developers.blogspot.com/2015/09/android-studio-14.html
Austyn Mahoney

教程链接丢失/通往这个页面
V-主

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。我认为您应该添加一些有关如何使用它的提示(如果文件夹不存在,它将失败)。
jc 2016年

16

大家好消息!从android支持库23.2开始,我们可以使用svg-s直到API级别7

如果您只想向后兼容直到Lollipop(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  
    }  
}  

还请记住:

  • 而不是android:src您需要app:srcCompat在ImageViews中使用该属性。
  • 您不能在StateListDrawables或其他xml drawable中使用svg -s,而是以编程方式创建它们。
  • 您不能使用android:background属性或View.setBackgroundResource()函数,请使用View.setBackground()改用。
  • 如果是通知,则不能使用svg -s。

我已经做到了,但是我只在api级> 21的设备上看到svg,这是怎么了?
Morteza Rastgoo,

1
您是否在svg中使用颜色属性?您能创建一个新问题并在其中插入svg和您的build.gradle文件吗?
bentaf '16

1
@Morteza仍然
无法解决

8

自从nacho-coloma的回答对我有所帮助以来,我就采用了他的出色脚本并将其日常使用变得更加简单。

第一:

  1. 在目录drawable-svg旁边创建res目录。
  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.


5

Android支持库23.2支持矢量可绘制对象和动画矢量可绘制对象

  1. 添加vectorDrawables.useSupportLibrary = true到您的build.gradle文件。
  2. 用于 app:srcCompat="@drawable/ic_add"代替android:src="..."setImageResource()用于ImageView

http://android-developers.blogspot.sk/2016/02/android-support-library-232.html


我已经做到了,但是我只在api级别> 21的设备上看到svg,这是怎么了?
Morteza Rastgoo,

@MortezaRastgoo您能否在某个地方显示您的代码?没有它,我无法猜测。这对我来说很有用。
lordmegamax

3

如果需要将SVG图标缩放到许多不同的尺寸,则不是直接在设备上使用的SVG图标,这通常就是为什么首先要使用矢量格式的原因。大图标永远不会优雅地缩小比例,因为计算机显示器是由像素组成的。因此,矢量图像的线条可能会“在像素之间”对齐,从而产生模糊的边框。此外,大图标比小图标需要更多的细节,而小图标则需要很少的细节。详细的图标在很小的尺寸下看起来不太好,而简单的图标在缩放到非常大的尺寸时看起来不太好。我最近阅读了一位专业UI设计师关于此的出色文章:关于这些矢量图标


您可以使用来在一定程度上控制这些东西shape-rendering。但是,支持很少。
乔伊(Joey)2013年

谢谢@Joey,很高兴知道。但是,即使这样也不允许您指定如果图像渲染得足够小,则应完全删除一个元素。
ZeroOne

4
我刚刚想到的随机现象:应用程序中图标大小的变化通常是由于目标设备的像素密度不同所致,因此它们的外观大小大致相同。因此,实际上实际上不必根据大小来详细说明。至少不是在这种情况下。我目前正在使用SVG作为两个工作中的Android应用程序中图标的源格式,您可能需要注意的唯一事情是将边缘与基线大小(mdpi)的像素边界对齐。Android还建议图标在基线大小的线条上至少使用两个像素。
乔伊,

1
我正在使用Inkscape以必要的分辨率渲染它们。因为我们的艺术家无论如何都会绘制矢量图标,所以这是最简单的方法。它还允许动态生成不透明度变化的颜色(例如,启用/禁用的图标对),这是SVG的巨大优势。缩小最大的PNG时,差异很小,但是微妙可见(边缘也不太清晰,因为您也缩小了抗锯齿的比例)。
乔伊,

1
对于超低分辨率,您只需要一个版本,而对于所有其他分辨率,则只需要一个版本。与现在必须为Android提供的5-6种不同的位图?此外,为什么要限制支持的图像格式:让用户决定他们喜欢哪种格式。
velis 2014年


2

我刚刚开始使用VictorTrello的开放源代码库,在构建期间将SVG文件转换为具有各种所需分辨率的PNG文件。

优点

  • 每次更改或添加图标时,您都不必运行脚本或工具来创建各种PNG文件。(添加新的svg文件或重命名现有文件后,您确实需要在Android Studio中点击“重建”)
  • 您的源中没有PNG,因此混乱程度降低了。

缺点

  • 到目前为止,我唯一看到的缺点是Android Studio尚无法识别XML生成的资源,因此您会在XML文件中收到一些红色警告,并且基于SVG的可绘制对象没有自动完成功能。不过,它运行良好,并且此问题应在以后的Android Studio版本中修复。

如果您使用http://materialdesignicons.com/生成的SVG,请确保下载整个文件,或者在选择“查看SVG”时从“ SVG文件”选项卡复制


1

svg很棒。谁想要使用svg:

右键单击可绘制的“新/矢量资产”,在计算机硬盘驱动器中的默认图标中选择“材料图标”,在文件中选择“区域设置SVG文件”,在“资源名称”中为svg文件键入名称,然后单击“下一步”按钮和“完成”

您可以在drawable中使用它。fillcolor必须是硬代码。

简单的例子

navigation_toggle.xml

<vector xmlns:android="http://schemas.android.com/apk/res/android"
        android:width="24dp"
        android:height="24dp"
        android:viewportWidth="24.0"
        android:viewportHeight="24.0">
    <path
        android:fillColor="#FFFFFF"
        android:pathData="M3,18h18v-2H3v2zm0,-5h18v-2H3v2zm0,-7v2h18V6H3z"/>
</vector>

0

我从来没有在Windows的Cygwin中运行Linux Shell脚本的运气。因此,这里有一个批处理文件,可以完成Nacho Coloma的bash脚本的工作。一个小的区别是,此批处理文件需要输入文件名和输出文件名,例如“ svg2png -w24 input.svg output.png”。

根据Stephan的指示,在项目的src / main目录中设置一个“ svg”文件夹,然后将SVG文件和此批处理文件复制到该文件夹​​中。从svg文件夹运行批处理文件。如果您使用的是32位Windows,则可能需要将Inkscape的路径更改为使用“程序文件(x86)”。

@echo off
echo Convert an SVG file to a PNG resource file with multiple resolutions.

rem Check the arguments
set temp=%1
set switch=%temp:~0,2%
set pixels=%temp:~2%
if not "%switch%"=="-w" (
if not "%switch%"=="-h" (
echo Error:  Invalid image width or height switch.  Use -w or -h, with target image size in dp appended.
goto :error
))
echo %pixels%| findstr /r /c:"^[1-9][0-9]*$" >nul
if errorlevel 1 (
echo Error:  Invalid numeric image size.  Image size must be a positive integer.
goto :error
)
if "%3"=="" (
echo Error:  Not enough arguments.
goto :error
)
if not "%4"=="" (
echo Error:  Too many arguments.
goto :error
)

call :export %1 %2 %3 mdpi
call :export %1 %2 %3 hdpi
call :export %1 %2 %3 xhdpi
call :export %1 %2 %3 xxhdpi
call :export %1 %2 %3 xxxhdpi
exit /b

:export
rem parameters: <width/height> <input-file> <output-file> <density>

set temp=%1
set switch=%temp:~0,2%
set pixels=%temp:~2%

if %4==mdpi set /a size=%pixels%
if %4==hdpi set /a size=%pixels%*3/2
if %4==xhdpi set /a size=%pixels%*2
if %4==xxhdpi set /a size=%pixels%*3
if %4==xxxhdpi set /a size=%pixels%*4

echo %size% pixels ../res/drawable-%4/%3
"C:\Program Files\Inkscape\inkscape.exe" %switch%%size% --export-background-opacity=0 --export-png=../res/drawable-%4/%3 %2
exit /b

:error
echo Synopsis: svg2png -w^<width-pixels^>^|-h^<height-pixels^> ^<input-file^> ^<output-file^>
echo Example:  svg2png -w24 "wifi white.svg" wifi_connect_24dp.png
exit /b
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.