简体   繁体   English

为什么我的矢量可绘制缩放不符合预期?

[英]Why isn't my vector drawable scaling as expected?

I am attempting to use vector drawables in my Android app.我正在尝试在我的 Android 应用程序中使用矢量绘图。 From http://developer.android.com/training/material/drawables.html (emphasis mine):来自http://developer.android.com/training/material/drawables.html (强调我的):

In Android 5.0 (API Level 21) and above, you can define vector drawables, which scale without losing definition.在 Android 5.0(API 级别 21)及更高版本中,您可以定义矢量可绘制对象,它可以缩放而不会丢失定义。

Using this drawable:使用这个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" />

and this ImageView:和这个 ImageView:

<ImageView
    android:layout_width="400dp"
    android:layout_height="400dp"
    android:src="@drawable/icon_bell"/>

produces this blurry image when attempting to display the icon at 400dp (on a largish high-res circa 2015 mobile device running lollipop):尝试以 400dp 显示图标时会产生此模糊图像(在 2015 年左右运行棒棒糖的较大高分辨率移动设备上):

模糊的铃铛图标

Changing the width and height in the definition of the vector drawable to 200dp significantly improves the situation at the 400dp rendered size.将可绘制矢量定义中的宽度和高度更改为 200dp 可显着改善 400dp 渲染大小的情况。 However, setting this as a drawable for a TextView element (ie icon to the left of the text) now creates a huge icon.但是,将其设置为 TextView 元素的可绘制对象(即文本左侧的图标)现在会创建一个巨大的图标。

My questions:我的问题:

1) Why is there a width/height specification in the vector drawable? 1) 为什么矢量可绘制对象中有宽度/高度规范? I thought the entire point of these is that they scale up and down losslessly making width and height meaningless in its definition?我认为这些的全部意义在于它们无损地放大和缩小,使得宽度和高度在其定义中毫无意义?

2) Is it possible to use a single vector drawable which works as a 24dp drawable on a TextView but scales up well to use as much larger images too? 2) 是否可以使用单个矢量可绘制对象,它在 TextView 上用作 24dp 可绘制对象,但也可以很好地扩展以使用更大的图像? Eg how do I avoid creating multiple vector drawables of different sizes and instead use one which scales to my rendered requirements?例如,我如何避免创建多个不同大小的矢量可绘制对象,而是使用一个可以缩放到我的渲染要求的​​对象?

3) How do I effectively use the width/height attributes and what is the difference with viewportWidth/Height? 3) 如何有效地使用宽度/高度属性,与 viewportWidth/Height 有什么区别?

Additional details:额外细节:

  • Device is running API 22设备正在运行 API 22
  • Using Android Studio v1.5.1 with Gradle version 1.5.0将 Android Studio v1.5.1 与 Gradle 版本 1.5.0 一起使用
  • Manifest is compile and target level 23, min level 15. I've also tried moving min level to 21, but this made no difference.清单是编译和目标级别 23,最低级别 15。我也尝试将最低级别移动到 21,但这没有任何区别。
  • Decompiling the APK (with min level set to 21) shows a single XML resource in the drawable folder.反编译 APK(最低级别设置为 21)会在可绘制文件夹中显示单个 XML 资源。 No rasterized images are produced.不产生光栅化图像。

There is new info about this issue here:这里有关于这个问题的新信息:

https://code.google.com/p/android/issues/detail?id=202019 https://code.google.com/p/android/issues/detail?id=202019

It looks like using android:scaleType="fitXY" will make it scale correctly on Lollipop.看起来使用android:scaleType="fitXY"会使其在 Lollipop 上正确缩放。

From a Google engineer:来自谷歌工程师:

Hi, Let me know if scaleType='fitXY' can be a workaround for you , in order to get the image look sharp.嗨,让我知道 scaleType='fitXY' 是否可以成为您的解决方法,以使图像看起来清晰。

The marshmallow Vs Lollipop is due to a special scaling treatment added into marshmallow.棉花糖 Vs Lollipop 是由于在棉花糖中添加了特殊的缩放处理。

