Używanie Android Vector Drawables w przypadku awarii przed awarią Lollipop

95

Używam rysunków wektorowych w systemie Android przed Lollipopem i są to niektóre z moich bibliotek i wersji narzędzi:

  • Android Studio: 2.0.0
  • Wtyczka Android Gradle: 2.0.0
  • Narzędzia do budowania: 23.0.2
  • Biblioteka obsługi Androida: 23.3.0

Dodałem tę właściwość na poziomie mojej aplikacji Build.Gradle

android {  
  defaultConfig {  
    vectorDrawables.useSupportLibrary = true  
   }  
}

Warto również wspomnieć, że używam dodatkowego elementu do rysowania, takiego jak LayerDrawable (lista_warstw), jak podano na oficjalnym blogu Androida ( link tutaj ), aby ustawić rysunki dla rysunków wektorowych pozaapp:srcCompat

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

Znajdziesz bezpośrednie odniesienia do rysunków wektorowych poza aplikacją: srcCompat zawiedzie przed Lollipopem. Jednak AppCompat obsługuje ładowanie wektorów rysunkowych, gdy są przywoływane w innym kontenerze do rysowania, takim jak StateListDrawable, InsetDrawable, LayerDrawable, LevelListDrawable i RotateDrawable. Korzystając z tego pośrednictwa , można używać wektorów drawables w przypadkach takich jak atrybut android: drawableLeft w TextView, który normalnie nie byłby w stanie obsługiwać wektorów drawables.

Kiedy używam, app:srcCompatwszystko działa dobrze, ale kiedy używam:

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

na ImageView, ImageButton, TextViewlub EditTextprzed Lollipop zgłasza expection:

Caused by: android.content.res.Resources$NotFoundException: File res/drawable/search_toggle.xml from drawable resource ID #0x7f0200a9
Behzad Bahmanyar
źródło
Możliwy duplikat kompatybilności wektorów Androida
Adam Hurwitz,
Aby zobaczyć, jak używać VectorDrawable z drawableLeft, drawableRight, drawableTop, drawableDół sprawdź Ta odpowiedź
Behzad Bahmanyar
Możesz sprawdzić moją odpowiedź tutaj? stackoverflow.com/a/40523623/2557258
Yazon2006
W moim przypadku, vactor drawables nie były w odpowiednim pakiecie (otwarty widok projektu) oryginalna odpowiedź tutaj stackoverflow.com/a/35836318/2163045
murt

Odpowiedzi:

104

NAJNOWSZA AKTUALIZACJA - czerwiec / 2019

Biblioteka wsparcia zmieniła się nieco od czasu oryginalnej odpowiedzi. Teraz nawet wtyczka Android dla Gradle jest w stanie automatycznie generować PNG w czasie kompilacji. Tak więc poniżej są dwa nowe podejścia, które powinny działać w dzisiejszych czasach. Więcej informacji znajdziesz tutaj:

Generacja PNG

Gradle może automatycznie tworzyć obrazy PNG z Twoich zasobów podczas kompilacji. Jednak w tym podejściu nie wszystkie elementy XML są obsługiwane . To rozwiązanie jest wygodne, ponieważ nie musisz niczego zmieniać w kodzie ani w pliku build.gradle. Upewnij się tylko, że używasz wtyczki Android w wersji 1.5.0 lub nowszej i Android Studio 2.2 lub nowszej .

Używam tego rozwiązania w mojej aplikacji i działa dobrze. Nie jest wymagana dodatkowa flaga build.gradle . Żadne hacki nie są potrzebne. Jeśli przejdziesz do / build / generated / res / pngs / ... możesz zobaczyć wszystkie wygenerowane pliki PNG.

Tak więc, jeśli masz jakąś prostą ikonę (ponieważ nie wszystkie elementy xml są obsługiwane), to rozwiązanie może działać dla Ciebie. Po prostu zaktualizuj swoje Android Studio i wtyczkę Android dla Gradle.

Biblioteka wsparcia

