Utilisation de Drawables vectoriels Android sur un crash pré-Lollipop


91

J'utilise des dessins vectoriels dans Android avant Lollipop et voici quelques-unes de mes bibliothèques et versions d'outils:

  • Studio Android: 2.0
  • Plugin Android Gradle: 2.0.0
  • Outils de construction: 23.0.2
  • Bibliothèque de support Android: 23.3.0

J'ai ajouté cette propriété au niveau de mon application Build.Gradle

android {  
  defaultConfig {  
    vectorDrawables.useSupportLibrary = true  
   }  
}

Il convient également de mentionner que j'utilise un dessin supplémentaire tel que LayerDrawable (layer_list) comme indiqué dans le blog officiel d'Android ( lien ici ) pour définir les dessinables pour les dessins vectoriels en dehors deapp:srcCompat

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

Vous trouverez des dessins vectoriels faisant directement référence en dehors de l'application: srcCompat échouera avant Lollipop. Cependant, AppCompat prend en charge le chargement de dessinables vectoriels lorsqu'ils sont référencés dans un autre conteneur dessinable tel qu'un StateListDrawable, InsetDrawable, LayerDrawable, LevelListDrawable et RotateDrawable. En utilisant cette indirection , vous pouvez utiliser des dessins vectoriels dans des cas tels que l'attribut android: drawableLeft de TextView, qui ne serait normalement pas capable de prendre en charge les dessinables vectoriels.

Quand j'utilise app:srcCompattout fonctionne bien, mais quand j'utilise:

android:background
android:drawableLeft
android:drawableRight
android:drawableTop
android:drawableBottom

sur ImageView, ImageButton, TextViewou EditTextavant Lollipop, il jette un expection:

Caused by: android.content.res.Resources$NotFoundException: File res/drawable/search_toggle.xml from drawable resource ID #0x7f0200a9


Pour voir comment utiliser VectorDrawable avec drawableLeft, drawableRight, drawableTop, drawableBottom, consultez cette réponse
Behzad Bahmanyar

Pouvez-vous vérifier ma réponse ici? stackoverflow.com/a/40523623/2557258
Yazon2006

Dans mon cas, les drawables vactor n'étaient pas dans le bon package (ouvrir la vue du projet) réponse originale ici stackoverflow.com/a/35836318/2163045
murt

Réponses:


102

DERNIÈRE MISE À JOUR - Juin / 2019

La bibliothèque de support a un peu changé depuis la réponse originale. Désormais, même le plugin Android pour Gradle est capable de générer automatiquement le PNG au moment de la construction. Voici donc deux nouvelles approches qui devraient fonctionner de nos jours. Vous pouvez trouver plus d'informations ici:

Génération PNG

Gradle peut créer automatiquement des images PNG à partir de vos ressources au moment de la construction. Cependant, dans cette approche, tous les éléments xml ne sont pas pris en charge . Cette solution est pratique car vous n'avez rien à modifier dans votre code ou dans votre build.gradle. Assurez-vous simplement que vous utilisez Android Plugin 1.5.0 ou supérieur et Android Studio 2.2 ou supérieur .

J'utilise cette solution dans mon application et fonctionne correctement. Aucun indicateur build.gradle supplémentaire n'est nécessaire. Aucun piratage n'est nécessaire. Si vous allez dans / build / generated / res / pngs / ... vous pouvez voir tous les PNG générés.

Donc, si vous avez une icône simple (puisque tous les éléments xml ne sont pas pris en charge), cette solution peut fonctionner pour vous. Mettez simplement à jour votre Android Studio et votre plugin Android pour Gradle.

Bibliothèque d'assistance

C'est probablement la solution qui fonctionnera pour vous. Si vous êtes venu ici, cela signifie que votre Android Studio ne génère pas automatiquement les PNG. Donc, votre application plante.

Ou peut-être que vous ne voulez pas du tout qu'Android Studio génère des fichiers PNG.

Contrairement à cette "génération Auto-PNG" qui prend en charge un sous-ensemble d'éléments XML, cette solution prend en charge toutes les balises xml. Ainsi, vous avez un support complet pour votre dessin vectoriel.

Vous devez d'abord mettre à jour votre build.gradle pour le prendre en charge:

