Pourquoi ma mise à l'échelle vectorielle n'est-elle pas conforme aux attentes?

97

J'essaie d'utiliser des dessins vectoriels dans mon application Android. De http://developer.android.com/training/material/drawables.html (c'est moi qui souligne):

Dans Android 5.0 (niveau d'API 21) et au-dessus, vous pouvez définir des dessins vectoriels, qui sont mis à l'échelle sans perdre la définition.

En utilisant ce dessinable:

<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" />

et cette ImageView:

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

produit cette image floue en essayant d'afficher l'icône à 400dp (sur un gros appareil mobile haute résolution d'environ 2015 exécutant une sucette):

flouBellIcon

La modification de la largeur et de la hauteur dans la définition du vecteur dessinable à 200dp améliore considérablement la situation à la taille de rendu de 400dp. Cependant, le définir comme dessinable pour un élément TextView (c'est-à-dire l'icône à gauche du texte) crée maintenant une énorme icône.

Mes questions:

1) Pourquoi y a-t-il une spécification de largeur / hauteur dans le dessin vectoriel? Je pensais que le point entier de ceux-ci est qu'ils augmentent et diminuent sans perte de sens, ce qui rend la largeur et la hauteur dénuées de sens dans sa définition?

2) Est-il possible d'utiliser un seul vecteur dessinable qui fonctionne comme un dessin 24dp sur un TextView mais qui s'adapte bien pour utiliser des images beaucoup plus grandes? Par exemple, comment éviter de créer plusieurs dessins vectoriels de tailles différentes et en utiliser un qui s'adapte à mes exigences de rendu?

3) Comment utiliser efficacement les attributs width / height et quelle est la différence avec viewportWidth / Height?

Détails supplémentaires:

  • L'appareil exécute l'API 22
  • Utilisation d'Android Studio v1.5.1 avec Gradle version 1.5.0
  • Le manifeste est le niveau de compilation et cible 23, le niveau minimum 15. J'ai également essayé de déplacer le niveau minimum à 21, mais cela n'a fait aucune différence.
  • La décompilation de l'APK (avec un niveau minimum défini sur 21) affiche une seule ressource XML dans le dossier dessinable. Aucune image tramée n'est produite.
Chris Knight
la source
Juste pour être clair. Vous utilisez le studio Android? Quelle version d'Android Studio et quelle version du plugin Gradle? Lorsque je clique avec le bouton droit sur le dossier dessinable dans Android Studio et que New -> Vector Assetje le choisis , le fichier XML d'image vectorielle est déposé dans mon dossier dessinable. Cependant, si j'utilise apktool pour décompresser l'APK qui est construit, je vois que les fichiers XML sont drawable-anydpi-v21et évoluent correctement sur les appareils API 21+. Les fichiers raster sont placés dans les drawable-<mdpi/hdpi/etc>-v4dossiers et ne sont pas utilisés sur les appareils API 21+ (en fonction du fait qu'ils sont mis à l' échelle correctement)
Cory Charlton
@Cory Charlton. Curieuse. J'utilise Studio 1.5.1 avec Gradle 1.5.0. Après avoir changé le niveau minimum à 21, le seul endroit où l'image apparaît dans l'APK est le drawabledossier. La largeur / hauteur dans le fichier xml dessinable vectoriel est de 24dp. Spécifier un ImageView d'une hauteur / largeur de 400 dpi crée définitivement une image mal mise à l'échelle.
Chris Knight
C'est ce à quoi je m'attendais. La seule raison pour laquelle je me retrouve avec le drawable-anydpi-v21est que mon minSdkVersionest inférieur à 21. Y a-t-il un changement de comportement si vous définissez le minSdkVersionsur moins de 21? Qu'en est-il du déplacement du XML vers drawable-anydpi? Je ne m'attendrais pas à ce qu'il y ait un changement, mais je m'attendrais également à ce que votre image vectorielle soit mise à l'échelle correctement ...
Cory Charlton
La modification de la version minimale à 15 produit les mêmes résultats que vous. Un seul fichier xml drawable-anydpi-v21avec diverses images pixellisées dans le fichier mdi / hdpi / etc. Dossiers. Aucun changement au résultat final rendu cependant.
Chris Knight
Très étrange ... J'ai modifié une de mes applications en utilisant votre image et cela fonctionne bien (voir ma réponse modifiée)
Cory Charlton

Réponses:

100

Il y a de nouvelles informations sur ce problème ici:

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

