Как использовать векторные чертежи с представлением помимо ImageView с srcCompat?

app:srcCompat с ImageView допускает обратное совместимое использование векторных чертежей. Но как вы можете использовать их с другими View помимо ImageView? Например, атрибуты TextView, такие как android:drawableLeft.

Также использование вектора, выведенного как android:icon с MenuItem, вызвало сбой со следующим исключением:

Fatal Exception: android.view.InflateException: Binary XML file line #2: Error inflating class <unknown>
   at android.view.LayoutInflater.createView(LayoutInflater.java:626)
   at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:702)
   at android.view.LayoutInflater.inflate(LayoutInflater.java:470)
   at android.view.LayoutInflater.inflate(LayoutInflater.java:398)
   at android.support.v7.view.menu.MenuItemImpl.setActionView(MenuItemImpl.java:621)
   at android.support.v7.view.menu.MenuItemImpl.setActionView(MenuItemImpl.java:40)
   at android.support.v4.view.MenuItemCompat.setActionView(MenuItemCompat.java:310)
   at android.support.v7.view.SupportMenuInflater$MenuState.setItem(SupportMenuInflater.java:465)
   at android.support.v7.view.SupportMenuInflater$MenuState.addItem(SupportMenuInflater.java:479)
   at android.support.v7.view.SupportMenuInflater.parseMenu(SupportMenuInflater.java:196)
   at android.support.v7.view.SupportMenuInflater.inflate(SupportMenuInflater.java:118)
   at com.example.niceapp.context.main.MainActivity.onCreateOptionsMenu(MainActivity.java:101)
   at android.app.Activity.onCreatePanelMenu(Activity.java:2578)

С поддержкой библиотеки 23.2.0, как решить эту проблему?

Ответ 1

Для AppCompat версии 23.3.0, где нет решения для работы с помощью селектора XML (принятый ответ razzledazzle), мы можем сделать это программным путем:

activity_main.xml

<android.support.v7.widget.AppCompatImageButton
    android:id="@+id/btnEnter"
    />

MainActivity.java

AppCompatImageButton image = (AppCompatImageButton) findViewById(R.id.btnEnter);
if (image != null) {
    VectorDrawableCompat vcAccept = VectorDrawableCompat.create(getResources(), R.drawable.vc_accept, getTheme());
    VectorDrawableCompat vcAcceptWhite = VectorDrawableCompat.create(getResources(), R.drawable.vc_accept_white, getTheme());

    StateListDrawable stateList = new StateListDrawable();
    stateList.addState(new int[]{android.R.attr.state_focused, -android.R.attr.state_pressed}, vcAccept);
    stateList.addState(new int[]{android.R.attr.state_focused, android.R.attr.state_pressed}, vcAcceptWhite);
    stateList.addState(new int[]{-android.R.attr.state_focused, android.R.attr.state_pressed}, vcAcceptWhite);
    stateList.addState(new int[]{}, vcAccept);

    image.setImageDrawable(stateList);
}

Этот код эквивалентен для этого селектора xml:

<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_focused="true" android:state_pressed="false" android:drawable="@drawable/vc_accept" />
    <item android:state_focused="true" android:state_pressed="true" android:drawable="@drawable/vc_accept_white" />
    <item android:state_focused="false" android:state_pressed="true" android:drawable="@drawable/vc_accept_white" />
    <item android:drawable="@drawable/vc_accept" />
</selector>

UPDATE

Если векторный чертеж не показан с использованием API 23, вам нужно сначала преобразовать VectorDrawable в обычный Drawable. Если вы хотите использовать setCompoundDrawablesWithIntrinsicBounds, вам нужно это сделать, но для StateListDrawable мне не нужно было.

Drawable icon;
if (android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
    icon = VectorDrawableCompat.create(getResources(), R.drawable.vc_icon, getContext().getTheme());
} else {
    icon = getResources().getDrawable(R.drawable.vc_icon, getContext().getTheme());
}

Ответ 2

Обновление 2: они добавили возможность включить его снова в библиотеке поддержки 23.4.0:

Для пользователей AppCompat мы добавили API-интерфейс для повторного включения поддержки векторных чертежей из ресурсов (поведение, обнаруженное в 23.2), с помощью AppCompatDelegate.setCompatVectorFromResourcesEnabled( ) - имейте в виду, что это все еще может вызвать проблемы с использованием памяти и проблемы с обновлением экземпляров Configuration, поэтому почему она по умолчанию отключена.

Обновление: Это больше не работает начиная с версии 23.3.0

