web-dev-qa-db-fra.com

Vector Drawable in Layer List on Older Android Versions

Sur les versions plus récentes Android, le code suivant:

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:Android="http://schemas.Android.com/apk/res/Android">
    <item>
            <shape Android:shape="oval">
                <solid Android:color="#bdbdbd" />
                <size
                    Android:width="60dp"
                    Android:height="60dp" />
            </shape>
    </item>
    <item
        Android:drawable="@drawable/ic_library_books_black_24dp"
        Android:gravity="center"
        Android:width="40dp"
        Android:height="40dp"
        >
    </item>
</layer-list>

produit cela parfaitement:

Vector drawable showing white lists on grey circular background

Cependant, les versions antérieures Android (API 16 et 19, d'après ce que j'ai testé) n'aiment pas du tout cela et j'obtiens

E/AndroidRuntime: FATAL EXCEPTION: main
              Process: package.app, PID: 11490
              Android.view.InflateException: Binary XML file line #26: Error inflating class ImageView

sur l'inflation. J'ai utilisé app:srcCompat pour toutes mes images, il n'y a donc aucun problème.

Les dessins vectoriels standard fonctionnent également très bien, mais lorsqu'ils sont placés dans un layer-list ils causent le chaos. Existe-t-il des solutions de contournement?

30
Semdog

Les attributs largeur/hauteur du vecteur dessinable dans votre liste de calques ne sont pris en charge que dans l'API 23 (Marshmallow) et les versions supérieures. Si vous regardez votre liste de calques dessinable dans l'éditeur Android Studio, ces attributs devraient avoir des blocs jaunes autour d'eux avec un avertissement que cela ne fonctionnera pas de manière fiable sur les appareils plus anciens.

Mais je pense que vous pouvez vous débarrasser de l'avertissement et obtenir le même effet de centrage comme celui-ci:

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:Android="http://schemas.Android.com/apk/res/Android">
    <item>
        <shape Android:shape="oval">
            <solid Android:color="#bdbdbd" />
            <size
                Android:width="60dp"
                Android:height="60dp" />
        </shape>
    </item>
    <item
        Android:drawable="@drawable/ic_library_books_black_24dp"
        Android:top="10dp"
        Android:bottom="10dp"
        Android:left="10dp"
        Android:right="10dp">
    </item>
</layer-list>

J'ai testé cela sur un ancien téléphone API 15 et cela a bien fonctionné. J'espère que cela fonctionne aussi pour vous.

Mise à jour:

Dans une version précédente de cette réponse, j'avais déconseillé d'utiliser vectorDrawables.useSupportLibrary = true avec des listes de calques, car cela provoquait des plantages. Cependant, j'ai récemment entendu parler d'une solution de contournement qui semble résoudre le plantage (tout en évitant les gros fichiers png générés automatiquement qui @ développeur Android correctement mentionnés). Voici un résumé de ce qui doit être fait pour que cela fonctionne correctement:

Assurez-vous d'utiliser srcCompat dans votre xml.

    <Android.support.v7.widget.AppCompatImageView
    Android:layout_width="wrap_content"
    Android:layout_height="wrap_content"
    app:srcCompat="@drawable/layer_list_with_svg" />

Ajoutez 'vectorDrawables.useSupportLibrary' à app/build.gradle

 Android {
   defaultConfig {
     vectorDrawables.useSupportLibrary = true
   }
 }

Ajouter 'setCompatVectorFromResourcesEnabled (true)' à onCreate

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

Pourquoi tout cela est-il nécessaire?

Comme une personne plus avertie me l'a expliqué, cela a à voir avec la façon dont Android charge les fichiers vectoriels compatibles. Si vous utilisez vectorDrawables.useSupportLibrary = true alors les vues d'images se chargeront VectorDrawableCompat dessinables lorsque vous utilisez app:srcCompat. Lorsque votre liste de calques dessinable est gonflée, il est impossible de savoir comment créer ces dessins vectoriels référencés. Mais si vous activez setCompatVectorFromResourcesEnabled, il essaiera de raccorder le chargement de dessin vectoriel à un niveau beaucoup plus bas que les vues d'image et leur app:srcCompat attribut, il est donc en mesure de comprendre comment charger les dessins vectoriels référencés dans la liste des calques.

27

Cette réponse s'appuie sur l'article AppCompat - Age of the Vectors de Chris Banes (qui travaille sur la bibliothèque de support). Pour cette question, nous examinons spécifiquement la section intitulée La méthode "magique" .

Le crash que vous rencontrez est dû au fait que la bibliothèque de support n'autorise que certains façons d'utiliser VectorDrawables par défaut, et la liste des couches n'en fait pas partie.

Il existe un bloc de code spécifique que vous pouvez ajouter en haut de votre activité pour permettre une autre utilisation de VectorDrawable telle que <layer-list>:

static {
    AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
}

Remarque: l'article lié contient une faute de frappe dans ce nom de méthode, en utilisant "FromSources", il doit s'agir de "FromResources" comme indiqué ci-dessus.

Vous devez l'ajouter à chaque activité pour laquelle vous souhaitez utiliser ces éléments à dessiner, ou peut-être l'inclure dans une classe BaseActivity à partir de laquelle vos autres activités s'étendent.

