Android 带有 VectorDrawables srcCompat 的选择器 Drawable
Android Selector Drawable with VectorDrawables srcCompat
我遇到了与 VectorDrawables 新的向后兼容性的问题。
在支持库 23.2 中引入了一项新功能,用于向后兼容 Android VectorDrawables。
我有一个 ImageView,它是一个分配给 SelectorDrawable 的对象。这个 Drawable 包含多个 VectorDrawable,所以我想我应该使用 app:srcCompat 来实现兼容性。但它不适用于我的 android 4.1.2.
的 Galaxy S2
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/ic_gps_fixed_24dp"android:state_activated="true" android:state_selected="true"></item>
<item android:drawable="@drawable/ic_gps_not_fixed_24dp" android:state_activated="true" android:state_selected="false"></item>
<item android:drawable="@drawable/ic_gps_not_fixed_24dp" android:state_activated="false" android:state_selected="true"></item>
<item android:drawable="@drawable/ic_gps_off_24dp" android:state_activated="false" android:state_selected="false"></item>
<item android:drawable="@drawable/ic_gps_not_fixed_24dp"></item>
</selector>
所有可绘制对象都是矢量 xml 文件。
将此 SelectorDrawable 与 srcCompat 一起使用时出现此错误:
Caused by: android.content.res.Resources$NotFoundException: File res/drawable/ Caused by: android.content.res.Resources$NotFoundException: File res/drawable/ic_gps_fixed_24dp.xml from drawable resource ID #0x7f0201c1
at android.content.res.Resources.loadDrawable(Resources.java:1951)
at android.content.res.Resources.getDrawable(Resources.java:672)
at android.graphics.drawable.StateListDrawable.inflate(StateListDrawable.java:173)
at android.graphics.drawable.Drawable.createFromXmlInner(Drawable.java:881).xml from drawable resource ID #0x7f0201c1
使用 android:src 更糟。
如果我将其中一个矢量绘图与 app:srcCompat 一起使用,一切正常。所以我猜是SelectorDrawable和兼容性的问题。
有没有人遇到同样的问题并找到了解决方案,或者目前无法在 Android 5 之前在 SelectorDrawables 中使用 VectorDrawables?
小知识:
- 编译目标API 23
- 支持 Libraray 23.3.0
- vectorDrawables.useSupportLibrary = 真
- Gradle 2.0
正如@Jahnold 在问题评论中提到的那样,在 23.3 中删除了对从 xml 状态 xml 列表加载可绘制矢量的支持。
不过,我发现了几种可以提供帮助的方法。
1.使用色调
如果所选状态列表中的可绘制对象仅按颜色区分,则该方法适用。
首先,只创建一个带有色调和白色的矢量可绘制对象fillColor
:
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tintMode="multiply"
android:tint="@color/button_tint">
<path
android:fillColor="#ffffff"
android:pathData="M2.01 21L23 12 2.01 3 2 10l15 2-15 2z"/>
<path
android:pathData="M0 0h24v24H0z"/>
</vector>
二、创建颜色状态列表button_tint.xml
放置在res/color
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="#555555" android:state_enabled="false"/>
<item android:color="#6699dd"/>
</selector>
不要忘记向 build.gradle
添加后续行,否则该方法将不适用于旧的 Android 版本。
defaultConfig {
vectorDrawables.useSupportLibrary = true
}
2。硬编码创建 StateListDrawable
如果您将状态列表矢量可绘制对象用于不仅颜色不同而且图形也不同的情况,则该方法适用,因此您需要创建多个不同的 xml 文件。然后您可以以编程方式创建 StateListDrawable
,如 .
中所示
自从我问了这个问题后,有些事情发生了变化,所以我会自己回答。
支持库 23.4.0 重新启用了对来自 Ressources 的 VectorDrawables 的支持:Android Support Library 23.4.0 available now
您可以在 Google I/O 2016 年的演员阵容中找到更多相关信息:
What's new in the support library - Google I/O 2016
您需要将此添加到每个 Activity 要在低于 Android 5.0(代号 Lollipop,API 级别 21)的设备上使用 VectorDrawables 的地方:
static {
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
}
因此您现在可以在 DrawableContainers 中使用 VectorDrawables,但它仍然会导致上面来源中提到的一些问题,因此请谨慎使用。
到目前为止,我没有在我的应用程序中重新启用此功能,但我会在下一个主要版本中将我的很多图标更改为 VectorDrawables,然后将更深入地探讨这个主题。
看完支持库中的新内容 - Google I/O 2016 我注意到 AppCompatResources
[=26] 中的一个有用方法=].这是AppCompatResources#getColorStateList(Context context, int resId)
。在这种方法的帮助下,我实现了带有矢量绘图的选择器。这是我的颜色选择器文件 icon_selector
:
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="@color/red_selected" android:state_selected="true"/>
<item android:color="@color/red_pressed" android:state_pressed="true"/>
<item android:color="@color/red"/>
</selector>
并且有 java 方法 returns 着色可绘制对象:
private Drawable getTintedDrawable(@DrawableRes int drawableId) {
Drawable drawable;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
drawable = getResources().getDrawable(drawableId, getTheme());
} else {
drawable = getResources().getDrawable(drawableId);
}
drawable = DrawableCompat.wrap(drawable);
DrawableCompat.setTintList(drawable.mutate(), AppCompatResources.getColorStateList(this, R.color.selector_nav_bar_item_ico));
return drawable;
}
你可以像下面这样使用它
yourImageView.setImageDrawable(getTintedDrawable(R.drawable.ic_vector_image));
通过以下更改工作正常。
static {
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
}
已添加到应用程序中 class。
应用 build.gradle 在 defaultConfig
中
vectorDrawables.useSupportLibrary = true
我建议使用此解决方法来根据状态更改颜色:
设置一个正常的白色 VectorDrawable,并让色调具有颜色选择器。
这已经过测试,即使在 Android API 16 的模拟器上也能正常工作,当然,即使您在 gradle 中设置 "vectorDrawables.useSupportLibrary = true",它也能正常工作。
示例:启用第一个视图,禁用第二个视图:
MainActivity.kt
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
disabledSendMessageButton.isEnabled = false
}
}
res/layout/activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_height="match_parent" android:clipChildren="false"
android:clipToPadding="false" android:gravity="center" android:orientation="vertical" tools:context=".MainActivity">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/sendMessageButton" android:layout_width="wrap_content" android:layout_height="wrap_content"
android:clickable="true" android:focusable="true" android:foreground="?attr/selectableItemBackgroundBorderless"
android:minWidth="?attr/actionBarSize" android:minHeight="?attr/actionBarSize" android:padding="8dp"
android:scaleType="centerInside" app:srcCompat="@drawable/ic_baseline_send_24" app:tint="@color/color_selector"
tools:targetApi="m" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/disabledSendMessageButton" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:clickable="true" android:focusable="true"
android:foreground="?attr/selectableItemBackgroundBorderless" android:minWidth="?attr/actionBarSize"
android:minHeight="?attr/actionBarSize" android:padding="8dp" android:scaleType="centerInside"
app:srcCompat="@drawable/ic_baseline_send_24" app:tint="@color/color_selector" tools:targetApi="m" />
</LinearLayout>
res/color/color_selector.xml
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="@android:color/secondary_text_dark" android:state_enabled="false" />
<item android:color="@color/colorPrimary" />
</selector>
res/drawable/ic_baseline_send_24.xml
<vector android:height="24dp" android:tint="#FFFFFF"
android:viewportHeight="24.0" android:viewportWidth="24.0"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M2.01,21L23,12 2.01,3 2,10l15,2 -15,2z"/>
</vector>
使用animated-selector
例子
https://github.com/alexjlockwood/adp-delightful-details
https://www.androiddesignpatterns.com/2016/11/introduction-to-icon-animation-techniques.html
使用此工具创建 animated-vector
文件
我遇到了与 VectorDrawables 新的向后兼容性的问题。 在支持库 23.2 中引入了一项新功能,用于向后兼容 Android VectorDrawables。
我有一个 ImageView,它是一个分配给 SelectorDrawable 的对象。这个 Drawable 包含多个 VectorDrawable,所以我想我应该使用 app:srcCompat 来实现兼容性。但它不适用于我的 android 4.1.2.
的 Galaxy S2<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/ic_gps_fixed_24dp"android:state_activated="true" android:state_selected="true"></item>
<item android:drawable="@drawable/ic_gps_not_fixed_24dp" android:state_activated="true" android:state_selected="false"></item>
<item android:drawable="@drawable/ic_gps_not_fixed_24dp" android:state_activated="false" android:state_selected="true"></item>
<item android:drawable="@drawable/ic_gps_off_24dp" android:state_activated="false" android:state_selected="false"></item>
<item android:drawable="@drawable/ic_gps_not_fixed_24dp"></item>
</selector>
所有可绘制对象都是矢量 xml 文件。
将此 SelectorDrawable 与 srcCompat 一起使用时出现此错误:
Caused by: android.content.res.Resources$NotFoundException: File res/drawable/ Caused by: android.content.res.Resources$NotFoundException: File res/drawable/ic_gps_fixed_24dp.xml from drawable resource ID #0x7f0201c1
at android.content.res.Resources.loadDrawable(Resources.java:1951)
at android.content.res.Resources.getDrawable(Resources.java:672)
at android.graphics.drawable.StateListDrawable.inflate(StateListDrawable.java:173)
at android.graphics.drawable.Drawable.createFromXmlInner(Drawable.java:881).xml from drawable resource ID #0x7f0201c1
使用 android:src 更糟。
如果我将其中一个矢量绘图与 app:srcCompat 一起使用,一切正常。所以我猜是SelectorDrawable和兼容性的问题。
有没有人遇到同样的问题并找到了解决方案,或者目前无法在 Android 5 之前在 SelectorDrawables 中使用 VectorDrawables?
小知识:
- 编译目标API 23
- 支持 Libraray 23.3.0
- vectorDrawables.useSupportLibrary = 真
- Gradle 2.0
正如@Jahnold 在问题评论中提到的那样,在 23.3 中删除了对从 xml 状态 xml 列表加载可绘制矢量的支持。
不过,我发现了几种可以提供帮助的方法。
1.使用色调
如果所选状态列表中的可绘制对象仅按颜色区分,则该方法适用。
首先,只创建一个带有色调和白色的矢量可绘制对象fillColor
:
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tintMode="multiply"
android:tint="@color/button_tint">
<path
android:fillColor="#ffffff"
android:pathData="M2.01 21L23 12 2.01 3 2 10l15 2-15 2z"/>
<path
android:pathData="M0 0h24v24H0z"/>
</vector>
二、创建颜色状态列表button_tint.xml
放置在res/color
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="#555555" android:state_enabled="false"/>
<item android:color="#6699dd"/>
</selector>
不要忘记向 build.gradle
添加后续行,否则该方法将不适用于旧的 Android 版本。
defaultConfig {
vectorDrawables.useSupportLibrary = true
}
2。硬编码创建 StateListDrawable
如果您将状态列表矢量可绘制对象用于不仅颜色不同而且图形也不同的情况,则该方法适用,因此您需要创建多个不同的 xml 文件。然后您可以以编程方式创建 StateListDrawable
,如
自从我问了这个问题后,有些事情发生了变化,所以我会自己回答。
支持库 23.4.0 重新启用了对来自 Ressources 的 VectorDrawables 的支持:Android Support Library 23.4.0 available now
您可以在 Google I/O 2016 年的演员阵容中找到更多相关信息: What's new in the support library - Google I/O 2016
您需要将此添加到每个 Activity 要在低于 Android 5.0(代号 Lollipop,API 级别 21)的设备上使用 VectorDrawables 的地方:
static {
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
}
因此您现在可以在 DrawableContainers 中使用 VectorDrawables,但它仍然会导致上面来源中提到的一些问题,因此请谨慎使用。
到目前为止,我没有在我的应用程序中重新启用此功能,但我会在下一个主要版本中将我的很多图标更改为 VectorDrawables,然后将更深入地探讨这个主题。
看完支持库中的新内容 - Google I/O 2016 我注意到 AppCompatResources
[=26] 中的一个有用方法=].这是AppCompatResources#getColorStateList(Context context, int resId)
。在这种方法的帮助下,我实现了带有矢量绘图的选择器。这是我的颜色选择器文件 icon_selector
:
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="@color/red_selected" android:state_selected="true"/>
<item android:color="@color/red_pressed" android:state_pressed="true"/>
<item android:color="@color/red"/>
</selector>
并且有 java 方法 returns 着色可绘制对象:
private Drawable getTintedDrawable(@DrawableRes int drawableId) {
Drawable drawable;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
drawable = getResources().getDrawable(drawableId, getTheme());
} else {
drawable = getResources().getDrawable(drawableId);
}
drawable = DrawableCompat.wrap(drawable);
DrawableCompat.setTintList(drawable.mutate(), AppCompatResources.getColorStateList(this, R.color.selector_nav_bar_item_ico));
return drawable;
}
你可以像下面这样使用它
yourImageView.setImageDrawable(getTintedDrawable(R.drawable.ic_vector_image));
通过以下更改工作正常。
static {
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
}
已添加到应用程序中 class。
应用 build.gradle 在 defaultConfig
vectorDrawables.useSupportLibrary = true
我建议使用此解决方法来根据状态更改颜色: 设置一个正常的白色 VectorDrawable,并让色调具有颜色选择器。
这已经过测试,即使在 Android API 16 的模拟器上也能正常工作,当然,即使您在 gradle 中设置 "vectorDrawables.useSupportLibrary = true",它也能正常工作。
示例:启用第一个视图,禁用第二个视图:
MainActivity.kt
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
disabledSendMessageButton.isEnabled = false
}
}
res/layout/activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_height="match_parent" android:clipChildren="false"
android:clipToPadding="false" android:gravity="center" android:orientation="vertical" tools:context=".MainActivity">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/sendMessageButton" android:layout_width="wrap_content" android:layout_height="wrap_content"
android:clickable="true" android:focusable="true" android:foreground="?attr/selectableItemBackgroundBorderless"
android:minWidth="?attr/actionBarSize" android:minHeight="?attr/actionBarSize" android:padding="8dp"
android:scaleType="centerInside" app:srcCompat="@drawable/ic_baseline_send_24" app:tint="@color/color_selector"
tools:targetApi="m" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/disabledSendMessageButton" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:clickable="true" android:focusable="true"
android:foreground="?attr/selectableItemBackgroundBorderless" android:minWidth="?attr/actionBarSize"
android:minHeight="?attr/actionBarSize" android:padding="8dp" android:scaleType="centerInside"
app:srcCompat="@drawable/ic_baseline_send_24" app:tint="@color/color_selector" tools:targetApi="m" />
</LinearLayout>
res/color/color_selector.xml
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="@android:color/secondary_text_dark" android:state_enabled="false" />
<item android:color="@color/colorPrimary" />
</selector>
res/drawable/ic_baseline_send_24.xml
<vector android:height="24dp" android:tint="#FFFFFF"
android:viewportHeight="24.0" android:viewportWidth="24.0"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M2.01,21L23,12 2.01,3 2,10l15,2 -15,2z"/>
</vector>
使用animated-selector
例子
https://github.com/alexjlockwood/adp-delightful-details
https://www.androiddesignpatterns.com/2016/11/introduction-to-icon-animation-techniques.html
使用此工具创建 animated-vector
文件