Also, for your comments: " Correct behavior: The vector drawable should scale without quality loss. So if we want to use the same asset in 3 different sizes in our application, we don't have to duplicate vector_drawable.xml 3 times with different hardcoded sizes. "另外,对于您的评论:“正确的行为:vector drawable 应该在没有质量损失的情况下缩放。因此,如果我们想在我们的应用程序中使用 3 种不同尺寸的相同资产,我们不必复制 vector_drawable.xml 3 次不同的硬编码大小。”

Even though I totally agree this should be the case, in reality, the Android platform has performance concern such that we have not reach the ideal world yet.尽管我完全同意应该是这样,但实际上,Android 平台存在性能问题,以至于我们还没有达到理想的世界。 So it is actually recommended to use 3 different vector_drawable.xml for better performance if you are sure you want to draw 3 different size on the screen at the same time.因此,如果您确定要同时在屏幕上绘制 3 个不同的尺寸,实际上建议使用 3 个不同的 vector_drawable.xml 以获得更好的性能。

The technical detail is basically we are using a bitmap under the hook to cache the complex path rendering, such that we can get the best redrawing performance, on a par with redrawing a bitmap drawable.技术细节基本上是我们在钩子下使用位图来缓存复杂的路径渲染,这样我们可以获得最佳的重绘性能,与重绘位图drawable相当。

1) Why is there a width/height specification in the vector drawable? 1) 为什么矢量可绘制对象中有宽度/高度规范? I thought the entire point of these is that they scale up and down losslessly making width and height meaningless in its definition?我认为这些的全部意义在于它们无损地放大和缩小,使得宽度和高度在其定义中毫无意义?

For SDK versions less than 21 where the build system needs to generate raster images and as the default size in cases where you don't specify the width/height.对于低于 21 的 SDK 版本,构建系统需要生成光栅图像,并且在您未指定宽度/高度的情况下将其作为默认大小。

2) Is it possible to use a single vector drawable which works as a 24dp drawable on a TextView as well as a large near-screen width image? 2) 是否可以使用单个矢量可绘制对象作为 TextView 上的 24dp 可绘制对象以及大的近屏幕宽度图像?

I don't believe this is possible if you also need to target SDKs less than 21.如果您还需要针对小于 21 的 SDK,我认为这是不可能的。

3) How do I effectively use the width/height attributes and what is the difference with viewportWidth/Height? 3) 如何有效地使用宽度/高度属性,与 viewportWidth/Height 有什么区别?

Documentation: (actually not very useful now that I re-read it...) 文档:(现在我重新阅读它实际上不是很有用......)

android:width

Used to define the intrinsic width of the drawable.用于定义可绘制对象的固有宽度。 This support all the dimension units, normally specified with dp.这支持所有维度单位,通常用 dp 指定。

android:height

Used to define the intrinsic height the drawable.用于定义drawable的固有高度。 This support all the dimension units, normally specified with dp.这支持所有维度单位,通常用 dp 指定。

android:viewportWidth

Used to define the width of the viewport space.用于定义视口空间的宽度。 Viewport is basically the virtual canvas where the paths are drawn on.视口基本上是绘制路径的虚拟画布。

android:viewportHeight

Used to define the height of the viewport space.用于定义视口空间的高度。 Viewport is basically the virtual canvas where the paths are drawn on.视口基本上是绘制路径的虚拟画布。

More documentation: 更多文档:

Android 4.4 (API level 20) and lower doesn't support vector drawables. Android 4.4(API 级别 20)及更低版本不支持矢量绘图。 If your minimum API level is set at one of these API levels, Vector Asset Studio also directs Gradle to generate raster images of the vector drawable for backward-compatibility.如果您的最低 API 级别设置为这些 API 级别之一,Vector Asset Studio 还会指示 Gradle 生成矢量可绘制对象的光栅图像以实现向后兼容性。 You can refer to vector assets as Drawable in Java code or @drawable in XML code;您可以将矢量资源称为 Java 代码中的Drawable或 XML 代码中的@drawable when your app runs, the corresponding vector or raster image displays automatically depending on the API level.当您的应用程序运行时,相应的矢量或光栅图像会根据 API 级别自动显示。