android {
  defaultConfig {
    // This flag will also prevents Android Studio from generating PNGs automatically
    vectorDrawables.useSupportLibrary = true
  }
}

dependencies {
  // Use this for Support Library
  implementation 'com.android.support:appcompat-v7:23.2.0' // OR HIGHER

  // Use this for AndroidX
  implementation 'androidx.appcompat:appcompat:1.1.0' // OR HIGHER
}

Et puis, utilisez app:srcCompatau lieu de android:srclors du chargement VectorDrawables. N'oublie pas ça.

Pour TextView, si vous utilisez la androidxversion de la bibliothèque de support, vous pouvez utiliser app:drawableLeftCompat(ou droite, haut, bas) au lieu deapp:drawableLeft

Dans le cas de CheckBox/ RadioButton, utilisez à la app:buttonCompatplace de android:button.

Si vous n'utilisez pas la androidxversion de la bibliothèque de support et que votre version minSdkVersionest 17ou supérieure ou si vous utilisez un bouton, vous pouvez essayer de définir par programme via

Drawable icon = AppCompatResources.getDrawable(context, <drawable_id>);
textView.setCompoundDrawablesWithIntrinsicBounds(<leftIcon>,<topIcon>,<rightIcon>,<bottomIcon>);

MISE À JOUR - Juil / 2016

Ils ont réactivé ce VectorDrawable dans la
bibliothèque de support Android 23.4.0

Pour les utilisateurs d'AppCompat , nous avons ajouté une API opt-in pour réactiver la prise en charge de Vector Drawables à partir des ressources (le comportement trouvé dans 23.2) via AppCompatDelegate.setCompatVectorFromResourcesEnabled (true) - gardez à l'esprit que cela peut toujours causer des problèmes d'utilisation de la mémoire et problèmes de mise à jour des instances de configuration, d'où la raison pour laquelle il est désactivé par défaut.

Peut - être ,build.gradle paramètre est maintenant obsolète et qu'il vous suffit de l'activer dans les activités appropriées (cependant, vous devez tester).

Maintenant, pour l'activer, vous devez faire:

public class MainActivity extends AppCompatActivity {
    static {
        AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
    }

    ...
}

Réponse originale - avril / 2016

Je pense que cela se produit parce que Support Vector a été désactivé dans la dernière version de la bibliothèque: 23.3.0

Selon ce POST :

Pour les utilisateurs d'AppCompat, nous avons décidé de supprimer la fonctionnalité qui vous permet d'utiliser des dessins vectoriels à partir de ressources sur des appareils pré-Lollipop en raison de problèmes rencontrés dans l'implémentation de la version 23.2.0 / 23.2.1 (ISSUE 205236) . L'utilisation de app: srcCompat et setImageResource () continue de fonctionner.

Si vous visitez le problème ISSUE 205236 , il semble qu'ils seront activés à l'avenir, mais le problème de mémoire ne sera pas résolu bientôt:

Dans la prochaine version, j'ai ajouté une API opt-in où vous pouvez réactiver le support VectorDrawable qui a été supprimé. Il est livré avec les mêmes avertissements que précédemment (utilisation de la mémoire et problèmes de mise à jour de la configuration).

J'ai eu un problème similaire. Donc, dans mon cas, j'ai rétabli toutes les icônes qui utilisent le vecteur dessiné à partir d'une ressource en images PNG (puisque le problème de mémoire continuera à se produire même après avoir fourni une option pour l'activer à nouveau).

Je ne sais pas si c'est la meilleure option, mais cela corrige tous les plantages à mon avis.


1
Merci @Guilherme P. mais pourquoi n'avez-vous pas supprimé la vectorDrawables.useSupportLibrary = truepossibilité de revenir à l'activation de la génération de png au moment de la construction ?
Behzad Bahmanyar

1
Cela a été désactivé dans la dernière version v23.3.0 en raison de problèmes de mémoire. De cette façon, ils ne peuvent pas générer de png lors de l'exécution ... C'est pourquoi dans l'erreur logcat ils affichent: vecteur de balise inconnu (ou quelque chose comme ça).
W0rmH0le

