我正在尝试在我的 Android 应用程序中使用矢量绘图。来自 http://developer.android.com/training/material/drawables.html(强调我的):
在 Android 5.0(API 级别 21)及更高版本中,您可以定义矢量可绘制对象,它可以缩放而不会丢失定义。
使用这个drawable:
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="24dp"
android:width="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path android:fillColor="@color/colorPrimary" android:pathData="M14,20A2,2 0 0,1 12,22A2,2 0 0,1 10,20H14M12,2A1,1 0 0,1 13,3V4.08C15.84,4.56 18,7.03 18,10V16L21,19H3L6,16V10C6,7.03 8.16,4.56 11,4.08V3A1,1 0 0,1 12,2Z" />
和这个 ImageView:
<ImageView
android:layout_width="400dp"
android:layout_height="400dp"
android:src="@drawable/icon_bell"/>
尝试以 400dp 显示图标时会产生此模糊图像(在 2015 年左右运行棒棒糖的较大高分辨率移动设备上):
https://i.stack.imgur.com/ELOQi.png
将可绘制矢量定义中的宽度和高度更改为 200dp 可显着改善 400dp 渲染大小的情况。但是,将其设置为 TextView 元素的可绘制对象(即文本左侧的图标)现在会创建一个巨大的图标。
我的问题:
1) 为什么矢量可绘制对象中有宽度/高度规范?我认为这些的全部意义在于它们无损地放大和缩小,使得宽度和高度在其定义中毫无意义?
2) 是否可以使用单个矢量可绘制对象,它在 TextView 上用作 24dp 可绘制对象,但也可以很好地扩展以使用更大的图像?例如,我如何避免创建多个不同大小的矢量可绘制对象,而是使用一个可以缩放到我的渲染要求的对象?
3) 如何有效地使用宽度/高度属性,与 viewportWidth/Height 有什么区别?
额外细节:
设备正在运行 API 22
将 Android Studio v1.5.1 与 Gradle 版本 1.5.0 一起使用
清单是编译和目标级别 23,最低级别 15。我也尝试将最低级别移动到 21,但这没有任何区别。
反编译 APK(最低级别设置为 21)会在可绘制文件夹中显示单个 XML 资源。不产生光栅化图像。
New -> Vector Asset
时,它会将矢量图像 XML 拖放到我的可绘制文件夹中。但是,如果我使用 apktool 解压缩构建的 APK,我会看到 XML 文件位于 drawable-anydpi-v21
中,并且可以在 API 21+ 设备上正确缩放。光栅文件放置在 drawable-<mdpi/hdpi/etc>-v4
文件夹中,不用于 API 21+ 设备(基于它们正确缩放的事实)
drawable
文件夹。矢量可绘制 xml 文件中的宽度/高度为 24dp。指定 400dp 高度/宽度的 ImageView 肯定会创建一个缩放不良的图像。
drawable-anydpi-v21
的唯一原因是因为我的 minSdkVersion
小于 21。如果您将 minSdkVersion
设置为小于 21,行为会有什么变化吗?将 XML 移动到 drawable-anydpi
怎么样?我不希望会有变化,但我也希望您的矢量图像能够正确缩放......
drawable-anydpi-v21
中的单个 xml 文件,在 mdi/hdpi/etc 中包含各种光栅化图像。文件夹。但是最终渲染结果没有变化。
这里有关于这个问题的新信息:
https://code.google.com/p/android/issues/detail?id=202019
看起来使用 android:scaleType="fitXY"
会使其在 Lollipop 上正确缩放。
来自谷歌工程师:
嗨,让我知道 scaleType='fitXY' 是否可以成为您的解决方法,以使图像看起来清晰。棉花糖 Vs Lollipop 是由于在棉花糖中添加了特殊的缩放处理。另外,对于您的评论:“正确的行为:vector drawable 应该在没有质量损失的情况下缩放。因此,如果我们想在我们的应用程序中使用 3 种不同尺寸的相同资产,我们不必复制 vector_drawable.xml 3 次不同的硬编码大小。”尽管我完全同意应该是这种情况,但实际上,Android 平台存在性能问题,以至于我们还没有达到理想的世界。因此,如果您确定要同时在屏幕上绘制 3 个不同的尺寸,实际上建议使用 3 个不同的 vector_drawable.xml 以获得更好的性能。技术细节基本上是我们在钩子下使用位图来缓存复杂的路径渲染,这样我们可以获得最佳的重绘性能,与重绘位图drawable相当。
1) 为什么矢量可绘制对象中有宽度/高度规范?我认为这些的全部意义在于它们无损地放大和缩小,使得宽度和高度在其定义中毫无意义?
对于低于 21 的 SDK 版本,构建系统需要生成光栅图像,并且在您未指定宽度/高度的情况下将其作为默认大小。
2) 是否可以使用单个矢量可绘制对象作为 TextView 上的 24dp 可绘制对象以及大的近屏幕宽度图像?
如果您还需要针对小于 21 的 SDK,我认为这是不可能的。
3) 如何有效地使用宽度/高度属性,与 viewportWidth/Height 有什么区别?
Documentation:(现在我重新阅读它实际上不是很有用......)
android:width 用于定义drawable的内在宽度。这支持所有维度单位,通常用 dp 指定。 android:height 用于定义drawable的内在高度。这支持所有维度单位,通常用 dp 指定。 android:viewportWidth 用于定义视口空间的宽度。视口基本上是绘制路径的虚拟画布。 android:viewportHeight 用来定义视口空间的高度。视口基本上是绘制路径的虚拟画布。
Android 4.4(API 级别 20)及更低版本不支持矢量绘图。如果您的最低 API 级别设置为这些 API 级别之一,Vector Asset Studio 还会指示 Gradle 生成矢量可绘制对象的光栅图像以实现向后兼容性。您可以将矢量资源称为 Java 代码中的 Drawable 或 XML 代码中的 @drawable;当您的应用程序运行时,相应的矢量或光栅图像会根据 API 级别自动显示。
编辑:发生了一些奇怪的事情。这是我在模拟器 SDK 版本 23 中的结果(Lollipop+ 测试设备现在已经死了......):
https://i.stack.imgur.com/TwDm1.png
在模拟器 SDK 版本 21 中:
https://i.stack.imgur.com/NmK51.png
xml
中删除 width
height
viewport
height
和 width
吗?
AppCompat 23.2 为所有运行 Android 2.1 及更高版本的设备引入了矢量可绘制对象。无论矢量绘图的 XML 文件中指定的宽度/高度如何,图像都能正确缩放。不幸的是,API 级别 21 及更高级别没有使用此实现(有利于本机实现)。
好消息是我们可以强制 AppCompat 在 API 级别 21 和 22 上使用它的实现。坏消息是我们必须使用反射来做到这一点。
首先,确保您使用的是最新版本的 AppCompat,并且您已启用新的矢量实现:
android {
defaultConfig {
vectorDrawables.useSupportLibrary = true
}
}
dependencies {
compile 'com.android.support:appcompat-v7:23.2.0'
}
然后,在应用程序的 onCreate() 中调用 useCompatVectorIfNeeded();
:
private void useCompatVectorIfNeeded() {
int sdkInt = Build.VERSION.SDK_INT;
if (sdkInt == 21 || sdkInt == 22) { //vector drawables scale correctly in API level 23
try {
AppCompatDrawableManager drawableManager = AppCompatDrawableManager.get();
Class<?> inflateDelegateClass = Class.forName("android.support.v7.widget.AppCompatDrawableManager$InflateDelegate");
Class<?> vdcInflateDelegateClass = Class.forName("android.support.v7.widget.AppCompatDrawableManager$VdcInflateDelegate");
Constructor<?> constructor = vdcInflateDelegateClass.getDeclaredConstructor();
constructor.setAccessible(true);
Object vdcInflateDelegate = constructor.newInstance();
Class<?> args[] = {String.class, inflateDelegateClass};
Method addDelegate = AppCompatDrawableManager.class.getDeclaredMethod("addDelegate", args);
addDelegate.setAccessible(true);
addDelegate.invoke(drawableManager, "vector", vdcInflateDelegate);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
最后,确保您使用 app:srcCompat
而不是 android:src
来设置 ImageView 的图像:
<ImageView
android:layout_width="400dp"
android:layout_height="400dp"
app:srcCompat="@drawable/icon_bell"/>
您的矢量绘图现在应该可以正确缩放了!
为什么矢量可绘制对象中有宽度/高度规范?我认为这些的全部意义在于它们无损地放大和缩小,使得宽度和高度在其定义中毫无意义?
这只是向量的默认大小,以防您未在布局视图中定义它。 (即您使用包装内容作为图像视图的高度和宽度)
是否可以使用单个矢量可绘制对象作为 TextView 上的 24dp 可绘制对象以及大的近屏幕宽度图像?
是的,这是可能的,只要正在运行的设备使用棒棒糖或更高版本,我在调整大小方面没有任何问题。在之前的 API 中,当您构建应用程序时,矢量会针对不同的屏幕尺寸转换为 png。
如何有效地使用 width/height 属性,viewportWidth/Height 有什么区别?
这会影响向量周围空间的使用方式。即您可以使用它来更改视口内矢量的“重力”,使矢量具有默认边距,将矢量的某些部分留在视口之外,等等......通常,您只需将它们设置为相同尺寸。
我解决了我的问题只是改变了矢量图像的大小。从一开始就是这样的资源:
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="24dp"
android:height="24dp"
android:viewportHeight="300"
android:viewportWidth="300">
我已将其更改为 150dp(具有此矢量资源的布局中的 ImageView 大小),例如:
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="150dp"
android:height="150dp"
android:viewportHeight="300"
android:viewportWidth="300">
它对我有用。
我尝试了这些方法,但不幸的是,它们都没有奏效。我在 ImageView
中尝试 fitXY
,我尝试使用 app:srcCompat
但甚至无法使用它。但我找到了一个窍门:
将 Drawable 设置为 ImageView 的背景。
但这种方式的重点是视图维度。如果您的 ImageView
尺寸不正确,drawable 会拉伸。您必须在棒棒糖之前的版本中控制它或使用某些视图 PercentRelativeLayout
。
希望它有帮助。
首先 : 将 svg 导入 drawble : ((https://www.learn2crack.com/2016/02/android-studio-svg.html))
1-右键单击可绘制文件夹选择新建->矢量资产
https://i.stack.imgur.com/SVrpH.png
2- Vector Asset Studio 为您提供选择内置材质图标或您自己的本地 svg 文件的选项。如果需要,您可以覆盖默认大小。
https://i.stack.imgur.com/4MvVN.png
第二 :如果你想得到android API 7+的支持,你必须使用Android Support Library acording ((https://stackoverflow.com/a/44713221/1140304))
1-添加
vectorDrawables.useSupportLibrary = true
到您的 build.gradle 文件。
2-在您的布局中使用具有 imageView 的命名空间:
xmlns:app="http://schemas.android.com/apk/res-auto"
第三:使用 android:scaleType="fitXY" 设置 imageView 并使用 app:srcCompat
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="fitXY"
app:srcCompat="@drawable/ic_menu" />
第四:如果你想在java代码中设置svg,你必须在oncreate方法上添加以下行
@Override
protected void onCreate(Bundle savedInstanceState)
{
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
对我来说,导致矢量转换为 PNG 的原因是我的矢量可绘制对象中的 android:fillType="evenodd"
属性。当我删除了我的可绘制对象中所有出现的此属性(因此默认的 nonzero
填充类型应用于矢量),下次构建应用程序时一切都很好。
除了最重要的答案之外,在我这边移动到 ConstraintLayout 而不是 LinearLayout 或 FrameLayout 消除了 SVG 矢量可绘制的模糊。
通过添加解决
defaultConfig {
vectorDrawables.useSupportLibrary = true
}
到 build.gradle (module:app) 文件和 imageViews 替换
android:src="..."
和
app:srcCompat="..."
希望帮助
不定期副业成功案例分享