Edit: Something weird is going on.编辑:发生了一些奇怪的事情。 Here's my results in the emulator SDK version 23 (Lollipop+ test device is dead right now...):这是我在模拟器 SDK 版本 23 中的结果(Lollipop+ 测试设备现在已经死了......):

6.x 上的好消息

And in the emulator SDK version 21:在模拟器 SDK 版本 21 中:

5.x 上的蹩脚铃声

AppCompat 23.2 introduces vector drawables for all devices running Android 2.1 and above. AppCompat 23.2 为所有运行 Android 2.1 及更高版本的设备引入了矢量可绘制对象。 The images scale correctly, irregardless of the width/height specified in the vector drawable's XML file.无论矢量绘图的 XML 文件中指定的宽度/高度如何,图像都能正确缩放。 Unfortunately, this implementation is not used in API level 21 and above (in favor of the native implementation).不幸的是,API 级别 21 及更高级别没有使用此实现(有利于本机实现)。

The good news is that we can force AppCompat to use its implementation on API levels 21 and 22. The bad news is that we have to use reflection to do this.好消息是我们可以强制 AppCompat 在 API 级别 21 和 22 上使用它的实现。坏消息是我们必须使用反射来做到这一点。

First of all, make sure you're using the latest version of AppCompat, and that you have enabled the new vector implementation:首先,确保您使用的是最新版本的 AppCompat,并且您已启用新的矢量实现:

android {
  defaultConfig {
    vectorDrawables.useSupportLibrary = true
  }
}

dependencies {
    compile 'com.android.support:appcompat-v7:23.2.0'
}

Then, call useCompatVectorIfNeeded();然后,调用useCompatVectorIfNeeded(); in your Application's onCreate():在您的应用程序的 onCreate() 中:

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();
        }
    }
}

Finally, make sure you're using app:srcCompat instead of android:src to set your ImageView's image:最后,确保您使用app:srcCompat而不是android:src来设置 ImageView 的图像:

<ImageView
    android:layout_width="400dp"
    android:layout_height="400dp"
    app:srcCompat="@drawable/icon_bell"/>

Your vector drawables should now scale correctly!您的矢量绘图现在应该可以正确缩放了!

  1. Why is there a width/height specification in the vector drawable?为什么矢量可绘制对象中有宽度/高度规范? I thought the entire point of these is that they scale up and down losslessly making width and height meaningless in its definition?我认为这些的全部意义在于它们无损地放大和缩小,使得宽度和高度在其定义中毫无意义?

This is just the default size of the vector in case you don't define it in the layout view.这只是向量的默认大小,以防您未在布局视图中定义它。 (ie You use wrap content for the height and width of your imageview) (即您使用包装内容作为图像视图的高度和宽度)

  1. Is it possible to use a single vector drawable which works as a 24dp drawable on a TextView as well as a large near-screen width image?是否可以使用单个矢量可绘制对象作为 TextView 上的 24dp 可绘制对象以及大的近屏幕宽度图像?

Yes, It is possible and I haven't had any problem with resizing as long as the running device is using lollipop or higher.是的,这是可能的,只要正在运行的设备使用棒棒糖或更高版本,我在调整大小方面没有任何问题。 In previous APIs, the vector is converted to pngs for different screen sizes when you build the app.在之前的 API 中,当您构建应用程序时,矢量会针对不同的屏幕尺寸转换为 png。

  1. How do I effectively use the width/height attributes and what is the difference with viewportWidth/Height?如何有效地使用 width/height 属性,viewportWidth/Height 有什么区别?

This affects how the space around your vector is used.这会影响向量周围空间的使用方式。 ie You can use it to change the "gravity" of the vector inside the viewport, make the vector have a default margin, leave certain parts of the vector out of the viewport, etc... Normally, you just set them to the same size.即您可以使用它来更改视口内矢量的“重力”,使矢量具有默认边距,将矢量的某些部分留在视口之外,等等......通常,您只需将它们设置为相同尺寸。

