Perché il mio ridimensionamento disegnabile vettoriale non viene ridimensionato come previsto?


97

Sto tentando di utilizzare i disegnabili vettoriali nella mia app Android. Da http://developer.android.com/training/material/drawables.html (enfasi mia):

In Android 5.0 (livello API 21) e versioni successive, è possibile definire i disegnabili vettoriali, che scalano senza perdere la definizione.

Usando questo disegnabile:

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

e questo ImageView:

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

produce questa immagine sfocata quando si tenta di visualizzare l'icona a 400 dpi (su un dispositivo mobile ad alta risoluzione di circa 2015 che esegue lecca-lecca):

blurryBellIcon

Modificare la larghezza e l'altezza nella definizione del vettore disegnabile a 200dp migliora significativamente la situazione alla dimensione renderizzata di 400dp. Tuttavia, impostandolo come disegnabile per un elemento TextView (cioè un'icona a sinistra del testo) ora crea un'icona enorme.

Le mie domande:

1) Perché c'è una specifica di larghezza / altezza nel disegno vettoriale? Ho pensato che il punto centrale di questi è che si scalano su e giù senza perdite rendendo la larghezza e l'altezza prive di significato nella sua definizione?

2) È possibile utilizzare un singolo disegnabile vettoriale che funziona come un disegnabile 24dp su TextView ma si ridimensiona bene per utilizzare anche immagini molto più grandi? Ad esempio, come posso evitare di creare più drawable vettoriali di dimensioni diverse e invece utilizzarne uno che si adatta alle mie esigenze di rendering?

3) Come faccio a utilizzare efficacemente gli attributi larghezza / altezza e qual è la differenza con viewportWidth / Height?

Dettagli aggiuntivi:

  • Il dispositivo esegue l'API 22
  • Utilizzo di Android Studio v1.5.1 con Gradle versione 1.5.0
  • Manifest è compilato e target livello 23, livello minimo 15. Ho anche provato a spostare il livello minimo a 21, ma questo non ha fatto differenza.
  • La decompilazione dell'APK (con il livello minimo impostato su 21) mostra una singola risorsa XML nella cartella disegnabile. Non vengono prodotte immagini rasterizzate.

Giusto per essere chiari. Stai usando Android Studio? Quale versione di Android Studio e quale versione del plugin Gradle? Quando faccio clic con il pulsante destro del mouse sulla cartella disegnabile in Android Studio e lo scelgo New -> Vector Asset, rilascia l'XML dell'immagine vettoriale nella mia cartella disegnabile. Tuttavia, se utilizzo apktool per decomprimere l'APK che viene creato, vedo che i file XML sono presenti drawable-anydpi-v21e scalano correttamente sui dispositivi API 21+. I file raster vengono inseriti nelle drawable-<mdpi/hdpi/etc>-v4cartelle e non utilizzati su dispositivi API 21+ (in base al fatto che vengono ridimensionati correttamente)
Cory Charlton

@Cory Charlton. Curioso. Sto usando Studio 1.5.1 con Gradle 1.5.0. Dopo aver cambiato il livello minimo a 21, l'unico punto in cui l'immagine appare nell'APK è la drawablecartella. La larghezza / altezza nel file xml disegnabile vettoriale è 24dp. Specificare un ImageView di 400dp di altezza / larghezza sta sicuramente creando un'immagine scarsamente ridimensionata.
Chris Knight

Questo è quello che mi aspetterei. L'unico motivo per cui mi ritrovo con il drawable-anydpi-v21è perché il mio minSdkVersionè inferiore a 21. Qualche cambiamento nel comportamento se imposti il minSdkVersiona meno di 21? Che ne dici di spostare l'XML in drawable-anydpi? Non mi aspetto che ci sia un cambiamento, ma mi aspetto anche che la tua immagine vettoriale venga ridimensionata correttamente ...
Cory Charlton

Riportare la versione minima a 15 produce gli stessi risultati. Un singolo file xml drawable-anydpi-v21con varie immagini rasterizzate in mdi / hdpi / ecc. cartelle. Nessuna modifica al risultato finale reso però.
Chris Knight

Molto strano ... Ho modificato una delle mie app usando la tua immagine e funziona bene (vedi la mia risposta modificata)
Cory Charlton

Risposte:


100

Ci sono nuove informazioni su questo problema qui:

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

Sembra usare android:scaleType="fitXY" lo ridimensionerà correttamente su Lollipop.

Da un ingegnere di Google:

Ciao, fammi sapere se scaleType = 'fitXY' può essere una soluzione alternativa per te, al fine di ottenere un'immagine nitida.

Il marshmallow Vs Lollipop è dovuto a uno speciale trattamento di ridimensionamento aggiunto nel marshmallow.

Inoltre, per i tuoi commenti: "Comportamento corretto: il disegno vettoriale dovrebbe ridimensionarsi senza perdita di qualità. Quindi, se vogliamo utilizzare lo stesso asset in 3 diverse dimensioni nella nostra applicazione, non dobbiamo duplicare vector_drawable.xml 3 volte con differenti dimensioni hardcoded. "

Anche se sono totalmente d'accordo che dovrebbe essere così, in realtà, la piattaforma Android ha problemi di prestazioni tali che non abbiamo ancora raggiunto il mondo ideale. Quindi si consiglia effettivamente di utilizzare 3 diversi vector_drawable.xml per prestazioni migliori se si è sicuri di voler disegnare contemporaneamente 3 diverse dimensioni sullo schermo.

Il dettaglio tecnico è fondamentalmente che stiamo usando una bitmap sotto l'hook per memorizzare nella cache il rendering del percorso complesso, in modo tale da ottenere le migliori prestazioni di ridisegno, alla pari con il ridisegno di una bitmap disegnabile.


27
Va bene Google, è assolutamente terribile che dobbiamo copiare e incollare disegnabili identici con le stesse finestre e percorsi che hanno solo dimensioni diverse.
Miha_x64

Questo ha risolto il problema sul dispositivo che mostrava il mio logo pixelato, ma sull'altra unità, dove prima andava tutto bene, ora le proporzioni sono sbagliate. Non capisco perché questo sia un problema, è un vettore! Non pixel! : P
tplive

@ Harish Gyanani, android: scaleType = "fitXY" risolve il problema, ma per quanto riguarda le icone dinamiche e non quadrate?
Manuela

28

1) Perché c'è una specifica di larghezza / altezza nel disegno vettoriale? Ho pensato che il punto centrale di questi è che si scalano su e giù senza perdite rendendo la larghezza e l'altezza prive di significato nella sua definizione?

Per le versioni SDK inferiori alla 21 in cui il sistema di compilazione deve generare immagini raster e come dimensione predefinita nei casi in cui non si specifica la larghezza / altezza.

2) È possibile utilizzare un unico disegno vettoriale che funziona come un disegnabile 24dp su un TextView così come una grande immagine di larghezza vicino allo schermo?

Non credo che questo sia possibile se devi scegliere come target anche SDK inferiori a 21.

3) Come faccio a utilizzare efficacemente gli attributi larghezza / altezza e qual è la differenza con viewportWidth / Height?

Documentazione: (in realtà non molto utile ora che l'ho riletto ...)

android:width

Utilizzato per definire la larghezza intrinseca del disegnabile. Questo supporta tutte le unità di misura, normalmente specificate con dp.

android:height

Utilizzato per definire l'altezza intrinseca del disegnabile. Questo supporta tutte le unità di misura, normalmente specificate con dp.

android:viewportWidth

Utilizzato per definire la larghezza dello spazio della finestra. Viewport è fondamentalmente la tela virtuale su cui vengono disegnati i percorsi.

android:viewportHeight

Utilizzato per definire l'altezza dello spazio della finestra. Viewport è fondamentalmente la tela virtuale su cui vengono disegnati i percorsi.

Ulteriore documentazione:

Android 4.4 (livello API 20) e versioni precedenti non supportano i drawable vettoriali. Se il tuo livello API minimo è impostato su uno di questi livelli API, Vector Asset Studio indica anche a Gradle di generare immagini raster del vettore disegnabili per compatibilità con le versioni precedenti. Puoi fare riferimento ad asset vettoriali come Drawablenel codice Java o @drawablenel codice XML; quando l'app viene eseguita, l'immagine vettoriale o raster corrispondente viene visualizzata automaticamente a seconda del livello di API.


Modifica: sta succedendo qualcosa di strano. Ecco i miei risultati nell'emulatore SDK versione 23 (il dispositivo di test Lollipop + è morto in questo momento ...):

Buone campane su 6.x

E nell'emulatore SDK versione 21:

Campane schifose su 5.x


1
Grazie Cory, avevo visto quella documentazione e non l'ho trovata molto utile. Il mio dispositivo è Lollipop (v22) e tuttavia non riesco a ridimensionare bene la grafica, al di sopra del 24dp specificato nel disegno vettoriale, indipendentemente dal fatto che l'altezza / peso siano esattamente specificati in ImageView o meno. Ho testato un manifest con target e compilato il livello 23 e il livello minimo 21, ma non aumenterà in modo uniforme il mio disegnabile vettoriale senza che io modifichi la larghezza / altezza nel disegnabile stesso. Non voglio davvero creare più drawable la cui unica differenza è altezza / larghezza!
Chris Knight

1
Grazie per la conferma e apprezzo molto il tuo aiuto. Come hai dimostrato, chiaramente dovrebbe funzionare come mi aspetto. L'utilizzo del codice esatto produce lo stesso risultato che avevo originariamente. Frustrante!
Chris Knight

1
AGGIORNAMENTO: ho eseguito il mio codice contro l'emulatore con API 22 e ottengo ancora lo stesso risultato. Ho eseguito il mio codice contro l'emulatore con API 23 e, tada !, funziona. Sembra che l'upscaling funzioni solo su dispositivi API 23+. Ho provato a cambiare la versione SDK di destinazione ma non ha fatto differenza.
Chris Knight

Hai trovato una soluzione a questo problema?
dazza5000

@corycharlton va bene rimuovere width height viewport heighte widthda xml?
VINNUSAURUS

9

AppCompat 23.2 introduce i drawable vettoriali per tutti i dispositivi con Android 2.1 e versioni successive. Le immagini vengono ridimensionate correttamente, indipendentemente dalla larghezza / altezza specificate nel file XML del disegno vettoriale. Sfortunatamente, questa implementazione non viene utilizzata nel livello API 21 e superiore (a favore dell'implementazione nativa).

La buona notizia è che possiamo forzare AppCompat a utilizzare la sua implementazione sui livelli API 21 e 22. La cattiva notizia è che dobbiamo usare la riflessione per farlo.

Prima di tutto, assicurati di utilizzare l'ultima versione di AppCompat e di aver abilitato la nuova implementazione vettoriale:

android {
  defaultConfig {
    vectorDrawables.useSupportLibrary = true
  }
}

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

Quindi, chiama useCompatVectorIfNeeded();onCreate () della tua applicazione:

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

Infine, assicurati di utilizzare app:srcCompatinvece di android:srcimpostare l'immagine di ImageView:

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

I tuoi disegnabili vettoriali ora dovrebbero ridimensionarsi correttamente!


Buon approccio, ma AppCompat 25.4 (e forse precedente) restituisce un errore di lanugine "può essere chiamato solo dallo stesso gruppo di librerie". Gli strumenti di compilazione che sto utilizzando lo consentono ancora di compilare, ma non sono sicuro che ci siano conseguenze in fase di esecuzione. Sto per testare.
androidguy

può essere chiamato solo dallo stesso gruppo di librerie, rimuovere questo errore aggiungendo questo: lintOptions {disable 'RestrictedApi'}
Usama Saeed US

6

1) Perché c'è una specifica di larghezza / altezza nel disegno vettoriale? Ho pensato che il punto centrale di questi è che si scalano su e giù senza perdite rendendo la larghezza e l'altezza prive di significato nella sua definizione?

Questa è solo la dimensione predefinita del vettore nel caso in cui non la definisci nella vista layout. (ad esempio, utilizzi il contenuto a capo per l'altezza e la larghezza della visualizzazione dell'immagine)

2) È possibile utilizzare un unico disegno vettoriale che funziona come un disegnabile 24dp su un TextView così come una grande immagine di larghezza vicino allo schermo?

Sì, è possibile e non ho avuto alcun problema con il ridimensionamento fintanto che il dispositivo in esecuzione utilizza lollipop o superiore. Nelle API precedenti, il vettore viene convertito in png per schermi di dimensioni diverse quando si crea l'app.