Zapewne jest to rozwiązanie, które Ci się spodoba. Jeśli tu jesteś, oznacza to, że Twoje Android Studio nie generuje plików PNG automatycznie. Twoja aplikacja ulega awarii.

A może nie chcesz, aby Android Studio w ogóle generował pliki PNG.

W odróżnieniu od „automatycznego generowania PNG”, które obsługuje podzbiór elementów XML, to rozwiązanie obsługuje wszystkie znaczniki XML. Więc masz pełne wsparcie dla swojego wektora do rysowania.

Najpierw musisz zaktualizować plik build.gradle, aby go obsługiwał:

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
}

A następnie użyj app:srcCompatzamiast android:srcpodczas ładowania VectorDrawables. Nie zapomnij o tym.

Dla TextViewjeśli używasz androidxwersji biblioteki wsparcia, można użyć app:drawableLeftCompat(lub w prawo, góra, dół) zamiastapp:drawableLeft

W przypadku CheckBox/ RadioButton, użyj app:buttonCompatzamiast android:button.

Jeśli nie używasz androidxwersji Support Library, a Twoja wersja minSdkVersionjest 17lub nowsza lub używasz przycisku, możesz spróbować ustawić programowo za pomocą

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

AKTUALIZACJA - lipiec / 2016

Ponownie włączyli VectorDrawable w
bibliotece obsługi systemu Android 23.4.0

Dla użytkowników AppCompat dodaliśmy API opt-in, aby ponownie włączyć obsługę Vector Drawables z zasobów (zachowanie znalezione w 23.2) za pośrednictwem AppCompatDelegate.setCompatVectorFromResourcesEnabled (true) - pamiętaj, że nadal może to powodować problemy z wykorzystaniem pamięci i problemy z aktualizacją instancji konfiguracji, dlatego jest ona domyślnie wyłączona.

Być może , build.gradleustawienie jest teraz przestarzała i po prostu trzeba ją włączyć w odpowiednich działań (jednak trzeba przetestować).

Teraz, aby to włączyć, musisz:

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

    ...
}

Oryginalna odpowiedź - kwiecień 2016 r

Myślę, że dzieje się tak, ponieważ Support Vector został wyłączony w najnowszej wersji biblioteki: 23.3.0

Według tego POST :

W przypadku użytkowników AppCompat zdecydowaliśmy się usunąć funkcjonalność, która pozwala na używanie elementów rysunkowych wektorowych z zasobów na urządzeniach sprzed wersji Lollipop z powodu problemów występujących w implementacji w wersji 23.2.0 / 23.2.1 (ISSUE 205236) . Korzystanie z aplikacji: srcCompat i setImageResource () nadal działa.

Jeśli odwiedzisz numer ISSUE 205236 , wydaje się, że w przyszłości się włączą , ale problem z pamięcią nie zostanie wkrótce rozwiązany:

W następnym wydaniu dodałem opt-in API, w którym można ponownie włączyć obsługę VectorDrawable, która została usunięta. Ma jednak te same zastrzeżenia, co poprzednio (zużycie pamięci i problemy z aktualizacją konfiguracji).

Miałem podobny problem. Tak więc w moim przypadku ponownie przywróciłem wszystkie ikony, które używają wektorów, które można narysować z zasobów, do obrazów PNG (ponieważ problem z pamięcią będzie się powtarzał nawet po udostępnieniu opcji ponownego włączenia).

Nie jestem pewien, czy to najlepsza opcja, ale moim zdaniem naprawia wszystkie awarie.