I solve my problem just change size of vector image.我解决了我的问题只是改变了矢量图像的大小。 From the first it was resource like:从一开始就是这样的资源:

<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">

And I've changed it to 150dp (size of ImageView in layout with this vector resource) like:我已将其更改为 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">

It's working for me.它对我有用。

I try must of these ways but unfortunately, none of them worked.我尝试了这些方法,但不幸的是,它们都没有奏效。 I try fitXY in ImageView , I try using app:srcCompat but cannot even use it.我在ImageView中尝试fitXY ,我尝试使用app:srcCompat但甚至无法使用它。 but I found a trick way:但我找到了一个窍门:

set the Drawable to background of your ImageView .将 Drawable 设置为ImageViewbackground

But the point of this way is Views dimensions.但这种方式的重点是视图维度。 if your ImageView has incorrect dimensions, drawable gets Stretch.如果您的ImageView尺寸不正确,drawable 会拉伸。 you must control it in pre-lollipop versions or Use some Views PercentRelativeLayout .您必须在棒棒糖之前的版本中控制它或使用一些视图PercentRelativeLayout

Hope it's helpful.希望它有帮助。

First : import svg to drawble : (( https://www.learn2crack.com/2016/02/android-studio-svg.html ))首先:将svg导入drawble:(( https://www.learn2crack.com/2016/02/android-studio-svg.html ))

1-Right click on the drawable folder select New -> Vector Asset 1-右键单击可绘制文件夹选择新建->矢量资产

在此处输入图像描述

2- The Vector Asset Studio provides you option to select the inbuilt Material icons or your own local svg file. 2- Vector Asset Studio 为您提供选择内置材质图标或您自己的本地 svg 文件的选项。 You can override the default size if needed.如果需要,您可以覆盖默认大小。

在此处输入图像描述

Second :if you want support from android API 7+ , you have to use Android Support Library acording (( https://stackoverflow.com/a/44713221/1140304 ))第二:如果你想得到 android API 7+ 的支持,你必须使用 Android Support Library acording (( https://stackoverflow.com/a/44713221/1140304 ))

1- add 1-添加

vectorDrawables.useSupportLibrary = true

to your build.gradle file.到您的 build.gradle 文件。

2-Use namespace in your layout that has imageView : 2-在您的布局中使用具有 imageView 的命名空间:

xmlns:app="http://schemas.android.com/apk/res-auto"

Third : set imageView with android:scaleType="fitXY" and use app:srcCompat第三:使用 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" />

Fourth :if you want set svg in java code you have to add below line on oncreate methoed第四:如果你想在java代码中设置svg,你必须在oncreate方法上添加以下行

 @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

For me, the reason that caused vectors to be converted to PNG was android:fillType="evenodd" attribute in my vector drawable.对我来说,导致矢量转换为 PNG 的原因是我的矢量可绘制对象中的android:fillType="evenodd"属性。 When I removed all occurrences of this attribute in my drawable (therefor the default nonzero fill type applied to the vector), next time the app was built everything was fine.当我在我的可绘制对象中删除了所有出现的这个属性(因此默认的nonzero填充类型应用于矢量),下次构建应用程序时一切都很好。

除了最重要的答案之外,在我这边移动到 ConstraintLayout 而不是 LinearLayout 或 FrameLayout 消除了 SVG 矢量可绘制的模糊。

solved by adding通过添加解决

defaultConfig {
    vectorDrawables.useSupportLibrary = true
}

to buil.gradle (module:app) file and for imageViews replace到 build.gradle (module:app) 文件和 imageViews 替换

android:src="..."

with

app:srcCompat="..."

hope help希望帮助

With SDK 30 this worked for me使用 SDK 30 这对我有用

<ImageView
    android:id="@+id/searchClear"
    android:layout_width="16dp"
    android:layout_height="16dp"
    android:layout_alignParentEnd="true"
    android:layout_centerVertical="true"
    android:layout_marginEnd="8dp"
    android:background="@android:color/transparent"
    android:src="@drawable/ic_close" />

being ic_close a vector image作为 ic_close 矢量图

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM