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

时间:2016-01-21 23:48:17

标签: android vector drawable scaling

我正在尝试在我的Android应用中使用矢量绘图。来自http://developer.android.com/training/material/drawables.html(强调我的):

  

在Android 5.0(API Level 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年左右的大型高分辨率移动设备上运行棒棒糖):

blurryBellIcon

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

我的问题:

1)为什么矢量可绘制中有宽度/高度规格?我认为这些的全部意义在于它们无损扩大和缩小,使得宽度和高度在其定义中毫无意义?

2)是否可以使用单个矢量drawable,它可以在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资源。不会生成栅格化图像。

8 个答案:

答案 0 :(得分:88)

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

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

看起来像是在使用 android:scaleType="fitXY"会让它在Lollipop上正确缩放。

来自Google工程师:

  

嗨,请告诉我scaleType =&#39; fitXY&#39;可以为你提供一种解决方法   为了让图像看起来更清晰。

     

棉花糖Vs棒棒糖是由于特殊的结垢处理   添加到棉花糖中。

     

另外,对于你的意见:&#34;正确的行为:矢量drawable   应该在没有质量损失的情因此,如果我们想要使用相同的资产   在我们的应用程序中有3种不同的尺寸,我们不必复制   vector_drawable.xml使用不同的硬编码大小3次。 &#34;

     

即使我完全同意这应该是这样的,实际上,   Android平台存在性能问题,我们还没有达到   理想的世界。所以实际上建议使用3种不同的   如果您确定需要,可以使用vector_drawable.xml获得更好的性能   在屏幕上同时绘制3种不同的尺寸。

     

技术细节基本上我们在钩子下使用位图   缓存复杂的路径渲染,这样我们就可以获得最佳效果   重绘性能,与重绘位图drawable相同。

答案 1 :(得分:25)

  

1)为什么矢量可绘制中有宽度/高度规格?我认为这些的全部意义在于它们无损扩大和缩小,使得宽度和高度在其定义中毫无意义?

对于小于21的SDK版本,构建系统需要生成光栅图像,并且在不指定宽度/高度的情况下作为默认大小。

  

2)是否可以使用单个矢量drawable,它在TextView上作为24dp可绘制,以及大的近屏宽度图像?

如果您还需要定位低于21的SDK,我也不相信这是可能的。

  

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

Documentation:(现在我重读了它真的不太有用......)

  

android:width

     

用于定义drawable的固有宽度。这支持所有维度单位,通常用dp。

指定      

android:height

     

用于定义drawable的内在高度。这支持所有维度单位,通常用dp。

指定      

android:viewportWidth

     

用于定义视口空间的宽度。视口基本上是绘制路径的虚拟画布。

     

android:viewportHeight

     

用于定义视口空间的高度。视口基本上是绘制路径的虚拟画布。

More documentation:

  

Android 4.4(API级别20)及更低版本不支持矢量绘图。如果您的最低API级别设置为其中一个API级别,Vector Asset Studio还会指示Gradle生成向量drawable的光栅图像以实现向后兼容性。您可以在Java代码中将矢量资产称为Drawable,或在XML代码中将@drawable称为矢量资产;当您的应用运行时,相应的矢量或光栅图像会根据API级别自动显示。

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

Good bells on 6.x

在模拟器SDK版本21中:

Crappy bells on 5.x

答案 2 :(得分:9)

AppCompat 23.2为所有运行Android 2.1及更高版本的设备引入了矢量绘图。无论vector drawable的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"/>

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

答案 3 :(得分:6)

1)为什么矢量可绘制中有宽度/高度规格?我认为这些的全部意义在于它们无损扩大和缩小,使得宽度和高度在其定义中毫无意义?

  

如果您没有在布局视图中定义它,那么这只是矢量的默认大小。 (即你使用包裹内容的高度和   你的imageview的宽度)

2)是否可以使用单个矢量drawable,它在TextView上作为24dp可绘制,以及大的近屏宽度图像?

  

是的,有可能,我没有任何调整大小的问题   只要正在运行的设备使用棒棒糖或更高。在以前   API,当矢量转换为不同屏幕尺寸的png   你构建应用程序。

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

  

这会影响矢量周围空间的使用方式。即你可以   用它来改变视口内矢量的“重力”,make   向量具有默认边距,保留向量的某些部分   超出视口等...通常,你只需将它们设置为相同   大小

答案 4 :(得分:3)

我必须尝试这些方法但不幸的是,它们都没有奏效。 我在fitXY中尝试ImageView,我尝试使用app:srcCompat,但甚至无法使用它。 但我发现了一种技巧:

  

将Drawable设置为background的{​​{1}}。

但这种方式的重点是视图维度。如果您的ImageView尺寸不正确,则drawable会获得Stretch。您必须在pre-lollipop版本中控制它或使用一些视图ImageView

希望它有所帮助。

答案 5 :(得分:2)

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

  

1 - 右键单击​​drawable文件夹,选择New - &gt;矢量资产

enter image description here

  

2- Vector Asset Studio提供了选择内置的选项   材质图标或您自己的本地svg文件。你可以覆盖   默认大小,如果需要。

enter image description here

第二次:如果您需要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设置imageView:scaleType =“fitXY”并使用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);
    }

答案 6 :(得分:1)

我解决了我的问题,只需更改矢量图像的大小即可。从一开始它就是这样的资源:

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

对我有用。

答案 7 :(得分:0)

对我来说,导致矢量转换为png的原因是我的矢量可绘制对象中的 android:fillType="..." 属性。当我删除我的可绘制对象中所有出现的该属性时,下次编译该应用程序时一切正常。