W0rmH0le
źródło
1
Dzięki @Guilherme P., ale dlaczego nie usunąłeś vectorDrawables.useSupportLibrary = trueopcji umożliwiającej ponowne włączenie generowania plików PNG w czasie kompilacji ?
Behzad Bahmanyar
1
Zostało to wyłączone w najnowszej wersji 23.3.0 z powodu problemów z pamięcią. W ten sposób nie mogą generować png w czasie wykonywania ... Dlatego w błędzie logcat wypisują: nieznany wektor znacznika (lub coś w tym rodzaju).
W0rmH0le
4
Oto instrukcje dotyczące akceptacji plus.google.com/+AndroidDevelopers/posts/B7QhFkWZ6YX . Niestety VectorDrawables nadal nie działają na urządzeniu Pre-Lollipop. Jestem na Support Library 23.4.0 i mam zarówno „generatedDensities = []”, jak i „vectorDrawables.useSupportLibrary = true” o nazwie w defaultConfig {}.
Adam Hurwitz,
1
@AdamHurwitz Zaktualizowałem odpowiedź .. Wygląda na to, że została ponownie włączona .. Jednak teraz musisz ją włączyć inaczej.
W0rmH0le
1
@juztcode Masz rację. Powinieneś użyć „androidx.appcompat: appcompat: 1.1.0”
W0rmH0le
63

Miałem ten sam problem. Ale robiąc dużo badań i rozwoju, otrzymałem odpowiedź.

W przypadku użycia w Imageview i ImageButton app:srcCompat="@drawable/...." oraz w przypadku innych widoków, takich jak Button, Textview, zamiast używać "drawableLeft/right..."w XML, określ programowalne elementy rysunkowe jako:

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

I użyj „AppCompatResources”, aby pobrać plik do rysowania.

Shashank Kapsime
źródło
60

Aby rozwinąć inne bardzo dobre odpowiedzi , oto diagram, który może ci pomóc. Jest ważny, jeśli masz bibliotekę wsparcia od 23.4.0 do co najmniej 25.1.0.

Wektor ściągawka do rysowania

David Ferrand
źródło
1
w przypadku, gdy próbujesz ustawić drawableLeft, zawiń go wewnątrz drawable, jak wspomniano tutaj. medium.com/@chrisbanes/…
nizam.sp
Dziękuję, ta tabela bardzo nam pomogła.
silentsudo
39

Odpowiedź udzielona przez Guillherme P jest niesamowita. Żeby zrobić małą poprawę, nie musisz dodawać tej linii w każdym działaniu, jeśli dodałeś ją raz w klasie Application, to również zadziała.

public class App extends Application {

static {
    AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
}

PAMIĘTAJ: nadal musisz mieć włączoną obsługę biblioteki w gradle:

android {
  defaultConfig {
    vectorDrawables.useSupportLibrary = true
  }
}

Upewnij się też, że używasz biblioteki obsługi w wersji nowszej niż 23.4, gdy Google ponownie dodał obsługę kontenerów do rysowania dla VectorDrawables (informacja o wersji )

Aktualizacja

A dla zmian w kodzie:

  1. Upewnij się, że aktualizujesz do app:srcCompatkażdego miejsca, które akceptuje android:srcatrybut (IDE ostrzeże Cię, jeśli jest nieprawidłowy, jak w przypadku <bitmap>tagu).
  2. Na drawableLeft, drawableStart, drawableRight, drawableEndatrybuty używane w TextViewi podobne widoki, trzeba będzie ustawić je programowo teraz. Przykładowe ustawienie 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);
    }
    
Benny
źródło
pamiętaj, że musisz mieć WŁĄCZONE korzystanie z biblioteki obsługi obiektów do rysowania wektorów w gradle: vectorDrawables.useSupportLibrary = true
Benny
2
Tak. Wiem. Ale jest konieczne opakowanie, aby rysunki wektorowe działały jak rysunki dla TexViews, więc ta odpowiedź jest niekompletna.
cesards
1
Świetna odpowiedź. Jest to szczególnie dobre dla aplikacji, których wszystkie działania rozszerzone są z niestandardowej aktywności podstawowej.
Hong
14

Miałem ten sam problem. I napraw to, usuwając

vectorDrawables.useSupportLibrary = true

Moja wersja docelowa to 25, a biblioteka wsparcia to

 compile 'com.android.support:appcompat-v7:25.3.1'
