Warum ist meine skalierbare Vektorskalierung nicht wie erwartet?

97

Ich versuche, Vektor-Drawables in meiner Android-App zu verwenden. Von http://developer.android.com/training/material/drawables.html (Schwerpunkt Mine):

In Android 5.0 (API Level 21) und höher können Sie Vektorzeichnungen definieren, die skalieren, ohne die Definition zu verlieren.

Verwenden dieses Zeichens:

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

und diese ImageView:

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

erzeugt dieses verschwommene Bild, wenn versucht wird, das Symbol mit 400 dp anzuzeigen (auf einem großen hochauflösenden Mobilgerät von ca. 2015 mit Lollipop):

blurryBellIcon

Durch Ändern der Breite und Höhe in der Definition des zu zeichnenden Vektors auf 200 dp wird die Situation bei der gerenderten Größe von 400 dp erheblich verbessert. Wenn Sie dies jedoch als Zeichen für ein TextView-Element festlegen (dh ein Symbol links neben dem Text), wird jetzt ein großes Symbol erstellt.

Meine Fragen:

1) Warum gibt es im Vektor eine Breite / Höhe-Spezifikation? Ich dachte, der springende Punkt dabei ist, dass sie verlustfrei auf und ab skalieren, wodurch Breite und Höhe in ihrer Definition bedeutungslos werden.

2) Ist es möglich, einen einzelnen Vektor zu zeichnen, der als 24-dp-Zeichen in einer Textansicht funktioniert, sich aber gut skalieren lässt, um auch so viel größere Bilder zu verwenden? Wie vermeide ich beispielsweise das Erstellen mehrerer Vektorzeichnungen in verschiedenen Größen und verwende stattdessen eine, die meinen gerenderten Anforderungen entspricht?

3) Wie verwende ich die Attribute width / height effektiv und was ist der Unterschied zu viewportWidth / Height?

Weitere Details:

  • Auf dem Gerät wird API 22 ausgeführt
  • Verwenden von Android Studio v1.5.1 mit Gradle Version 1.5.0
  • Manifest ist Kompilieren und Ziellevel 23, Min Level 15. Ich habe auch versucht, Min Level auf 21 zu verschieben, aber das machte keinen Unterschied.
  • Beim Dekompilieren der APK (mit der Mindeststufe 21) wird eine einzelne XML-Ressource im Zeichenordner angezeigt. Es werden keine gerasterten Bilder erzeugt.
Chris Knight
quelle
Nur um das klar zu stellen. Sie verwenden Android Studio? Welche Version von Android Studio und welche Version des Gradle-Plugins? Wenn ich in Android Studio mit der rechten Maustaste auf den Zeichenordner klicke und ihn auswähle New -> Vector Asset, wird das Vektorbild-XML in meinem Zeichenordner abgelegt. Wenn ich jedoch apktool verwende, um das zu erstellende APK zu entpacken, sehe ich, dass die XML-Dateien drawable-anydpi-v21auf API 21+ -Geräten vorhanden sind und korrekt skaliert werden. Die Rasterdateien werden in den drawable-<mdpi/hdpi/etc>-v4Ordnern abgelegt und nicht auf API 21+ -Geräten verwendet (basierend auf der Tatsache, dass sie korrekt skaliert werden)
Cory Charlton
@ Cory Charlton. Neugierig. Ich verwende Studio 1.5.1 mit Gradle 1.5.0. Nach dem Ändern der Mindeststufe auf 21 ist der drawableOrdner der einzige Ort, an dem das Bild in der APK angezeigt wird . Die Breite / Höhe in der vom Vektor zeichnbaren XML-Datei beträgt 24 dp. Wenn Sie eine ImageView mit einer Höhe / Breite von 400 dp angeben, wird definitiv ein schlecht skaliertes Bild erstellt.
Chris Knight
Das würde ich erwarten. Der einzige Grund, warum ich mit dem ende, drawable-anydpi-v21ist, dass mein minSdkVersionWert kleiner als 21 ist. Gibt es eine Verhaltensänderung, wenn Sie den Wert minSdkVersionauf weniger als 21 setzen? Was ist mit dem Verschieben des XML drawable-anydpi? Ich würde nicht erwarten, dass sich etwas ändert, aber ich würde auch erwarten, dass Ihr Vektorbild korrekt skaliert wird ...
Cory Charlton
Wenn Sie die Min-Version wieder auf 15 ändern, erhalten Sie dieselben Ergebnisse wie Sie. Eine einzelne XML-Datei drawable-anydpi-v21mit verschiedenen gerasterten Bildern in mdi / hdpi / etc. Ordner. Keine Änderung des gerenderten Endergebnisses.
Chris Knight
Sehr seltsam ... Ich habe eine meiner Apps mit Ihrem Bild geändert und es funktioniert einwandfrei (siehe meine bearbeitete Antwort).
Cory Charlton

