为什么我的矢量绘图未按预期缩放?

Why isn't my vector drawable scaling as expected?

我正在尝试在我的 Android 应用程序中使用矢量绘图。来自 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.

使用这个可绘制对象:

<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 年左右的大型高分辨率移动设备 运行 棒棒糖上)时生成此模糊图像:

将矢量可绘制对象定义中的宽度和高度更改为 200dp 可显着改善 400dp 渲染尺寸的情况。但是,将其设置为 TextView 元素的可绘制对象(即文本左侧的图标)现在会创建一个巨大的图标。

我的问题:

1) 为什么矢量绘图中有width/height 规范?我认为这些的全部意义在于它们无损地放大和缩小使得宽度和高度在其定义中毫无意义?

2) 是否可以使用单个矢量可绘制对象,它在 TextView 上用作 24dp 可绘制对象,但可以很好地缩放以使用更大的图像?例如。如何避免创建多个不同大小的矢量可绘制对象,而是使用一个可缩放到我的渲染要求的​​可绘制对象?

3) 如何有效地使用 width/height 属性以及与 viewportWidth/Height 有什么区别?

其他详细信息:

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?

对于低于 21 的 SDK 版本,构建系统需要生成光栅图像,并且在您未指定 width/height.

的情况下作为默认大小

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?

如果您还需要定位小于 21 的 SDK,我认为这是不可能的。

3) How do I effectively use the width/height attributes and what is the difference with viewportWidth/Height?

Documentation:(其实我现在用的不是很好re-read它...)

android:width

Used to define the intrinsic width of the drawable. This support all the dimension units, normally specified with dp.

android:height

Used to define the intrinsic height the drawable. This support all the dimension units, normally specified with 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. 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. You can refer to vector assets as Drawable in Java code or @drawable in XML code; when your app runs, the corresponding vector or raster image displays automatically depending on the API level.


编辑: 发生了一些奇怪的事情。这是我在模拟器 SDK 版本 23 中的结果(Lollipop+ 测试设备现在已经死了...):

并且在模拟器 SDK 版本 21 中:

  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?

这只是矢量的默认大小,以防您未在布局视图中定义它。 (即,您使用 wrap content 作为高度和 图片视图的宽度)

  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?

是的,这是可能的,而且我在调整大小方面没有遇到任何问题 只要 运行 设备使用棒棒糖或更高版本。在以前 API,矢量被转换为 png 以适应不同的屏幕尺寸 您构建应用程序。

  1. How do I effectively use the width/height attributes and what is the difference with viewportWidth/Height?

这会影响矢量周围 space 的使用方式。即你可以 用它来改变视口内矢量的“重力”,使 矢量有一个默认边距,留下矢量的某些部分 超出视口等...通常,您只需将它们设置为相同的 尺寸。

AppCompat 23.2 为所有设备 运行 Android 2.1 及更高版本引入了矢量可绘制对象。图像正确缩放,与矢量绘图的 XML 文件中指定的 width/height 无关。不幸的是,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"/>

您的矢量绘图现在应该可以正确缩放!

此处有关于此问题的新信息:

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

看起来像在使用 android:scaleType="fitXY" 将使其在 Lollipop 上正确缩放。

来自 Google 工程师:

Hi, Let me know if scaleType='fitXY' can be a workaround for you , in order to get the image look sharp.

The marshmallow Vs Lollipop is due to a special scaling treatment added into marshmallow.

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

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

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.

我必须尝试这些方法,但不幸的是,none 其中的方法奏效了。 我在 ImageView 中尝试 fitXY,我尝试使用 app:srcCompat,但我什至不能使用它。 但我找到了一个绝招:

set the Drawable to background of your ImageView.

但这种方式的重点是视图维度。如果您的 ImageView 尺寸不正确,可绘制对象会拉伸。您必须在棒棒糖之前的版本中控制它或使用一些视图 PercentRelativeLayout.

希望对您有所帮助。

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

1-Right click on the drawable folder select New -> Vector Asset

2- The Vector Asset Studio provides you option to select the inbuilt Material icons or your own local svg file. You can override the default size if needed.

Second :如果你想要 android API 7+ 的支持,你必须使用 Android Support Library acording (())

1- 添加

vectorDrawables.useSupportLibrary = true

到您的 build.gradle 文件。

2-在具有 imageView 的布局中使用命名空间:

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

Third : 设置 imageView 为 android:scaleType="fitXY" 并使用 app:srcCompat

 <ImageView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scaleType="fitXY"
        app:srcCompat="@drawable/ic_menu" />

Fourth :如果你想在 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" 属性。当我 删除我的 drawable 中出现的所有此属性时(因此默认 nonzero 填充类型应用于矢量),下次构建应用程序时一切正常。

除了最上面的答案,在我这边移动到 ConstraintLayout 而不是 LinearLayout 或 FrameLayout 消除了 SVG 矢量可绘制对象上的模糊。

通过添加解决

defaultConfig {
    vectorDrawables.useSupportLibrary = true
}

到buil.gradle (module:app) 文件和 imageViews 替换

android:src="..."

app:srcCompat="..."

希望得到帮助