Rajesh Nasit
źródło
1
to działało dla mnie. Dzięki. Właśnie to usunąłemvectorDrawables.useSupportLibrary = true
Android Średni
Myślę, że to nie jest właściwy sposób, aby to zrobić. Tak czy inaczej, zagłosowałem za twoją odpowiedzią. !!
Harish Reddy
Dziękuję Ci. t działa dla mojej aplikacji. Wszystkie rysunki wektorowe nadal działają, jeśli zostały usunięte. Aplikacja korzysta z com.android.support:appcompat-v7:28.0.0
Hong
10

VectorDrawables na pre-Lollipop powinny działać dobrze bez użycia

AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);

Jeśli chcesz używać VectorDrawables wewnątrz ImageViews, możesz użyć atrybutu srcCompati zadziała, ale w Buttons lub TextViews nie będzie , więc musisz zawinąć Drawable w InsetDrawable lub LayerDrawable. Jest jeszcze jedna sztuczka, którą odkryłem, jeśli używasz wiązania danych, możesz to zrobić:

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

To magicznie zadziała, nie badałem, co dzieje się za kulisami, ale wydaje mi się, że TextView używa metody getDrawable z AppCompatResources lub podobnej.

cesardy
źródło
Jak ustawić obraz wektorowy w selektorze?
Tushar Gogna
po ustawieniu vectorDrawables.useSupportLibrary = true w gradle default i AppCompatDelegate.setCompatVectorFromResourcesEnabled (true); w działaniu podczas tworzenia Aby uniknąć awarii z androidem: drawableleft w Textview , ustaw drawble w lewo do widoku tekstu programowo, na przykład: textview.setCompoundDrawablesWithIntrinsicBounds (R.drawable.movie, 0, 0, 0);
Afjalur Rahman Rana
7

Wiele prac badawczo-rozwojowych, w końcu otrzymuję to rozwiązanie na wypadek awarii na urządzeniach sprzed wersji Lollipop.

W przypadku Imageview

  • użyj app: srcCompat zamiast android: src

Dla TextView / EditText

  • Usuń drawableleft , drawableright ... i ustaw z rysowalnego kodu java.

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

Dla Build.gradle

vectorDrawables.useSupportLibrary = true

Jatin Mandanka
źródło
1
Było to unikalne rozwiązanie, które działało na TextInputEditText.
Heronsanches
1
Super, to rozwiązało mój problem. To powinna zostać zaakceptowana odpowiedź.
DJtiwari
7

Najłatwiejszy sposób użycia:

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

i ... po prostu użyj app:**Compatdla kompatybilności. Dodaj również obsługę build.gradle(moduł)

android {
   defaultConfig {
       vectorDrawables.useSupportLibrary = true
   }
}
Hamed Jaliliani
źródło
app: drawableEndCompat i app: drawableRightCompat to prawie to samo, jeśli jest angielski
Hossam Hassan
5

Dla każdego, kto zaktualizuje do Androida Gradle 3.0 i nowszy, nie ma potrzeby używania AppCompatDelegate.setCompatVectorFromResourcesEnabled(true)ani ustawiania vectorDrawables.useSupportLibrary = true(dodanie tego spowoduje problem) i używaniaapp:srcCompat , po prostu działa.

Zajmij mi dwa dni, aby to rozgryźć, i nie znalazłem żadnych powiązanych dokumentów w dokumentach Google ...

Geng Jiawen
źródło
Co ciekawe, używam gradle 3.3.0 i to rozwiązanie działa. Jednak Android Studio nadal mówi mi, aby włączyć ustawianie vectorDrawables.useSupportLibrary = true.
masterwok,
2

Używam VectorDrawables na urządzeniach Pre-Lollipop i tak to robię: -

Krok 1: Umieść to w ocenie na poziomie aplikacji.

android {
  defaultConfig {
    vectorDrawables.useSupportLibrary = true
  }
}

Krok 2:

Umieść to w swojej klasie Application i nie zapomnij zarejestrować swojej klasy Application w pliku manifestu.

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

Krok 3:

Pobierz VectorDrawables za pomocą,