3) Come faccio a utilizzare efficacemente gli attributi larghezza / altezza e qual è la differenza con viewportWidth / Height?

Ciò influisce sul modo in cui viene utilizzato lo spazio intorno al vettore. cioè Puoi usarlo per cambiare la "gravità" del vettore all'interno della finestra, fare in modo che il vettore abbia un margine predefinito, lasciare alcune parti del vettore fuori dalla finestra, ecc ... Normalmente, le imposti allo stesso modo taglia.


Grazie Emiura. Sarei interessato a come hai ottenuto più dimensioni di rendering utilizzando lo stesso disegnabile. Usando un drawable 24dp (a scopo di test) ho cambiato il mio ImageView per avere una larghezza e un'altezza specificate di 400dp ed eseguito sul mio dispositivo Lollipop (v22), ma il rendering è ancora scadente, come un'immagine rasterizzata con upscaling piuttosto che un'immagine vettoriale. Anche cambiato il mio manifest per avere destinazione e compilare contro v23 e min versione 21. Nessuna differenza.
Chris Knight

In questo caso è necessario utilizzare solo la dimensione massima nel disegno vettoriale e quindi specificare la dimensione 24dp nella visualizzazione del testo.
emirua

Vedo ora che ottieni immagini sfocate quando ingrandisci con differenze molto grandi tra la dimensione nel disegno vettoriale e la dimensione nel layout in Lollipop. Tuttavia, è ancora molto più semplice impostare la dimensione massima piuttosto che avere un mucchio di file per schermi di dimensioni diverse;).
emirua

4

Risolvo il mio problema basta cambiare la dimensione dell'immagine vettoriale. Fin dall'inizio è stata una risorsa come:

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

E l'ho cambiato in 150dp (dimensione di ImageView nel layout con questa risorsa vettoriale) come:

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

Per me sta funzionando.


grazie, questo è sufficiente per risolvere il problema sul mio tablet dove appariva pixelato.
user2342558

3

Provo in questi modi, ma sfortunatamente nessuno di loro ha funzionato. Provo fitXYa ImageView, cerco di utilizzare app:srcCompat, ma non posso nemmeno usarlo. ma ho trovato un modo complicato:

imposta il Drawable su backgrounddel tuo ImageView.

Ma il punto di questo modo sono le dimensioni delle viste. se il tuo ImageViewha dimensioni errate, drawable diventa Stretch. è necessario controllarlo nelle versioni pre-lecca-lecca o utilizzare alcune visualizzazioni PercentRelativeLayout.

Spero sia utile.


2

Primo : importa svg in drawble: (( https://www.learn2crack.com/2016/02/android-studio-svg.html ))

1-Fare clic con il pulsante destro del mouse sulla cartella disegnabile selezionare Nuovo -> Asset vettoriale

inserisci qui la descrizione dell'immagine

2- Vector Asset Studio fornisce l'opzione per selezionare le icone dei materiali incorporate o il tuo file svg locale. Se necessario, puoi sostituire la dimensione predefinita.

inserisci qui la descrizione dell'immagine

Secondo : se desideri supporto dall'API Android 7+, devi utilizzare la libreria di supporto Android acording (( https://stackoverflow.com/a/44713221/1140304 ))

1- aggiungi

vectorDrawables.useSupportLibrary = true

al tuo file build.gradle.

2-Usa lo spazio dei nomi nel tuo layout che ha imageView:

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

Terzo : imposta imageView con android: scaleType = "fitXY" e usa app: srcCompat

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

Quarto : se vuoi impostare svg in codice java devi aggiungere sotto la riga su oncreate methoed

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

La tua soluzione funziona su API 21 e versioni successive? user3210008 sopra menziona che l'implementazione non viene utilizzata su API 21 e successive.
AJW

2

Per me il motivo per cui i vettori venivano convertiti in png era l' android:fillType="evenodd"attributo nel mio disegnabile vettoriale. Quando ho rimosso tutte le occorrenze di questo attributo nel mio drawable (quindi il nonzerotipo di riempimento predefinito applicato al vettore), la prossima volta che l'app è stata creata, tutto è andato bene.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.