Для пользователей AppCompat мы решили удалить функции, которые позволяют использовать векторные чертежи из ресурсов на устройствах, предшествующих Lollipop из-за проблем, обнаруженных при реализации в версии 23.2.0/23.2.1 [https://code.google.com/p/android/issues/detail?id=205236, https://code.google.com/p/android/issues/detail?id=204708]. Использование приложения: srcCompat и setImageResource() продолжает работать.

От анонс разработчиков Google+ в Google+


Использование AppCompat и приложения: srcCompat - самый надежный способ интеграции векторных чертежей в ваше приложение.

Эта цитата из официального blogpost для выпуска версии 23.2.0 в Библиотеке поддержки.

В сообщении также упоминается следующее:

Вы найдете прямое обращение к векторным чертежам вне app:srcCompat, которые не будут выполняться до Lollipop. Тем не менее, AppCompat поддерживает загрузку векторных векторов, когда они ссылаются в другом контейнере, например, StateListDrawable, InsetDrawable, LayerDrawable, LevelListDrawable и RotateDrawable. Используя эту косвенность, вы можете использовать векторные чертежи в таких случаях, как атрибут TextView s android:drawableLeft, который обычно не сможет поддерживать векторные чертежи.

Это приводит к следующим шагам:

Шаг 1:

Создайте или импортируйте векторный ресурс, который вам нужен для приложения. Например, можно создать вектор, подходящий для значка поиска, и назовите его ic_action_search_vector.xml

Шаг 2:

Создайте другой ресурс, который можно использовать для прокси-сервера, для созданного ранее вектора. Скажем, для предыдущего ic_action_search_vector.xml, ic_action_search.xml может быть создан как простой StateListDrawable, который может содержать следующие строки:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@drawable/ic_action_search_vector"/>
</selector>

Этот шаг можно пропустить, если вы указали вектор, который можно извлечь из другого ресурса, который вы будете использовать с вашим представлением.

Шаг 3:

Используйте ресурс, который можно вытащить (здесь, ic_action_search.xml), который ссылается на векторный drawable (ic_action_search_vector.xml) вместо прямого преобразования вектора. Для меню это будет выглядеть так:

<item android:id="@+id/search"
        android:title="@string/search"
        android:icon="@drawable/ic_action_search"
        app:showAsAction="always"/>

Это решение этой проблемы!

Ответ 3

векторные образы можно использовать pre-Lollipop в других местах, чем app:srcCompat, но он поставляется с ценой.

Я сделал эту диаграмму, чтобы помочь (действителен для библиотеки поддержки 23.4.0 - не менее - 25.1.0).

VectorDrawable cheatsheet

Ответ 4

Вы можете добавить Vector Drawable в TextView программно. Используйте VectorDrawableCompat для добавления drawableLeft/drawableRight/drawableTop/drawableBottom/drawableStart/drawableEnd.

Шаги:

я. Если TextView находится внутри Activity:

TextView tvUserName= (TextView)findViewById(R.id.et_username_or_email);
VectorDrawableCompat drawableCompat=VectorDrawableCompat.create(getResources(), R.drawable.layer_list_ic_user, tvUserName.getContext().getTheme());
tvUserName.setCompoundDrawablesRelativeWithIntrinsicBounds(drawableCompat, null, null, null);

II. Если TextView находится внутри фрагмента:

TextView tvUserName= (TextView )view.findViewById(R.id.et_username_or_email);
VectorDrawableCompat drawableCompat=VectorDrawableCompat.create(getActivity().getResources(), R.drawable.layer_list_ic_user, tvUserName.getContext().getTheme());
tvUserName.setCompoundDrawablesRelativeWithIntrinsicBounds(drawableCompat, null, null, null);

Для получения дополнительной информации о VectorDrawableCompat см. ссылку

Ответ 5

Android 5.0 (API уровня 21) и выше обеспечивает поддержку векторной поддержки. Если ваше приложение имеет минимальный уровень API, который ниже, Vector Asset Studio добавляет векторный файл для рисования в ваш проект; также, во время сборки Gradle создает растровые изображения PNG с различными разрешениями. Gradle генерирует плотности PNG, определенные свойством сгенерированных свойств домена (DSL), созданным в файле build.gradle. Для создания PNG для системы сборки требуется Android Plugin для Gradle 1.5.0 или новее.

Это неверно, если вы включили в свой gradle  vectorDrawables.useSupportLibrary = true

либо установите значение false, либо полностью удалите строку, и все ваши векторы будут работать так, как они были. Но для старых версий андроида они смогут полагаться на преобразованный PNG

Ответ 6

Я использую новую библиотеку поддержки, и все, что мне нужно сделать, это:

compile 'com.android.support:appcompat-v7:25.1.1'

В файле Build.gradle

 defaultConfig {

    vectorDrawables.useSupportLibrary = true
}

Теперь, когда вы используете такой же фрагмент, активность или адаптер, используйте это как первую строку в своем классе

static {
    AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
}

после этого используйте, как и раньше, something.xml

<ImageView
    android:id="@+id/ivMainNavigationIcon"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content" />

Something.java

thumbIcon.setImageDrawable(ContextCompat.getDrawable(context,R.drawable.ic_check_circle_black_24dp));

или если у вас есть требуемый идентификатор для динамического набора

thumbIcon.setImageDrawable(ContextCompat.getDrawable(context,drawableID));

Ответ 7

Форма android studio 3.0.0 android: src не поддерживает векторное изображение и ниже 21 мое исключение исключения. используйте приложение: srcCompat для векторного изображения. Храните файлы векторного изображения внутри папки drawable.

android {  
   defaultConfig {  
     vectorDrawables.useSupportLibrary = true  
    }  
 }

И в классе приложения определите это:

@Override
public void onCreate() {
    super.onCreate();
    AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
}

Теперь вы можете использовать ваш .xml файл. Не забудьте указать эту ссылку: xmlns: app = "http://schemas.android.com/apk/res-auto"

<RelativeLayout
    android:id="@+id/counterValuePanel"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >
    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:srcCompat="@drawable/ic_cart_notify"/>
</RelativeLayout>

Теперь вы можете использовать приложение: srcCompat = "@drawable/ic_cart_notify", но если вы попытаетесь использовать в android: background или android: drawableLeft, то у вас есть исключение "Ошибка раздувания". Для этого создайте новый обернутый файл .xml, ic_cart_notify - значок вектора.

<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@drawable/ic_cart_notify"/>
</layer-list>