imageView.setImageDrawable(ContextCompat.getDrawable(this, R.drawable.my_vector_drawable));
Parag Kadam
źródło
2

Po użyciu poniższego kodu.

android {
  defaultConfig {
  vectorDrawables.useSupportLibrary = true  
                }
        }




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

nadal istnieje problem z obrazami wektorowymi dla poniższych atrybutów

DrawableEnd, DrawableStart, DrawableTop, DrawableBottom, Background

W takim przypadku postępuj zgodnie z poniższymi wskazówkami. Zamiast bezpośredniego odniesienia do obrazu wektorowego użyj znacznika selektora jako pośredniego pliku do rysowania.

Przykład:

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>

Twój 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"
       />
Rajesh Gr
źródło
Dzięki !!! W przypadku właściwości tła dodanie selektora pomiędzy, a nie bezpośrednio przy użyciu wektorów do rysowania działało dla api pre-lollipop (19).
Ankur
w moim przypadku to nadal nie działa dla API (16), nawet zawijającselector
mochadwi
1

Wypróbowaliśmy 3 rzeczy

vectorDrawables.useSupportLibrary = true

Ustawienie setCompatVectorFromResourcesEnabled w klasie Application

static {
    AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
}

I użyć app:srcCompat

Ale nawet potem zawodziło

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

potem zorientowaliśmy się, że nasz SVG ma tag Gradient. Konwersja tagu gradientu na indywidualne ścieżki dla poniższego API <= 23 i użycie tego samego API SVG> = 24 zadziałało.

Pomoc z tej odpowiedzi: https://stackoverflow.com/a/47783962/2171513

Aalap
źródło
Nie wiem, dlaczego nikt tego nie głosuje, ale to faktycznie może być rozwiązanie mojego problemu. Dziękuję
DevMike01
1

Walczyłem z tym godzinami.

Wypróbowałem wszystko, co podpowiadały mi te odpowiedzi, ale moja aplikacja nie przestawała się zawieszać. Usunąłem ten wiersz: app:srcCompat="@drawable/keyboard"i moja aplikacja przestała się zawieszać. a kiedy dodałem z powrotem to samo, znowu zaczęło się zawieszać. Postanowiłem więc otworzyć ten plik i zobaczyłem błąd w pierwszym wierszu:

„Klawiatura do rysowania” nie ma deklaracji w podstawowym folderze do rysowania; może to prowadzić do awarii.

Kliknąłem plik prawym przyciskiem myszy i kliknąłem „Pokaż w eksploratorze” i nie znajdował się on w folderze do rysowania, ale w katalogu drawable-v24. Więc skopiowałem go i wkleiłem do katalogu do rysowania i ostatecznie pozbyłem się awarii.

Fahad Maqsood Qazi
źródło
0

Po prostu nałóż wektor, który można narysować na listę stanów, a problem zostanie rozwiązany

Na przykład masz obraz wektorowy strzałki wstecz:

ic_back_arrow.xml

tak, powinieneś nałożyć go na listę warstw 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>

Ponieważ logika:

vectorDrawables.useSupportLibrary = true

i

AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);

nie pomoże Ci na niektórych chińskich urządzeniach i starszych urządzeniach Samsung. Jeśli ich nie pokryjesz, to się nie powiedzie.

mr.boyfox
źródło
-2

Sugestia Guilherme P nie zadziałała dla mnie. Poszedłem naprzód i podjąłem decyzję o użyciu png, gdzie muszę robić rzeczy poza aplikacją: srcCompat, czyli drawableLeft, drawableRight itp. To była dość łatwa zmiana i nie ma potencjalnych problemów z pamięcią AppCompatDelegate.setCompatVectorFromResourcesEnabled ( prawdziwe); wprowadza.

Ryan Newsom
źródło
-3

Alternatywą dla odpowiedzi Benny'ego jest stworzenie Activitynadklasy:

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

  //...
}

Teraz rozszerz VectorDrawableActivityzamiast AppCompatActivity.

Code-Apprentice
źródło
@cesards Dlaczego nie?
Code-Apprentice