首页 文章

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

提问于
浏览
69

我试图在我的Android应用程序中使用矢量drawables . 从http://developer.android.com/training/material/drawables.html(强调我的):

在Android 5.0(API Level 21)及更高版本中,您可以定义矢量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" />

和这个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资源 . 不会生成栅格化图像 .

7 回答

  • 1

    这里有关于此问题的新信息:

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

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

    来自Google工程师:

    大家,让我知道scaleType ='fitXY'是否可以为您解决方法,以使图像看起来更清晰 . 棉花糖Vs棒棒糖是由于棉花糖中添加了特殊的结垢处理 . 另外,对于你的评论:“正确的行为:矢量drawable应该扩展而没有质量损失 . 所以如果我们想在我们的应用程序中使用3种不同大小的相同资产,我们不必复制vector_drawable.xml 3次不同虽然我完全同意这应该是这种情况,但实际上,Android平台存在性能问题,因此我们还没有达到理想的世界 . 因此,如果您确定要在屏幕上同时绘制3种不同的大小,实际上建议使用3种不同的vector_drawable.xml以获得更好的性能 . 技术细节基本上我们在钩子下使用位图来缓存复杂的路径渲染,这样我们就可以获得最佳的重绘性能,与重绘位图drawable相同 .

  • 21

    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生成可绘制矢量的光栅图像向后兼容 . 您可以将矢量资产称为Java代码中的Drawable或XML代码中的@drawable;当您的应用运行时,相应的矢量或光栅图像会根据API级别自动显示 .


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

    Good bells on 6.x

    在模拟器SDK版本21中:

    Crappy bells on 5.x

  • 1

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

    你的矢量drawables现在应该正确缩放!

  • 3

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

    这只是向量的默认大小,以防您在布局视图中未定义它 . (即您使用换行内容作为imageview的高度和宽度)

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

    是的,只要正在运行的设备使用棒棒糖或更高级别,我就没有任何调整大小的问题 . 在以前的API中,在构建应用程序时,矢量将转换为用于不同屏幕大小的png .

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

    这会影响矢量周围空间的使用方式 . 即您可以使用它来更改视口内矢量的“重力”,使矢量具有默认边距,将矢量的某些部分保留在视口之外等等...通常,您只需将它们设置为相同尺寸 .

  • 8

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

    将Drawable设置为ImageView的背景 .

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

    希望它有用 .

  • 79

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

    1 - 右键单击drawable文件夹,选择New - > Vector Asset

    enter image description here

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

    enter image description here

    Second :如果你想得到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"
    

    Third :用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" />
    

    Fourth :如果你想在java代码中设置svg你必须在oncreate methoed上添加以下行

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

    我只是改变矢量图像的大小来解决我的问题 . 从第一个开始,资源就像:

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

    它对我有用 .

相关问题