Jak wyśrodkować wektor możliwy do rysowania na liście warstw bez skalowania

102

Próbuję użyć a VectorDrawablein a LayerListbez skalowania wektora. Na przykład:

<layer-list>
    <item android:drawable="@color/grid_item_activated"/>
    <item android:gravity="center" android:drawable="@drawable/ic_check_white_48dp"/>
</layer-list>

ic_check_white_48dpIdentyfikator do rysowania zdefiniowany jako:

<vector xmlns:android="http://schemas.android.com/apk/res/android"
        android:width="48dp"
        android:height="48dp"
        android:viewportWidth="24.0"
        android:viewportHeight="24.0">
    <path
        android:fillColor="#FFFFFFFF"
        android:pathData="M9,16.17L4.83,12l-1.42,1.41L9,19 21,7l-1.41,-1.41z"/>
</vector>

Pożądanym efektem jest wyśrodkowanie ikony zaznaczenia na warstwie, którą można rysować, bez skalowania. Problem polega na tym, że powyższa lista warstw powoduje przeskalowanie ikony zaznaczenia, aby dopasować ją do rozmiaru warstwy.

I można wytworzyć pożądany efekt, jeśli wymienić rozciągliwej wektor z PNG dla każdej gęstości i modyfikowania warstwy-lista w następujący sposób:

<layer-list>
    <item android:drawable="@color/grid_item_activated"/>
    <item>
        <bitmap android:gravity="center" android:src="@drawable/ic_check_white_48dp"/>
    </item>
</layer-list>

Czy jest jakiś sposób, aby to zrobić za pomocą VectorDrawable?

Ashughes
źródło
11
Wygląda na to, że może to być po prostu błąd w API 21/22. Właśnie przetestowałem urządzenie z interfejsem API 23 i wektor, który można rysować, był prawidłowo wyśrodkowany.
ashughes
1
Powinieneś odpowiedzieć na swoje pytanie i zaakceptować je. W ten sposób jest bardziej widoczny, a pytanie nie pojawia się bez odpowiedzi.
Marcin Koziński
1
Nie odpowiedziałem na moje pytanie, ponieważ nie mam jeszcze odpowiedzi, co robić w API 21/22. Moim tymczasowym rozwiązaniem było użycie PNG zamiast wektorów. Mam nadzieję, że nadal znajdę sposób, aby wektory działały.
ashughes
1
Otworzyłem problem tutaj: code.google.com/p/android/issues/detail?id=219600
toobsco42
Jest to już naprawione w API 23, więc zgaduję, że twój raport o błędzie zostanie po prostu oznaczony jako naprawiony.
ashughes

Odpowiedzi:

29

Napotkałem ten sam problem, próbując wyśrodkować wektory drawables na liście warstwowej.

Mam obejście, to nie jest dokładnie to samo, ale działa, musisz ustawić rozmiar dla całego elementu do rysowania i dodać wypełnienie do elementu wektorowego:

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item>
        <shape>
            <size android:height="120dp" android:width="120dp"/>
            <solid android:color="@color/grid_item_activated"/>
        </shape>
    </item>
    <item android:top="24dp"
          android:bottom="24dp"
          android:left="24dp"
          android:right="24dp"
          android:drawable="@drawable/ic_check_white_48dp"/>
</layer-list>

Rozmiar powyższego kształtu określa rozmiar całego rysowanego kształtu, w tym przykładzie 120 dp, wypełnienie drugiego elementu, w tym przykładzie 24 dp, wyśrodkowuje obraz wektorowy.

To nie to samo, co gravity="center"używanie wektorów w API 21 i 22, ale jego działający sposób.

Nicolas Tyler
źródło
2
Wygląda na to, że wymagałoby to znajomości dokładnego rozmiaru elementu do rysowania (w celu ustawienia rozmiaru kształtu), zamiast pozwalać mu wypełniać całą przestrzeń, na którą jest ustawiony, z wyśrodkowanym w nim czekiem do rysowania.
ashughes,
1
możesz nie znać rozmiaru, ale nie możesz zmienić wypełnienia tą metodą, możesz nałożyć na siebie elementy do rysowania w obrazach. Jak wspomniałem powyżej, nie ma idealnego rozwiązania dla gravity="center"API 21 i 22.
Nicolas Tyler
Nie działa podczas korzystania z windowBackground. Jawne rozmiary nie działają w tym przypadku. I to też nie działa, jeśli umieszczę listę warstw na innej liście warstw.
Wsiewołod Ganin
19

Zmagałem się z tym samym problemem. Jedynym sposobem, w jaki udało mi się to naprawić i uniknąć możliwości skalowania w górę, było ustawienie rozmiaru do rysowania i użycie grawitacji do wyrównania go:

<layer-list>
    <item android:drawable="@color/grid_item_activated"/>
     <item
        android:gravity="center"
        android:width="48dp"
        android:height="48dp"
        android:drawable="@drawable/ic_check_white_48dp"/>
</layer-list>

Mam nadzieję, że to pomoże!

Felipe Duarte
źródło
34
Nie pomaga to w API 21-22, ponieważ widthatrybut nie jest dostępny na tych poziomach API. W API 23 nie są one potrzebne, ponieważ błąd został naprawiony, a plik do rysowania nie jest już rozciągany.
WonderCsabo
3
To nie działa poprawnie dla API 27, obraz jest rozciągnięty do pełnego ekranu
Konstantin Konopko
8

Edytowane rozwiązanie, które sprawi, że Twój SplashScreen będzie wyglądał świetnie na wszystkich interfejsach API, w tym API21 do API23