Selon l'article, cela devrait signifier que les éléments suivants fonctionneront désormais:

DrawableContainers qui référencent d'autres ressources drawables qui ne contiennent qu'une ressource vectorielle.

... StateListDrawable ... InsetDrawable, LayerDrawable, LevelListDrawable et RotateDrawable.

Il convient de noter cependant que cette méthode est fortement formulée avec le mot "peut", ceci peut fonctionner, et il n'est pas activé par défaut, alors soyez conscient et vérifiez qu'il fonctionne vraiment pour vous!

Maintenant, il y a une autre dimension à cette question, le crédit aux autres utilisateurs Selim Ajimi et Rapunzel Van Winkle pour répondre à cela dans leurs réponses. <layer-list> a un comportement différent entre les API, en particulier les attributs width et height de votre <item> uniquement pris en charge dans l'API 23+. Ce n'est pas la cause de votre plantage, ni ne provoquera le plantage de votre application, mais cela signifiera que votre image ne ressemblera pas comme prévu une fois qu'elle fonctionnera dans les API précédentes.

La suggestion de Rapunzel Van Winkle semble en effet être un bon moyen de positionner correctement le drawable sur les API (testé sur les API 16 et 24):

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:Android="http://schemas.Android.com/apk/res/Android">
    <item>
        <shape Android:shape="oval">
            <solid Android:color="#bdbdbd" />
            <size
                Android:width="60dp"
                Android:height="60dp" />
        </shape>
    </item>
    <item
        Android:drawable="@drawable/ic_library_books_black_24dp"
        Android:top="10dp"
        Android:bottom="10dp"
        Android:left="10dp"
        Android:right="10dp"
        >
    </item>
</layer-list>
9
Lewis McGeary
<?xml version="1.0" encoding="utf-8"?>
<layer-list
    xmlns:Android="http://schemas.Android.com/apk/res/Android" >
    <item
        Android:drawable="@[package:]drawable/drawable_resource"
        Android:id="@[+][package:]id/resource_name"
        Android:top="dimension"
        Android:right="dimension"
        Android:bottom="dimension"
        Android:left="dimension" />
</layer-list>

Comme vous pouvez le voir ici n'a pas d'attributs largeur/hauteur ...

Vous pouvez ajouter Bitmap à l'élément Je pense que c'est la meilleure solution

1
Selim Ajimi

La prise en charge de VectorDrawble sur les anciennes Android versions est assez limitée , en supposant que vous utilisez vectorDrawables.useSupportLibrary = true:

Vous pouvez utiliser VectorDrawableCompat au niveau 7 de l'API et AnimatedVectorDrawableCompat sur tous les appareils exécutant Android 5.0 (API niveau 11) et supérieur. La manière Android charge les éléments à dessiner, pas chaque endroit qui accepte un ID dessinable, comme dans un fichier XML, prend en charge le chargement de dessins vectoriels. Le package Android.support.v7.appcompat a ajouté un certain nombre de fonctionnalités pour faciliter l'utilisation des dessins vectoriels. Premièrement, lorsque vous utilisez Android .support.v7.appcompat package avec ImageView ou avec des sous-classes telles que ImageButton et FloatingActionButton, vous pouvez utiliser le nouvel attribut app: srcCompat pour référencer les dessins vectoriels ainsi que tout autre dessin disponible pour Android: src

Même en code, vous êtes limité:

Pour modifier les drawables au moment de l'exécution, vous pouvez utiliser la méthode setImageResource () comme précédemment. Utilisation d'AppCompat et de l'application: srcCompat est la méthode la plus infaillible d'intégration de dessins vectoriels dans votre application.

Que pouvez-vous faire?

Peu de solutions possibles:

  1. vous pouvez dessiner une alternative pour le VectorDrawable que vous utilisez dans le LayerDrawable. Mettez-le dans res/drawable-xxhdpi, par exemple, et le VectorDrawable dans res/drawable-anydpi.

  2. n'utilisez pas vectorDrawables.useSupportLibrary = true jusqu'à ce que l'application dispose d'un minSdk capable d'utiliser VectorDrawable. Cela fonctionnera car le IDE générera des fichiers PNG pour vous.

  3. créez le layerList par programme (exemple ( ici ), et pour mettre le VectorDrawable, utilisez AppCompatResources.getDrawable. Vous pourriez également être en mesure de le faire avec XML, à l'exception de la partie de mise de VectorDrawable.

BTW, la raison pour laquelle il est limité, est que Google n'a pas réussi à le rendre efficace et sans problèmes. Vous pouvez obtenir leur tentative initiale de prise en charge complète, en utilisant AppCompatDelegate.setCompatVectorFromResourcesEnabled (true), mais comme le disent les documents, vous prenez un risque ici :

Par défaut, cette fonctionnalité est désactivée, car son activation peut entraîner des problèmes d'utilisation de la mémoire et des problèmes de mise à jour des instances de configuration. Si vous mettez à jour la configuration manuellement, vous ne souhaiterez probablement pas l'activer. Vous avez été prévenu .

1
android developer