Il semble que l'utilisation du logiciel android:scaleType="fitXY"le fasse évoluer correctement sur Lollipop.

D'un ingénieur Google:

Salut, Faites-moi savoir si scaleType = 'fitXY' peut être une solution de contournement pour vous, afin d'obtenir une image nette.

La guimauve Vs Lollipop est due à un traitement spécial de détartrage ajouté à la guimauve.

Aussi, pour vos commentaires: "Comportement correct: le vecteur dessiné doit être mis à l'échelle sans perte de qualité. Donc, si nous voulons utiliser le même actif dans 3 tailles différentes dans notre application, nous n'avons pas à dupliquer vector_drawable.xml 3 fois avec des tailles codées en dur. "

Même si je suis tout à fait d'accord que cela devrait être le cas, en réalité, la plate-forme Android a des problèmes de performances tels que nous n'avons pas encore atteint le monde idéal. Il est donc recommandé d'utiliser 3 vector_drawable.xml différents pour de meilleures performances si vous êtes sûr de vouloir dessiner 3 tailles différentes sur l'écran en même temps.

Le détail technique est que nous utilisons un bitmap sous le hook pour mettre en cache le rendu du chemin complexe, de sorte que nous puissions obtenir les meilleures performances de redessinage, au même titre que le redessinage d'un bitmap dessiné.

Parc Taehyun
la source
27
Ok Google, c'est absolument terrible que nous devions copier-coller des dessinables identiques avec les mêmes fenêtres et des chemins qui n'ont que des tailles différentes.
Miha_x64
Cela a corrigé le problème sur l'appareil qui montrait mon logo pixélisé, mais sur l'autre unité, où tout était OK avant, c'est maintenant le mauvais rapport hauteur / largeur. Je ne comprends pas pourquoi c'est un problème, c'est un vecteur! Pas des pixels! : P
tplive
@Harish Gyanani, android: scaleType = "fitXY" résout le problème mais qu'en est-il des icônes dynamiques et non carrées?
Manuela
28

1) Pourquoi y a-t-il une spécification de largeur / hauteur dans le dessin vectoriel? Je pensais que le point entier de ceux-ci est qu'ils augmentent et diminuent sans perte de sens, ce qui rend la largeur et la hauteur dénuées de sens dans sa définition?

Pour les versions de SDK inférieures à 21 où le système de construction doit générer des images raster et comme taille par défaut dans les cas où vous ne spécifiez pas la largeur / hauteur.

2) Est-il possible d'utiliser un seul vecteur dessinable qui fonctionne comme un dessin 24dp sur un TextView ainsi qu'une grande image de largeur proche de l'écran?

Je ne pense pas que cela soit possible si vous devez également cibler des SDK inférieurs à 21.

3) Comment utiliser efficacement les attributs width / height et quelle est la différence avec viewportWidth / Height?

Documentation: (en fait pas très utile maintenant que je l'ai relue ...)

android:width

Utilisé pour définir la largeur intrinsèque du dessinable. Cela prend en charge toutes les unités de dimension, normalement spécifiées avec dp.

android:height

Utilisé pour définir la hauteur intrinsèque du dessinable. Cela prend en charge toutes les unités de dimension, normalement spécifiées avec dp.

android:viewportWidth

Utilisé pour définir la largeur de l'espace de la fenêtre. Viewport est essentiellement le canevas virtuel sur lequel les chemins sont dessinés.

android:viewportHeight

Utilisé pour définir la hauteur de l'espace de la fenêtre. Viewport est essentiellement le canevas virtuel sur lequel les chemins sont dessinés.

Plus de documentation:

Android 4.4 (niveau d'API 20) et inférieur ne prend pas en charge les dessins vectoriels. Si votre niveau d'API minimum est défini sur l'un de ces niveaux d'API, Vector Asset Studio demande également à Gradle de générer des images raster du vecteur pouvant être dessiné pour une compatibilité descendante. Vous pouvez faire référence aux actifs vectoriels comme Drawabledans le code Java ou@drawable dans le code XML; lorsque votre application s'exécute, l'image vectorielle ou raster correspondante s'affiche automatiquement en fonction du niveau d'API.


Edit: Quelque chose de bizarre se passe. Voici mes résultats dans l'émulateur SDK version 23 (le dispositif de test Lollipop + est mort en ce moment ...):

Bonnes cloches sur 6.x

Et dans l'émulateur SDK version 21:

Cloches de merde sur 5.x

Cory Charlton
la source
1
Merci Cory, j'avais vu cette documentation et je ne l'ai pas trouvée très utile. Mon appareil est Lollipop (v22) et pourtant je ne parviens pas à mettre le graphique à l'échelle, au-dessus des 24dp spécifiés dans le dessin vectoriel, que la hauteur / le poids soient exactement spécifiés dans ImageView ou non. J'ai testé un manifeste avec la cible et le niveau de compilation 23 et le niveau minimum 21, mais il n'augmentera pas facilement mon dessin vectoriel sans que je change la largeur / hauteur dans le dessinable lui-même. Je ne veux pas vraiment créer plusieurs drawables dont la seule différence est la hauteur / largeur!
Chris Knight
1
Merci pour la confirmation et apprécions vraiment votre aide. Comme vous l'avez démontré, cela devrait clairement fonctionner comme je l'attends. L'utilisation de votre code exact produit le même résultat que celui que j'avais initialement. Frustrant!
Chris Knight
1
MISE À JOUR: J'ai couru mon code contre l'émulateur avec l'API 22 et j'obtiens toujours le même résultat. Ran mon code contre l'émulateur avec l'API 23 et, tada !, cela fonctionne. Il semble que la mise à l'échelle ne fonctionne que sur les appareils API 23+. J'ai essayé de changer la version du SDK cible, mais cela n'a fait aucune différence.
Chris Knight
Avez-vous trouvé une solution à ce problème?
dazza5000
@corycharlton est-il correct de supprimer width height viewport heightet widthde supprimer xml?
VINNUSAURUS
9

AppCompat 23.2 introduit des dessins vectoriels pour tous les appareils exécutant Android 2.1 et supérieur. Les images sont mises à l'échelle correctement, indépendamment de la largeur / hauteur spécifiée dans le fichier XML du dessin vectoriel. Malheureusement, cette implémentation n'est pas utilisée dans l'API de niveau 21 et supérieur (en faveur de l'implémentation native).

La bonne nouvelle est que nous pouvons forcer AppCompat à utiliser son implémentation aux niveaux d'API 21 et 22. La mauvaise nouvelle est que nous devons utiliser la réflexion pour ce faire.

Tout d'abord, assurez-vous que vous utilisez la dernière version d'AppCompat et que vous avez activé la nouvelle implémentation vectorielle:

android {
  defaultConfig {
    vectorDrawables.useSupportLibrary = true
  }
}

dependencies {
    compile 'com.android.support:appcompat-v7:23.2.0'
}

Ensuite, appelez useCompatVectorIfNeeded();onCreate () de votre application:

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();
        }
    }
}

Enfin, assurez-vous que vous utilisez app:srcCompat au lieu de android:srcpour définir l'image de votre ImageView:

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

Vos dessins vectoriels devraient maintenant être mis à l'échelle correctement!

user3210008
la source
Bonne approche, mais AppCompat 25.4 (et peut-être plus tôt) donne une erreur de charpie "ne peut être appelé qu'à partir du même groupe de bibliothèques". Les outils de construction que j'utilise le permettent toujours de se construire, mais je ne suis pas sûr qu'il y ait des conséquences à l'exécution. Sur le point de tester.
androidguy
ne peut être appelé qu'à partir du même groupe de bibliothèques, supprimez cette erreur en ajoutant ceci: lintOptions {disable 'RestrictedApi'}
Usama Saeed US
6

1) Pourquoi y a-t-il une spécification de largeur / hauteur dans le dessin vectoriel? Je pensais que le point entier de ceux-ci est qu'ils augmentent et diminuent sans perte de sens, ce qui rend la largeur et la hauteur dénuées de sens dans sa définition?

Il s'agit simplement de la taille par défaut du vecteur au cas où vous ne la définiriez pas dans la vue de mise en page. (c'est-à-dire que vous utilisez du contenu enveloppant pour la hauteur et la largeur de votre image)

2) Est-il possible d'utiliser un seul vecteur dessinable qui fonctionne comme un dessin 24dp sur un TextView ainsi qu'une grande image de largeur proche de l'écran?

Oui, c'est possible et je n'ai eu aucun problème de redimensionnement tant que le périphérique en cours d'exécution utilise sucette ou une version supérieure. Dans les API précédentes, le vecteur est converti en png pour différentes tailles d'écran lorsque vous créez l'application.

3) Comment utiliser efficacement les attributs width / height et quelle est la différence avec viewportWidth / Height?

Cela affecte la façon dont l'espace autour de votre vecteur est utilisé. c'est-à-dire que vous pouvez l'utiliser pour changer la "gravité" du vecteur à l'intérieur de la fenêtre, donner au vecteur une marge par défaut, laisser certaines parties du vecteur hors de la fenêtre, etc ... Normalement, vous les définissez simplement sur la même Taille.

émirua
la source
Merci Emiura. Je serais intéressé de savoir comment vous avez réalisé plusieurs tailles de rendu en utilisant le même dessinable. En utilisant un dessin 24dp (à des fins de test), j'ai changé mon ImageView pour avoir une largeur et une hauteur spécifiées de 400dp et je l'ai exécuté sur mon appareil Lollipop (v22) mais le rendu est toujours médiocre, comme une image tramée à grande échelle plutôt qu'une image vectorielle. Également changé mon manifeste pour avoir la cible et compiler contre la version v23 et min 21. Aucune différence.
Chris Knight
Dans ce cas, il vous suffit d'utiliser la plus grande taille dans votre dessin vectoriel, puis de spécifier la taille 24dp dans votre vue texte.
emirua
Je vois maintenant que vous obtenez des images floues lorsque vous agrandissez avec de très grandes différences entre la taille du vecteur dessiné et la taille de la mise en page dans Lollipop. Cependant, il est encore beaucoup plus simple de définir la plus grande taille plutôt que d'avoir un tas de fichiers pour différentes tailles d'écran;).
emirua
4

Je résous mon problème en changeant simplement la taille de l'image vectorielle. Dès le début, c'était une ressource comme:

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

Et je l'ai changé en 150dp (taille d'ImageView dans la mise en page avec cette ressource vectorielle) comme:

<vector xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:aapt="http://schemas.android.com/aapt"
    android:width="150dp"
    android:height="150dp"
    android:viewportHeight="300"
    android:viewportWidth="300">

Ça marche pour moi.

Djek-Grif
la source
merci, cela suffit pour résoudre le problème sur ma tablette où il est apparu pixelisé.
user2342558
3

J'essaye la plupart de ces moyens, mais malheureusement, aucun d'entre eux n'a fonctionné. J'essaie fitXYdans ImageView, j'essayer d' utiliser app:srcCompatmais ne peux même pas l' utiliser. mais j'ai trouvé une astuce:

définissez Drawable sur backgroundde votre ImageView.

Mais le but de cette façon est de voir les dimensions. si votre ImageViewa des dimensions incorrectes, drawable obtient Stretch. vous devez le contrôler dans les versions pré-lollipop ou utiliser certaines vues PercentRelativeLayout.

J'espère que c'est utile.

Mahdi Astanei
la source
2

Premièrement : importez svg vers drawble: (( https://www.learn2crack.com/2016/02/android-studio-svg.html ))

1-Faites un clic droit sur le dossier dessinable sélectionnez Nouveau -> Vector Asset

entrez la description de l'image ici

2- Le Vector Asset Studio vous offre la possibilité de sélectionner les icônes de matériau intégrées ou votre propre fichier svg local. Vous pouvez remplacer la taille par défaut si nécessaire.

entrez la description de l'image ici

Deuxièmement : si vous voulez une prise en charge d'Android API 7+, vous devez utiliser la bibliothèque de support Android selon (( https://stackoverflow.com/a/44713221/1140304 ))

1- ajouter

vectorDrawables.useSupportLibrary = true

dans votre fichier build.gradle.

2-Utilisez l'espace de noms dans votre mise en page qui a imageView:

xmlns:app="http://schemas.android.com/apk/res-auto"

Troisièmement : définissez imageView avec android: scaleType = "fitXY" et utilisez app: srcCompat

 <ImageView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scaleType="fitXY"
        app:srcCompat="@drawable/ic_menu" />

Quatrièmement : si vous voulez définir svg dans le code java, vous devez ajouter la ligne ci-dessous sur oncreate methoed

 @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
Milad Ahmadi
la source
Votre solution fonctionne-t-elle sur l'API 21 et plus? user3210008 ci-dessus mentionne que l'implémentation n'est pas utilisée sur l'API 21 et plus.
AJW
2

Pour moi, la raison pour laquelle les vecteurs ont été convertis en png était l' android:fillType="evenodd"attribut dans mon dessin vectoriel. Lorsque j'ai supprimé toutes les occurrences de cet attribut dans mon dessin (par conséquent, le nonzerotype de remplissage par défaut appliqué au vecteur), la prochaine fois que l'application a été créée, tout allait bien.

Mahozad
la source