4
Voici les instructions d' activation plus.google.com/+AndroidDevelopers/posts/B7QhFkWZ6YX . Malheureusement, VectorDrawables ne fonctionne toujours pas sur l'appareil Pre-Lollipop. Je suis sur la bibliothèque de support 23.4.0 et j'ai à la fois 'generatedDensities = []' et 'vectorDrawables.useSupportLibrary = true' appelés dans defaultConfig {}.
Adam Hurwitz

1
@AdamHurwitz J'ai mis à jour la réponse .. Il semble qu'elle a été de nouveau activée .. Cependant, vous devez l'activer différemment maintenant.
W0rmH0le

1
@juztcode Vous avez raison. Vous devez utiliser 'androidx.appcompat: appcompat: 1.1.0'
W0rmH0le

63

J'ai eu le même problème. Mais en faisant beaucoup de R&D, j'ai eu la réponse.

Pour Imageview et ImageButton, app:srcCompat="@drawable/...." et pour d'autres vues comme Button, Textview, au lieu d'utiliser "drawableLeft/right..."dans le XML, spécifiez les drawables par programme comme:

button.setCompoundDrawablesWithIntrinsicBounds(AppCompatResources.getDrawable(mContext,R.drawable.ic_share_brown_18dp), null, null, null);

Et utilisez "AppCompatResources" pour obtenir le dessinable.



39

La réponse de Guillherme P est assez impressionnante. Juste pour apporter une petite amélioration, vous n'avez pas besoin d'ajouter cette ligne dans chaque activité, si vous l'avez ajoutée une fois dans la classe Application, cela fonctionnera également.

public class App extends Application {

static {
    AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
}

RAPPELEZ-VOUS: Vous devez toujours avoir activé l'utilisation de la bibliothèque de support dans gradle:

android {
  defaultConfig {
    vectorDrawables.useSupportLibrary = true
  }
}

Assurez-vous également que vous utilisez une version de bibliothèque de support supérieure à la v23.4, lorsque Google a rajouté la prise en charge des conteneurs Drawable pour VectorDrawables ( note de publication )

Mettre à jour

Et pour les changements de code:

  1. Assurez-vous de mettre à jour à app:srcCompatchaque endroit qui accepte l' android:srcattribut (l'EDI vous avertira s'il est invalide comme pour la <bitmap>balise).
  2. Pour drawableLeft, drawableStart, drawableRight, drawableEndattributs utilisés dans TextViewet des vues similaires, vous devrez les définir par programme pour l' instant. Un exemple de réglage drawableStart:

    Drawable drawable = AppCompatResources.getDrawable(
            getContext(),
            R.drawable.your_vector_drawable);
    
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
        textView.setCompoundDrawablesRelativeWithIntrinsicBounds(drawable, null, null, null);
    }

rappelez-vous que vous devez avoir activé l'utilisation de la bibliothèque de support pour les dessins vectoriels dans gradle: vectorDrawables.useSupportLibrary = true
Benny

2
Oui. Je connais. Mais il est nécessaire de disposer d'un wrapper pour que les dessinables vectoriels fonctionnent comme des dessinables pour TexViews, donc cette réponse est incomplète.
cesards

1
Très bonne réponse. Il est particulièrement utile pour les applications avec toutes ses activités étendues à partir d'une activité de base personnalisée.
hong

14

J'ai eu le même problème. Et corrigez-le en supprimant

vectorDrawables.useSupportLibrary = true

Ma version cible est 25 et la bibliothèque de support est

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

1
cela a fonctionné pour moi. Merci. Je viens de supprimer celavectorDrawables.useSupportLibrary = true
Android Mediocre

Je pense que ce n'est pas la bonne façon de le faire, de toute façon j'ai abandonné votre réponse. !!
Harish Reddy

Je vous remercie. t fonctionne pour mon application. Tous les dessinables vectoriels fonctionnent toujours s'ils ont été supprimés. L'application utilise com.android.support:appcompat-v7:28.0.0
Hong

10

VectorDrawables sur pré-sucette devrait fonctionner correctement sans utiliser

AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);

Si vous souhaitez utiliser VectorDrawables dans ImageViews, vous pouvez utiliser l'attribut srcCompatet cela fonctionnera, mais dans Buttons ou TextViews, ce ne sera pas le cas , vous devez donc envelopper le Drawable dans un InsetDrawable ou un LayerDrawable. Il y a une autre astuce que j'ai découverte, si vous utilisez la liaison de données, vous pouvez le faire:

android:drawableLeft="@{@drawable/vector_ic_access_time_24px}"
android:drawableStart="@{@drawable/vector_ic_access_time_24px}"

Cela fonctionnera comme par magie, je n'ai pas enquêté sur ce qui se passe dans les coulisses, mais je suppose que TextView utilise la méthode getDrawable de AppCompatResources ou similaire.


Comment définir une image vectorielle dans le sélecteur?
Tushar Gogna

après avoir défini vectorDrawables.useSupportLibrary = true dans gradle default et AppCompatDelegate.setCompatVectorFromResourcesEnabled (true); en activité lors de la création Pour éviter un crash avec Android: drawableleft dans Textview , définissez drawble à gauche sur la vue de texte par programme par exemple: textview.setCompoundDrawablesWithIntrinsicBounds (R.drawable.movie, 0, 0, 0);
Afjalur Rahman Rana

7

Beaucoup de R&D, pour enfin obtenir cette solution pour les plantages sur les appareils pré-sucette

Pour Imageview

  • utilisez app: srcCompat au lieu d'android: src

Pour TextView / EditText

  • Supprimez drawableleft , drawableright .... et définissez le code java drawable.

txtview.setCompoundDrawablesWithIntrinsicBounds (AppCompatResources.getDrawable (EventDetailSinglePage.this, R.drawable.ic_done_black_24_n), null, null, null);

Pour Build.gradle

vectorDrawables.useSupportLibrary = true


1
C'était la solution unique qui fonctionnait sur TextInputEditText.
heronsanches

1
Awsome, c'est résolu mon problème. Cela devrait être une réponse acceptée.
DJtiwari

6

Utilisation la plus simple:

app:drawableRightCompat ="@drawable/ic_mobilelogin"
app:drawableEndCompat="@drawable/ic_mobilelogin"
app:srcCompat="@drawable/ic_mobile"

et ... juste utiliser app:**Compatpour la compatibilité. Ajouter également le support sur build.gradle(module)

android {
   defaultConfig {
       vectorDrawables.useSupportLibrary = true
   }
}

app: drawableEndCompat et app: drawableRightCompat est à peu près la même chose si son anglais
Hossam Hassan

5

Pour toute personne qui met à niveau vers Android Gradle 3.0 et supérieur, il n'est pas nécessaire d'utiliser AppCompatDelegate.setCompatVectorFromResourcesEnabled(true)ou de définir vectorDrawables.useSupportLibrary = true(ajouter cela causera un problème) et d'utiliserapp:srcCompat , cela fonctionne simplement.

Prenez-moi deux jours pour comprendre cela, et je n'ai trouvé aucun document connexe dans les documents de Google ...


Intéressant, j'utilise gradle 3.3.0 et cette solution fonctionne. Cependant, Android Studio me dit toujours d'activer set vectorDrawables.useSupportLibrary = true.
masterwok

2

J'utilise VectorDrawables sur des appareils Pre-lollipop et voici comment je le fais: -

Étape 1: mettez ceci dans le niveau de votre application.

android {
  defaultConfig {
    vectorDrawables.useSupportLibrary = true
  }
}

Étape 2:

Mettez ceci dans votre classe Application et n'oubliez pas d'enregistrer votre classe Application dans le fichier manifeste.

public class App extends Application {
    static {
        AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
    }
}

Étape 3:

Obtenez VectorDrawables en utilisant,

imageView.setImageDrawable(ContextCompat.getDrawable(this, R.drawable.my_vector_drawable));

2

Après avoir utilisé le code ci-dessous.

android {
  defaultConfig {
  vectorDrawables.useSupportLibrary = true  
                }
        }




public class App extends Application {
static {
    AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
}}

encore, un problème d'images vectorielles existe pour les attributs ci-dessous sont

DrawableEnd, DrawableStart, DrawableTop, DrawableBottom, Arrière-plan

Dans ce cas, veuillez suivre comme ci-dessous, au lieu de référencer l'image vectorielle directement, utilisez la balise de sélection comme fichier intermédiaire pouvant être dessiné.

Exemple:

ic_warranty_icon.xml

