为什么我的vector绘图按比例缩放?

我试图在我的Android应用程序中使用vector绘制。 来自http://developer.android.com/training/material/drawables.html (强调我的):

在Android 5.0(API Level 21)及以上版本中,您可以定义vector绘图, 这些绘图可以缩放而不会丢失定义。

使用这个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

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

我的问题:

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

2)是否可以使用一个单独的vector绘图工具作为一个24DP可绘制的TextView,但扩大以及使用更大的图像呢? 例如,我如何避免创build不同大小的多个vector绘图,而是使用一个缩放到我的渲染要求?

3)如何有效地使用宽度/高度属性和viewportWidth / Height有什么不同?

额外细节:

  • 设备正在运行API 22
  • 使用Android Studio v1.5.1与Gradle版本1.5.0
  • 清单是编译和目标水平23,最低水平15.我也尝试移动最低水平到21,但这没有什么区别。
  • 反编译APK(最低级别设置为21)将在可绘制文件夹中显示一个XML资源。 没有产生光栅化的图像。

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

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

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

来自Google工程师:

嗨,让我知道如果scaleType ='fitXY'可以是你的解决方法,为了让图像看起来尖锐。

棉花糖VS棒棒糖是由于棉花糖添加到特殊的缩放处理。

同样,对于你的评论:“正确的行为:vector绘制应该缩放没有质量损失。因此,如果我们想要在我们的应用程序中使用3种不同大小的相同的资产,我们不必重复vector_drawable.xml 3次不同硬编码大小“。

即使我完全同意这种情况,但事实上,Android平台的性能问题还是没有达到理想的程度。 因此,如果您确定要同时在屏幕上绘制3种不同的大小,则实际上build议使用3个不同的vector_drawable.xml以获得更好的性能。

技术细节是基本上我们使用钩子下的位图来caching复杂的path渲染,这样我们就可以得到最好的重绘性能,与重画一个可绘制的位图并驾齐驱。

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

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

2)是否有可能使用一个单一的vectordrawable作为一个24DP可绘制的TextView以及一个大型的近屏宽度图像?

我不相信这是可能的,如果你还需要针对小于21的SDK。

3)如何有效地使用宽度/高度属性和viewportWidth / Height有什么不同?

文档:(实际上现在我不重视它,现在不是很有用…)

android:width

用于定义drawable的内在宽度。 这支持所有的尺寸单位,通常用dp指定。

android:height

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

android:viewportWidth

用于定义视口空间的宽度。 视口基本上是绘制path的虚拟canvas。

android:viewportHeight

用于定义视口空间的高度。 视口基本上是绘制path的虚拟canvas。

更多文档:

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


编辑:一些奇怪的事情正在发生。 这是我的结果在模拟器SDK版本23(棒棒糖+testing设备现在死了…):

6.x上的好钟声

而在模拟器SDK版本21中:

5.x上的蹩脚铃声

AppCompat 23.2为运行Android 2.1及更高版本的所有设备引入了vector绘图。 无论vector绘图的XML文件中指定的宽度/高度如何,图像都可以正确缩放。 不幸的是,这个实现并没有用在API级别21以上(赞成本地实现)。

好消息是,我们可以强制AppCompat在API层21和22上使用它的实现。坏消息是我们必须使用reflection来执行此操作。

首先,确保您使用的是最新版本的AppCompat,并且已经启用了新的vector实现:

 android { defaultConfig { vectorDrawables.useSupportLibrary = true } } dependencies { compile 'com.android.support:appcompat-v7:23.2.0' } 

然后,调用useCompatVectorIfNeeded(); 在您的应用程序的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(); } } } 

最后,确保你使用app:srcCompat而不是android:src来设置ImageView的图像:

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

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

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

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

2)是否有可能使用一个单一的vectordrawable作为一个24DP可绘制的TextView以及一个大型的近屏宽度图像?

是的,只要运行设备使用棒棒糖或更高,这是可能的,我没有任何resize的问题。 在以前的API中,当您构build应用程序时,vector将转换为不同大小的PNG。

3)如何有效地使用宽度/高度属性和viewportWidth / Height有什么不同?

这将影响如何使用vector周围的空间。 即你可以用它来改变在视口内的向量的“重力”,使向量有一个默认的边距,保持向量的某些部分从视口,等等…通常,你只是将它们设置为相同尺寸。

我尝试了这些方法,但不幸的是,他们都没有工作。 我尝试在ImageView fitXY ,我尝试使用app:srcCompat但甚至不能使用它。 但是我发现了一个诡计:

将Drawable设置为ImageView background

但这种方式的重点是视图维度。 如果您的ImageView具有不正确的尺寸,drawable将获得Stretch。 你必须控制它在前棒棒糖版本或使用一些意见PercentRelativeLayout

希望这是有帮助的。

首先 :导入svg到可绘制:(( https://www.learn2crack.com/2016/02/android-studio-svg.html ))

1-在可绘制文件夹上单击鼠标右键,selectNew – > Vector Asset

在这里输入图像说明

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

在这里输入图像说明

第二 :如果你想从Android API 7+支持,你必须使用Android支持库acording(( 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方法上添加下面的代码

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