Przede wszystkim przeczytaj ten artykuł i postępuj zgodnie z DOBRYM sposobem tworzenia ekranu powitalnego.

Jeśli Twoje logo jest zniekształcone lub nie pasuje, a Twoim celem jest tylko APIs24 +, możesz po prostu zmniejszyć rozmiar swojego wektora do rysowania bezpośrednio w jego pliku xml:

<vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt"
android:viewportWidth="640"
android:viewportHeight="640"
android:width="240dp"
android:height="240dp">
<path
    android:pathData="M320.96 55.9L477.14 345L161.67 345L320.96 55.9Z"
    android:strokeColor="#292929"
    android:strokeWidth="24" />
</vector>

w powyższym kodzie przeskaluję rysunek, który narysowałem na płótnie 640x640, do rozmiaru 240x240. potem po prostu umieściłem go na moim ekranie powitalnym do rysowania w ten sposób i działa świetnie:

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" android:opacity="opaque"
android:paddingBottom="20dp" android:paddingRight="20dp" android:paddingLeft="20dp" android:paddingTop="20dp">

<!-- The background color, preferably the same as your normal theme -->
<item>
    <shape>
        <size android:height="120dp" android:width="120dp"/>
        <solid android:color="@android:color/white"/>
    </shape>
</item>

<!-- Your product logo - 144dp color version of your app icon -->
<item
    android:drawable="@drawable/logo_vect"
    android:gravity="center">

</item>
</layer-list>

mój kod faktycznie rysuje tylko trójkąt na obrazku u dołu, ale tutaj widzisz, co możesz dzięki temu osiągnąć. Rozdzielczość jest w końcu świetna w przeciwieństwie do pikselowanych krawędzi, które uzyskiwałem podczas korzystania z bitmapy. więc używaj wektora, który można narysować na wszelkie sposoby (istnieje strona o nazwie vectr, której użyłem do stworzenia mojego bez konieczności pobierania specjalistycznego oprogramowania).

EDYTUJ, aby działał również na API21-22-23

Chociaż powyższe rozwiązanie działa na urządzeniach z API24 +, po zainstalowaniu mojej aplikacji na urządzeniu z API22 byłem bardzo rozczarowany. Zauważyłem, że ekran powitalny ponownie próbował wypełnić cały widok i wyglądał jak gówno. Po wyrywaniu brwi przez pół dnia w końcu brutalnie wymusiłem rozwiązanie siłą woli.

musisz utworzyć drugi plik nazwany dokładnie tak jak splashscreen xml (powiedzmy splash_screen.xml) i umieścić go w 2 folderach o nazwach drawable-v22 i drawable-v21, które utworzysz w folderze res / (aby je zobaczyć, musisz zmienić widok projektu z Androida na Projekt). Służy to do poinformowania telefonu, aby przekierował do plików umieszczonych w tych folderach, gdy odpowiednie urządzenie uruchamia interfejs API odpowiadający sufiksowi -vXX w folderze do rysowania, zobacz ten link . umieść następujący kod na liście warstw pliku splash_screen.xml, który tworzysz w tych folderach:

<item>
<shape>
    <size android:height="120dp" android:width="120dp"/>
    <solid android:color="@android:color/white"/>
</shape>
</item>

<!-- Your product logo - 144dp color version of your app icon -->
<item android:gravity="center">
    <bitmap android:gravity="center"
        android:src="logo_vect"/>

</item>

tak wyglądają foldery

Z jakiegoś powodu w przypadku tych interfejsów API musisz owinąć twoją rysowaną mapę bitową, aby działała, a ostateczny wynik wygląda tak samo. Problem polega na tym, że musisz użyć podejścia z dodatkowymi folderami do rysowania, ponieważ druga wersja pliku splash_screen.xml spowoduje, że ekran powitalny nie będzie w ogóle wyświetlany na urządzeniach z interfejsami API wyższymi niż 23. Być może będziesz musiał również umieścić pierwsza wersja pliku splash_screen.xml do drawable-v24, jako że Android domyślnie ustawia najbliższy folder drawable-vXX, jaki może znaleźć dla zasobów.

mój ekran powitalny

quealegriamasalegre
źródło
4

Obecnie pracuję nad ekranem powitalnym z rysowanym wektorem wyśrodkowanym zarówno w poziomie, jak iw pionie. Oto, co otrzymałem na poziomie API 25:

<layer-list xmlns:android="http://schemas.android.com/apk/res/android" android:opacity="opaque">

<!-- background color, same as theme -->
<item android:drawable="@android:color/white"/>

<!-- app logo -->

<item
    android:drawable="@drawable/my_app_logo"
    android:gravity="center_horizontal|center_vertical"
    android:width="200dp"
    android:height="200dp"
    />
</layer-list>

Jeśli chcesz dostosować wymiar rysowanego wektora, zmodyfikuj android:widthi android:heightatrybuty powyżej, niekoniecznie modyfikuj oryginalny rysunkowy.

Ponadto moje doświadczenie jest takie, że NIE WOLNO umieszczać wektora do rysowania w tagu bitmapowym, który nigdy nie działa dla mnie. Bitmapa jest przeznaczona do plików w formacie .png, .jpg i .gif, a nie do rysowania wektorowego.

Odniesienie

https://developer.android.com/guide/topics/resources/drawable-resource#LayerList

https://developer.android.com/guide/topics/resources/more-resources.html#Dimension

Wenhe Qi
źródło
1
Dobry chwyt dotyczący rysunków wektorowych, które muszą znajdować się wewnątrz znacznika <item> zamiast znacznika <bitmap>.
FrostRocket