Antworten:

99

Hier gibt es neue Informationen zu diesem Problem:

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

Es sieht so aus, android:scaleType="fitXY"als würde es mit Lollipop korrekt skaliert.

Von einem Google-Ingenieur:

Hallo, lassen Sie mich wissen, ob scaleType = 'fitXY' eine Problemumgehung für Sie sein kann, damit das Bild scharf aussieht.

Der Marshmallow Vs Lollipop ist auf eine spezielle Skalierungsbehandlung zurückzuführen, die dem Marshmallow zugesetzt wurde.

Außerdem für Ihre Kommentare: "Richtiges Verhalten: Der zu zeichnende Vektor sollte ohne Qualitätsverlust skaliert werden. Wenn wir also dasselbe Asset in unserer Anwendung in drei verschiedenen Größen verwenden möchten, müssen wir vector_drawable.xml nicht dreimal mit verschiedenen duplizieren fest codierte Größen. "

Obwohl ich völlig damit einverstanden bin, dass dies der Fall sein sollte, hat die Android-Plattform in Wirklichkeit Leistungsprobleme, so dass wir die ideale Welt noch nicht erreicht haben. Es wird daher empfohlen, 3 verschiedene vector_drawable.xml zu verwenden, um eine bessere Leistung zu erzielen, wenn Sie sicher sind, dass Sie 3 verschiedene Größen gleichzeitig auf dem Bildschirm zeichnen möchten.

Das technische Detail ist im Grunde, dass wir eine Bitmap unter dem Hook verwenden, um das komplexe Pfad-Rendering zwischenzuspeichern, sodass wir die beste Neuzeichnungsleistung erzielen können, die mit dem Neuzeichnen einer Bitmap-Zeichnung vergleichbar ist.

Taehyun Park
quelle
26
Okay, Google, es ist absolut schrecklich, dass wir identische Drawables mit denselben Ansichtsfenstern und Pfaden kopieren und einfügen müssen, die nur unterschiedliche Größen haben.
Miha_x64
Dadurch wurde das Problem behoben, bei dem mein Logo pixelig angezeigt wurde. Bei dem anderen Gerät, bei dem zuvor alles in Ordnung war, ist jetzt das falsche Seitenverhältnis falsch. Ich verstehe nicht, warum das ein Problem ist, es ist ein Vektor! Keine Pixel! : P
tplive
@ Harish Gyanani, android: scaleType = "fitXY" löst das Problem, aber was ist mit dynamischen und nicht quadratischen Symbolen?
Manuela
28

1) Warum gibt es im Vektor eine Breite / Höhe-Spezifikation? Ich dachte, der springende Punkt dabei ist, dass sie verlustfrei auf und ab skalieren, wodurch Breite und Höhe in ihrer Definition bedeutungslos werden.

Für SDK-Versionen unter 21, bei denen das Build-System Raster-Images generieren muss, und als Standardgröße, wenn Sie die Breite / Höhe nicht angeben.