<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="17dp"
android:height="24dp"
android:autoMirrored="true"
android:viewportWidth="17"
android:viewportHeight="24">

<path
    android:fillColor="#fff"
    android:pathData="M10.927,15.589l-1.549,0.355a7.47,7.47 0,0 1,-0.878 0.056c-4.136,0 -7.5,-3.364 -7.5,-7.5s3.364,-7.5 7.5,-7.5 7.5,3.364 7.5,7.5c0,3.286 -2.126,6.078 -5.073,7.089zM8.5,2a6.508,6.508 0,0 0,-6.5 6.5c0,3.583 2.917,6.5 6.5,6.5s6.5,-2.917 6.5,-6.5 -2.917,-6.5 -6.5,-6.5z" />

safe_ic_warranty_icon.xml

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

Votre TextView / Layout.

<TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:drawableStart="@drawable/ic_warranty_icon"
       />


<LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@drawable/ic_warranty_icon"
       />

Merci !!! Pour la propriété d'arrière-plan, l'ajout d'un sélecteur entre les deux plutôt que d'utiliser directement le dessin vectoriel a fonctionné pour l'api pré-lollipop (19).
Ankur

dans mon cas, cela ne fonctionne toujours pas pour l'API (16), même avecselector
mochadwi

1

Nous avons essayé 3 choses

vectorDrawables.useSupportLibrary = true

Définition de setCompatVectorFromResourcesEnabled dans la classe Application

static {
    AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
}

Et utilise app:srcCompat

Mais même après ça, ça échouait avec

Resources$NotFoundException: File res/drawable/$my_icon__0.xml from color state list resource ID #0x7f080008

puis nous avons compris que notre SVG avait une balise Gradient. La conversion de la balise de dégradé en chemins individuels pour l'API ci-dessous <= 23 et l'utilisation de la même API SVG> = 24 a fonctionné.

J'ai obtenu de l'aide de cette réponse https://stackoverflow.com/a/47783962/2171513


Je ne sais pas pourquoi personne n'a voté pour cela, mais cela pourrait en fait être la solution à mon problème. Merci
DevMike01

0

Chevauchez simplement le vecteur dessiné à la liste d'états, le problème sera résolu

Par exemple, vous avez une image vectorielle de flèche arrière:

ic_back_arrow.xml

oui, vous devez le superposer à la liste de calques xml (ic_back_arrow_vector_vector.xml):

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

Parce que la logique:

vectorDrawables.useSupportLibrary = true

et

AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);

ne vous aidera pas sur certains appareils chinois et anciens appareils Samsung. Si vous ne les chevauchez pas, cela échouera.


0

Je me débattais avec ça pendant des heures.

J'ai essayé tout ce que ces réponses m'ont dit, mais mon application n'a pas cessé de planter. J'ai supprimé cette ligne: app:srcCompat="@drawable/keyboard"et mon application a cessé de planter. et puis quand j'ai ajouté cette même chose, il a recommencé à planter. J'ai donc décidé d'ouvrir ce fichier et j'ai vu une erreur à la première ligne disant

«Le 'Keyboard' dessinable n'a pas de déclaration dans le dossier dessinable de base, cela peut entraîner des plantages.

J'ai cliqué avec le bouton droit sur le fichier et cliqué sur "Afficher dans l'explorateur" et ce n'était pas dans le dossier drawable mais dans le répertoire drawable-v24. Je l'ai donc copié et collé dans le répertoire drawable et finalement je me suis débarrassé des plantages.


-2

La suggestion de Guilherme P ne fonctionnait pas pour moi. Je suis allé de l'avant et j'ai pris la décision d'utiliser des png là où je dois faire des choses en dehors de l'application: srcCompat ie drawableLeft, drawableRight, etc. C'était un changement assez facile à faire, et n'a pas les problèmes de mémoire potentiels AppCompatDelegate.setCompatVectorFromResourcesEnabled ( vrai); introduit.


-3

Une alternative à la réponse de Benny est de créer une Activitysuperclasse:

public abstract class VectorDrawableActivity extends AppCompatActivity {
  static {
    AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
  }

  //...
}

Maintenant, étendez VectorDrawableActivityau lieu de AppCompatActivity.


@cesards Pourquoi pas?
Code-Apprentice
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.