提问人:Chris Knight 提问时间:1/22/2016 最后编辑:Chris Knight 更新时间:7/7/2022 访问量:107848
为什么我的矢量可绘制对象缩放不符合预期?
Why isn't my vector drawable scaling as expected?
问:
我正在尝试在我的 Android 应用程序中使用矢量可绘制对象。从 http://developer.android.com/training/material/drawables.html(强调我的):
在 Android 5.0(API 级别 21)及更高版本中,您可以定义矢量可绘制对象,这些可绘制对象在不丢失定义的情况下进行缩放。
使用此可绘制对象:
<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 显示图标时(在运行 Lollipop 的大约 2015 年的大型高分辨率移动设备上)时,会产生以下模糊图像:
将矢量可绘制对象定义中的宽度和高度更改为 200dp 可显著改善 400dp 渲染大小的情况。但是,将其设置为 TextView 元素(即文本左侧的图标)的可绘制对象现在会创建一个巨大的图标。
我的问题:
1) 为什么矢量可绘制对象中有宽度/高度规格?我认为这些的全部意义在于它们无损地向上和向下缩放,使宽度和高度在其定义中毫无意义?
2) 是否可以使用单个矢量可绘制对象,该可绘制对象在 TextView 上用作 24dp 可绘制对象,但也可以很好地放大以使用更大的图像?例如,如何避免创建多个不同大小的矢量可绘制对象,而是使用一个可根据我的渲染要求缩放的可绘制对象?
3) 如何有效地使用 width/height 属性,与 viewportWidth/Height 有什么区别?
其他详细信息:
- 设备正在运行 API 22
- 将 Android Studio v1.5.1 与 Gradle 版本 1.5.0 结合使用
- 清单为编译,目标级别为 23,最低级别为 15。我也尝试将最低级别移动到 21,但这没有区别。
- 反编译 APK(最小级别设置为 21)会在 drawable 文件夹中显示单个 XML 资源。不会生成栅格化图像。
答:
1) 为什么矢量可绘制对象中有宽度/高度规格?我认为这些的全部意义在于它们无损地向上和向下缩放,使宽度和高度在其定义中毫无意义?
对于小于 21 的 SDK 版本,其中构建系统需要生成光栅图像,并且在未指定宽度/高度的情况下作为默认大小。
2) 是否可以使用单个矢量可绘制对象,该可绘制对象在 TextView 上用作 24dp 可绘制对象以及大型近屏宽度图像?
如果您还需要针对小于 21 的 SDK,我认为这是不可能的。
3) 如何有效地使用 width/height 属性,与 viewportWidth/Height 有什么区别?
文档:(实际上现在我重新阅读它不是很有用......
android:width
用于定义可绘制对象的固有宽度。这支持所有尺寸单位,通常用 dp 指定。
android:height
用于定义可绘制对象的固有高度。这支持所有尺寸单位,通常用 dp 指定。
android:viewportWidth
用于定义视口空间的宽度。视口基本上是绘制路径的虚拟画布。
android:viewportHeight
用于定义视口空间的高度。视口基本上是绘制路径的虚拟画布。
Android 4.4(API 级别 20)及更低版本不支持矢量可绘制对象。如果您的最低 API 级别设置为这些 API 级别之一,Vector Asset Studio 还会指示 Gradle 生成矢量可绘制对象的光栅图像,以实现向后兼容性。您可以像在 Java 代码或 XML 代码中引用矢量资产一样;当您的应用运行时,相应的矢量或光栅图像会自动显示,具体取决于 API 级别。
Drawable
@drawable
编辑:奇怪的事情正在发生。这是我在模拟器 SDK 版本 23 中的结果(Lollipop+ 测试设备现在已死......
在模拟器 SDK 版本 21 中:
评论
width
height
viewport
height
width
xml
- 为什么矢量可绘制对象中有宽度/高度规范?我认为这些的全部意义在于它们无损地向上和向下缩放,使宽度和高度在其定义中毫无意义?
这只是矢量的默认大小,以防您未在布局视图中定义它。(即,您将包装内容用于高度和 imageview 的宽度)
- 是否可以使用单个矢量可绘制对象,该可绘制对象在 TextView 上用作 24dp 可绘制对象以及大型近屏宽度图像?
是的,这是可能的,我在调整大小方面没有任何问题 只要正在运行的设备使用棒棒糖或更高。在上一页 API,当矢量转换为不同屏幕尺寸的 png 时 您构建应用。
- 如何有效地使用 width/height 属性,与 viewportWidth/Height 有什么区别?
这会影响矢量周围空间的使用方式。即您可以 用它来改变视口内矢量的“重力”,使 向量具有默认边距,保留向量的某些部分 在视口之外,等等...通常,您只需将它们设置为相同 大小。
评论
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();
}
}
}
最后,确保您使用的是 ImageView 的图像,而不是设置图像:app:srcCompat
android:src
<ImageView
android:layout_width="400dp"
android:layout_height="400dp"
app:srcCompat="@drawable/icon_bell"/>
您的矢量可绘制对象现在应该可以正确缩放!
评论
这里有关于这个问题的新信息:
https://code.google.com/p/android/issues/detail?id=202019
看起来使用将使它在 Lollipop 上正确缩放。android:scaleType="fitXY"
来自 Google 工程师:
嗨,让我知道 scaleType='fitXY' 是否可以成为您的解决方法,在 为了让图像看起来清晰。
棉花糖与棒棒糖是由于特殊的缩放处理 加入棉花糖。
另外,供您评论:“ 正确行为:矢量可绘制对象 应该在不损失质量的情况下进行扩展。因此,如果我们想使用相同的资产 在我们的应用程序中有 3 种不同的尺寸,我们不必复制 vector_drawable.xml 3 次,具有不同的硬编码大小。"
尽管我完全同意情况应该是这样,但实际上, Android平台存在性能问题,因此我们无法达到 理想的世界。所以实际上建议使用 3 种不同的 如果您确定需要,vector_drawable.xml以获得更好的性能 同时在屏幕上绘制 3 个不同的尺寸。
技术细节基本上是我们在钩子下使用位图 缓存复杂的路径渲染,这样我们才能得到最好的 重绘性能,与重绘位图可绘制对象相当。
评论
我必须尝试这些方法,但不幸的是,它们都没有奏效。
我尝试使用,我尝试使用,但甚至不能使用它。
但我找到了一个技巧方法:fitXY
ImageView
app:srcCompat
将 Drawable 设置为 .
background
ImageView
但这种方式的重点是视图维度。如果尺寸不正确,则可绘制对象将获得拉伸。您必须在 pre-lollipop 版本或 Use some Views 中控制它。ImageView
PercentRelativeLayout
希望对您有所帮助。
第一个:将 svg 导入到绘图:((https://www.learn2crack.com/2016/02/android-studio-svg.html))
1-右键单击可绘制文件夹,选择新建 -> 矢量资产
2- Vector Asset Studio 为您提供了选择内置的选项 材质图标或您自己的本地 svg 文件。您可以覆盖 如果需要,默认大小。
第二:如果你想得到android API 7+的支持,你必须使用android支持库((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 methoed上添加以下行
@Override
protected void onCreate(Bundle savedInstanceState)
{
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
评论
我解决了我的问题,只是更改矢量图像的大小。从一开始,它就是这样的资源:
<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">
它对我有用。
评论
对我来说,导致矢量转换为PNG的原因是我的矢量可绘制对象中的android:fillType=“evenodd”
属性。当我删除了可绘制对象中所有出现的此属性时(因此应用于矢量的默认填充类型),下次构建应用程序时,一切都很好。nonzero
除了最重要的答案之外,在我这边,移动到 ConstraintLayout 而不是 LinearLayout 或 FrameLayout 可以消除 SVG 矢量可绘制对象上的模糊。
通过添加解决
defaultConfig {
vectorDrawables.useSupportLibrary = true
}
到 buil.gradle (module:app) 文件,并将 imageViews 替换为
android:src="..."
跟
app:srcCompat="..."
希望帮助
评论
New -> Vector Asset
drawable-anydpi-v21
drawable-<mdpi/hdpi/etc>-v4
drawable
drawable-anydpi-v21
minSdkVersion
minSdkVersion
drawable-anydpi
drawable-anydpi-v21