2) Ist es möglich, einen einzelnen Vektor zu verwenden, der als 24-dp-Zeichnung in einer Textansicht sowie als großes Bild in Bildschirmnähe verwendet werden kann?

Ich glaube nicht, dass dies möglich ist, wenn Sie auch SDKs unter 21 anvisieren müssen.

3) Wie verwende ich die Attribute width / height effektiv und was ist der Unterschied zu viewportWidth / Height?

Dokumentation: (eigentlich nicht sehr nützlich, jetzt wo ich es wieder lese ...)

android:width

Wird verwendet, um die Eigenbreite des Zeichens zu definieren. Dies unterstützt alle Maßeinheiten, die normalerweise mit dp angegeben werden.

android:height

Wird verwendet, um die Eigenhöhe des Zeichens zu definieren. Dies unterstützt alle Maßeinheiten, die normalerweise mit dp angegeben werden.

android:viewportWidth

Wird verwendet, um die Breite des Ansichtsfensterbereichs zu definieren. Das Ansichtsfenster ist im Grunde die virtuelle Leinwand, auf der die Pfade gezeichnet werden.

android:viewportHeight

Wird verwendet, um die Höhe des Ansichtsfensterbereichs zu definieren. Das Ansichtsfenster ist im Grunde die virtuelle Leinwand, auf der die Pfade gezeichnet werden.

Weitere Dokumentation:

Android 4.4 (API Level 20) und niedriger unterstützt keine Vektor-Drawables. Wenn Ihre Mindest-API-Ebene auf eine dieser API-Ebenen festgelegt ist, weist Vector Asset Studio Gradle außerdem an, Rasterbilder des Vektors zu generieren, die aus Gründen der Abwärtskompatibilität gezeichnet werden können. Sie können auf Vektor-Assets wie Drawableim Java-Code oder @drawableim XML-Code verweisen . Wenn Ihre App ausgeführt wird, wird das entsprechende Vektor- oder Rasterbild je nach API-Ebene automatisch angezeigt.


Edit: Etwas Seltsames ist los. Hier sind meine Ergebnisse im Emulator SDK Version 23 (Lollipop + Testgerät ist momentan tot ...):

Gute Glocken am 6.x.

Und im Emulator SDK Version 21:

Crappy Glocken auf 5.x.

Cory Charlton
quelle
1
Danke Cory, ich hatte diese Dokumentation gesehen und fand sie auch nicht sehr nützlich. Mein Gerät ist Lollipop (v22), und dennoch kann ich die Grafik nicht gut skalieren lassen, über den 24dp, die in der Vektorzeichnung angegeben sind, unabhängig davon, ob die Höhe / das Gewicht in der ImageView genau angegeben sind oder nicht. Ich habe ein Manifest mit Ziel- und Kompilierungsstufe 23 und Mindeststufe 21 getestet, aber es skaliert meinen Vektor nicht reibungslos, ohne dass ich die Breite / Höhe im Zeichen selbst ändere. Ich möchte nicht wirklich mehrere Drawables erstellen, deren einziger Unterschied Höhe / Breite ist!
Chris Knight
1
Vielen Dank für die Bestätigung und bedanken uns für Ihre Hilfe. Wie Sie gezeigt haben, sollte es eindeutig so funktionieren, wie ich es erwartet habe. Die Verwendung Ihres genauen Codes führt zu demselben Ergebnis wie ursprünglich. Frustrierend!
Chris Knight
1
UPDATE: Ich habe meinen Code mit API 22 gegen den Emulator ausgeführt und erhalte immer noch das gleiche Ergebnis. Lief meinen Code gegen Emulator mit API 23 und, tada!, Es funktioniert. Es sieht so aus, als ob die Hochskalierung nur auf API 23+ -Geräten funktioniert. Es wurde versucht, die SDK-Zielversion zu ändern, aber das machte keinen Unterschied.
Chris Knight
Haben Sie eine Lösung für dieses Problem gefunden?
dazza5000
@corycharlton ist es okay zu entfernen width height viewport heightund widthvon xml?
VINNUSAURUS
9

AppCompat 23.2 führt Vektor-Drawables für alle Geräte mit Android 2.1 und höher ein. Die Bilder werden unabhängig von der in der XML-Datei des Vektorzeichens angegebenen Breite / Höhe korrekt skaliert. Leider wird diese Implementierung in API Level 21 und höher nicht verwendet (zugunsten der nativen Implementierung).

Die gute Nachricht ist, dass wir AppCompat zwingen können, seine Implementierung auf den API-Ebenen 21 und 22 zu verwenden. Die schlechte Nachricht ist, dass wir Reflexion verwenden müssen, um dies zu tun.

Stellen Sie zunächst sicher, dass Sie die neueste Version von AppCompat verwenden und die neue Vektorimplementierung aktiviert haben:

android {
  defaultConfig {
    vectorDrawables.useSupportLibrary = true
  }
}

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

Rufen useCompatVectorIfNeeded();Sie dann onCreate () Ihrer Anwendung auf:

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

Stellen Sie schließlich sicher, dass Sie das Image Ihrer ImageView verwenden, app:srcCompatanstatt android:srces festzulegen:

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

Ihre Vektorzeichnungen sollten jetzt korrekt skaliert werden!

user3210008
quelle
Guter Ansatz, aber AppCompat 25.4 (und möglicherweise früher) gibt einen Flusenfehler aus "kann nur von derselben Bibliotheksgruppe aufgerufen werden". Die Build-Tools, die ich verwende, lassen es immer noch erstellen, aber ich bin mir nicht sicher, ob es Laufzeitfolgen gibt. Über zu testen.
AndroidGy
kann nur von derselben Bibliotheksgruppe aufgerufen werden. Entfernen Sie diesen Fehler, indem Sie Folgendes hinzufügen: lintOptions {disable 'RestrictedApi'}
Usama Saeed US
6

1) Warum gibt es im Vektor eine Breite / Höhe-Spezifikation? Ich dachte, der springende Punkt dabei ist, dass sie verlustfrei auf und ab skalieren, wodurch Breite und Höhe in ihrer Definition bedeutungslos werden.

Dies ist nur die Standardgröße des Vektors, falls Sie ihn nicht in der Layoutansicht definieren. (dh Sie verwenden Wrap-Inhalte für die Höhe und Breite Ihrer Bildansicht.)

2) Ist es möglich, einen einzelnen Vektor zu verwenden, der als 24-dp-Zeichnung in einer Textansicht sowie als großes Bild in Bildschirmnähe verwendet werden kann?

Ja, es ist möglich und ich hatte keine Probleme mit der Größenänderung, solange das laufende Gerät Lollipop oder höher verwendet. In früheren APIs wird der Vektor beim Erstellen der App für verschiedene Bildschirmgrößen in PNGs konvertiert.

3) Wie verwende ich die Attribute width / height effektiv und was ist der Unterschied zu viewportWidth / Height?

Dies wirkt sich darauf aus, wie der Raum um Ihren Vektor verwendet wird. dh Sie können es verwenden, um die "Schwerkraft" des Vektors innerhalb des Ansichtsfensters zu ändern, den Vektor mit einem Standardrand zu versehen, bestimmte Teile des Vektors aus dem Ansichtsfenster herauszulassen usw. Normalerweise setzen Sie sie einfach auf den gleichen Wert Größe.

Emirua
quelle
Danke Emiura. Mich würde interessieren, wie Sie mehrere gerenderte Größen mit demselben Zeichenelement erstellt haben. Mit einem 24-dp-Zeichenobjekt (zu Testzwecken) habe ich mein ImageView so geändert, dass es eine bestimmte Breite und Höhe von 400 dp hat und auf meinem Lollipop-Gerät (v22) ausgeführt wird. Es wird jedoch immer noch schlecht gerendert, wie ein gerastertes, hochskaliertes Bild anstelle eines Vektorbilds. Außerdem wurde mein Manifest so geändert, dass es Ziel und Kompilierung für Version 23 und Min Version 21 hat. Kein Unterschied.
Chris Knight
In diesem Fall müssten Sie nur die größte Größe in Ihrem Vektor zeichnen und dann die 24-dp-Größe in Ihrer Textansicht angeben.
Emirua
Ich sehe jetzt, dass Sie verschwommene Bilder erhalten, wenn Sie mit sehr großen Unterschieden zwischen der Größe des zu zeichnenden Vektors und der Größe des Layouts in Lollipop vergrößern. Es ist jedoch immer noch viel ordentlicher, nur die größte Größe einzustellen, anstatt eine Reihe von Dateien für verschiedene Bildschirmgrößen zu haben;).
Emirua
4

Ich löse mein Problem, ändere einfach die Größe des Vektorbildes. Von Anfang an war es eine Ressource wie:

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

Und ich habe es in 150dp (Größe von ImageView im Layout mit dieser Vektorressource) geändert wie:

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

Es funktioniert für mich.

Djek-Grif
quelle
Vielen Dank, dies reicht aus, um das Problem auf meinem Tablet zu lösen, bei dem es pixelig erschien.
user2342558
3

Ich versuche es auf diese Weise, aber leider hat keiner von ihnen funktioniert. Ich versuche , fitXYin ImageView, versuche ich mit , app:srcCompataber kann es nicht einmal verwenden. aber ich habe einen Trick gefunden:

Setze das Drawable auf backgrounddein ImageView.

Der Sinn dieses Weges sind jedoch die Dimensionen von Ansichten. Wenn Sie ImageViewfalsche Abmessungen haben, erhält Drawable Stretch. Sie müssen es in Pre-Lollipop-Versionen steuern oder einige Ansichten verwendenPercentRelativeLayout .

Hoffe es ist hilfreich.

Mahdi Astanei
quelle
2

Erstens : Importieren Sie svg in drawble: (( https://www.learn2crack.com/2016/02/android-studio-svg.html ))

1-Klicken Sie mit der rechten Maustaste auf den Zeichenordner und wählen Sie Neu -> Vektor-Asset

Geben Sie hier die Bildbeschreibung ein

2- In Vector Asset Studio können Sie die Symbole für eingebautes Material oder Ihre eigene lokale SVG-Datei auswählen. Sie können die Standardgröße bei Bedarf überschreiben.

Geben Sie hier die Bildbeschreibung ein

Zweitens : Wenn Sie Unterstützung von Android API 7+ wünschen, müssen Sie die Aufzeichnung der Android Support Library verwenden (( https://stackoverflow.com/a/44713221/1140304 )).

1- hinzufügen

vectorDrawables.useSupportLibrary = true

in Ihre build.gradle-Datei.

2-Verwenden Sie einen Namespace in Ihrem Layout mit imageView:

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

Drittens : Setzen Sie imageView mit android: scaleType = "fitXY" und verwenden Sie app: srcCompat

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

Viertens : Wenn Sie svg in Java-Code setzen möchten, müssen Sie bei oncreate methoed die folgende Zeile hinzufügen

 @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
Milad Ahmadi
quelle
Funktioniert Ihre Lösung mit API 21 und höher? user3210008 oben erwähnt, dass die Implementierung unter API 21 und höher nicht verwendet wird.
AJW
2

Für mich war der Grund, warum Vektoren in png konvertiert wurden, ein android:fillType="evenodd"Attribut in meinem Vektor, das gezeichnet werden konnte. Als ich alle Vorkommen dieses Attributs in meinem Drawable entfernte (daher der Standardfüllungstyp nonzero, der auf den Vektor angewendet wurde), war beim nächsten Erstellen der App alles in Ordnung.

Mahozad
quelle