Zmień kolor wypełnienia zasobu wektorowego w Android Studio

169

Android Studio obsługuje teraz zasoby wektorowe w wersjach 21+ i generuje pliki PNG dla niższych wersji w czasie kompilacji. Mam zasób wektorowy (z ikon materiałów), dla którego chcę zmienić kolor wypełnienia. Działa to na 21+, ale wygenerowane png nie zmieniają koloru. Czy jest na to sposób?

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

Patrick
źródło

Odpowiedzi:

333

Nie edytuj bezpośrednio zasobów wektorowych. Jeśli używasz wektora do rysowania w ImageButton, po prostu wybierz kolor w android:tint.

<ImageButton
        android:layout_width="48dp"
        android:layout_height="48dp"
        android:id="@+id/button"
        android:src="@drawable/ic_more_vert_24dp"
        android:tint="@color/primary" />
RRRR-MM-DD
źródło
24
barwienie działa tylko na 21+ urządzeniach, czy masz jakieś sugestie dotyczące urządzeń pre-lollipop
mudit
12
android: tint działa na wszystkich wersjach Androida od APIv1. Masz na myśli drawableTint.
RRRR-MM-DD
31
android:tintmusi być poandroid:src
EmmanuelMess
5
A co drawableLeftw Buttonśrodku?
Pratik Butani
8
@mudit spróbuj użyć wektora z fillColor = "# colorvalue", nie używaj odniesienia @ color, ponieważ działają one tylko w SDK 21+ dla wektorów (a więc nie dla wygenerowanych
plików
95

Możesz to zrobić.

ALE nie możesz używać odniesień @color do kolorów (..lame), w przeciwnym razie będzie działać tylko dla L +

<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="24dp"
    android:height="24dp"
    android:viewportWidth="24.0"
    android:viewportHeight="24.0">
<path
    android:fillColor="#FFAABB"
    android:pathData="M15.5,14h-0.79l-0.28,-0.27C15.41,12.59 16,11.11 16,9.5 16,5.91 13.09,3 9.5,3S3,5.91 3,9.5 5.91,16 9.5,16c1.61,0 3.09,-0.59 4.23,-1.57l0.27,0.28v0.79l5,4.99L20.49,19l-4.99,-5zm-6,0C7.01,14 5,11.99 5,9.5S7.01,5 9.5,5 14,7.01 14,9.5 11.99,14 9.5,14z"/>

urSus
źródło
11
To powinna być akceptowana odpowiedź! @color odwołania nie działają w wektorach przed Lollipopem (więc wektor -> konwersja PNG) code.google.com/p/android/issues/detail?id=186431
PieterAelse
5
Odniesienia @color mogą być teraz używane dla atrybutu fillColor dla wszystkich wersji Androida, jednak nie obsługuje on list stanów kolorów.
TheIT
Wygląda na to, że sposób tworzenia list stanów wektorów jest za pomocą AnimatedStateListDrawable s
gMale
1
@TheIT co mam, aby to włączyć? Wydaje się, że nie działa dla mnie
urSus
@PieterAelse Nie jestem pewien, co zrobić, jeśli chcesz użyć tego samego zasobu, ale z różnymi tłami (odcienie jak w poprzedniej odpowiedzi). W swoim rozwiązaniu będziesz mieć kilka instancji tego samego zasobu, ale z innym kolorem wypełnienia
Anton Makov,
71

Jak powiedziano w innych odpowiedziach, nie edytuj bezpośrednio wektora do rysowania, zamiast tego możesz odcień w kodzie java, na przykład:

    mWrappedDrawable = mDrawable.mutate();
    mWrappedDrawable = DrawableCompat.wrap(mWrappedDrawable);
    DrawableCompat.setTint(mWrappedDrawable, mColor);
    DrawableCompat.setTintMode(mWrappedDrawable, PorterDuff.Mode.SRC_IN);

Dla uproszczenia stworzyłem klasę pomocniczą:

import android.content.Context;
import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.support.annotation.ColorRes;
import android.support.annotation.DrawableRes;
import android.support.annotation.NonNull;
import android.support.v4.content.ContextCompat;
import android.support.v4.graphics.drawable.DrawableCompat;
import android.view.MenuItem;
import android.view.View;
import android.widget.ImageView;

/**
 * {@link Drawable} helper class.
 *
 * @author Filipe Bezerra
 * @version 18/01/2016
 * @since 18/01/2016
 */
public class DrawableHelper {
    @NonNull Context mContext;
    @ColorRes private int mColor;
    private Drawable mDrawable;
    private Drawable mWrappedDrawable;

    public DrawableHelper(@NonNull Context context) {
        mContext = context;
    }

    public static DrawableHelper withContext(@NonNull Context context) {
        return new DrawableHelper(context);
    }

    public DrawableHelper withDrawable(@DrawableRes int drawableRes) {
        mDrawable = ContextCompat.getDrawable(mContext, drawableRes);
        return this;
    }

    public DrawableHelper withDrawable(@NonNull Drawable drawable) {
        mDrawable = drawable;
        return this;
    }

    public DrawableHelper withColor(@ColorRes int colorRes) {
        mColor = ContextCompat.getColor(mContext, colorRes);
        return this;
    }

    public DrawableHelper tint() {
        if (mDrawable == null) {
            throw new NullPointerException("É preciso informar o recurso drawable pelo método withDrawable()");
        }

        if (mColor == 0) {
            throw new IllegalStateException("É necessário informar a cor a ser definida pelo método withColor()");
        }

        mWrappedDrawable = mDrawable.mutate();
        mWrappedDrawable = DrawableCompat.wrap(mWrappedDrawable);
        DrawableCompat.setTint(mWrappedDrawable, mColor);
        DrawableCompat.setTintMode(mWrappedDrawable, PorterDuff.Mode.SRC_IN);

        return this;
    }

    @SuppressWarnings("deprecation")
    public void applyToBackground(@NonNull View view) {
        if (mWrappedDrawable == null) {
            throw new NullPointerException("É preciso chamar o método tint()");
        }

        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
            view.setBackground(mWrappedDrawable);
        } else {
            view.setBackgroundDrawable(mWrappedDrawable);
        }
    }

    public void applyTo(@NonNull ImageView imageView) {
        if (mWrappedDrawable == null) {
            throw new NullPointerException("É preciso chamar o método tint()");
        }

        imageView.setImageDrawable(mWrappedDrawable);
    }

    public void applyTo(@NonNull MenuItem menuItem) {
        if (mWrappedDrawable == null) {
            throw new NullPointerException("É preciso chamar o método tint()");
        }

        menuItem.setIcon(mWrappedDrawable);
    }

    public Drawable get() {
        if (mWrappedDrawable == null) {
            throw new NullPointerException("É preciso chamar o método tint()");
        }

        return mWrappedDrawable;
    }
}

Aby użyć, wykonaj następujące czynności:

    DrawableHelper
            .withContext(this)
            .withColor(R.color.white)
            .withDrawable(R.drawable.ic_search_24dp)
            .tint()
            .applyTo(mSearchItem);

Lub:

    final Drawable drawable = DrawableHelper
            .withContext(this)
            .withColor(R.color.white)
            .withDrawable(R.drawable.ic_search_24dp)
            .tint()
            .get();

    actionBar.setHomeAsUpIndicator(drawable);
Filipe Bezerra de Sousa
źródło
Cześć Filipe, dziękuję za odpowiedź. Czy masz bibliotekę, github z wyciętym kodem, gdzie możemy zobaczyć licencję? dzięki :)
Vincent D.
2
Nie, Vicent, żadnej licencji. Rozwiązanie jest tak proste, że wydaje mi się, że użyty tutaj wzorzec Builder nie jest konieczny. Ale każdy może skorzystać z tego rozwiązania i używać go bez licencji.
Filipe Bezerra de Sousa
Świetna odpowiedź! Działa dokładnie tak, jak potrzebuję
Eoin
1
@VincentD. Stopka strony internetowej SO mówi: „Wkłady użytkowników licencjonowane na podstawie CC by-sa 3.0 z wymaganym
podaniem źródła
U mnie zadziałało z pewnymi zmianami. Dziękuję za pomoc.
André Luiz Reis
36

Aby zmienić kolor obrazu wektorowego, możesz bezpośrednio użyć android: tint = "@ color / colorAccent"

<ImageView
        android:id="@+id/ivVectorImage"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/ic_account_circle_black_24dp"
        android:tint="@color/colorAccent" />

Aby programowo zmienić kolor

ImageView ivVectorImage = (ImageView) findViewById(R.id.ivVectorImage);
ivVectorImage.setColorFilter(getResources().getColor(R.color.colorPrimary));
Rana Ranvijay Singh
źródło
Funkcja getColor () jest przestarzała
David,
Jak go używać do rysowania *** TextView?
Hemant Kaushik
getColor (ResId) jest przestarzała @David, ale getColor(ResId, Theme)tak nie jest. Lub możesz użyć, ResourcesCompat.getColor(getResources(), R.color.primary, null);jeśli nie dbasz o motyw… lub jeśli delegat kontekstu / polityki JEST działaniem, możesz to zrobić getTheme()dla tego ostatniego parametru.
Martin Marconcini
15

Obecnie działającym rozwiązaniem jest android: fillColor = "# FFFFFF"

Nic nie działało dla mnie poza twardym kodowaniem w wektorze

<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="24dp"
    android:height="24dp"
    android:viewportWidth="24.0"
      android:fillColor="#FFFFFF"
    android:viewportHeight="24.0">
<path
    android:fillColor="#FFFFFF"
    android:pathData="M15.5,14h-0.79l-0.28,-0.27C15.41,12.59 16,11.11 16,9.5 16,5.91 13.09,3 9.5,3S3,5.91 3,9.5 5.91,16 9.5,16c1.61,0 3.09,-0.59 4.23,-1.57l0.27,0.28v0.79l5,4.99L20.49,19l-4.99,-5zm-6,0C7.01,14 5,11.99 5,9.5S7.01,5 9.5,5 14,7.01 14,9.5 11.99,14 9.5,14z"/>

Jednak wypełnienie i tinta mogą wkrótce działać. Więcej informacji można znaleźć w tej dyskusji:

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

Również kolory pozostają w pamięci podręcznej, więc usunięcie aplikacji dla wszystkich użytkowników może pomóc.

sivi
źródło
7

Android studio obsługuje teraz wektory pre-lollipop. Brak konwersji do formatu PNG. Nadal możesz zmienić kolor wypełnienia i będzie działać.

W swoim ImageView użyj

 app:srcCompat="@drawable/ic_more_vert_24dp"

W pliku ocen

 // Gradle Plugin 2.0+  
 android {  
   defaultConfig {  
     vectorDrawables.useSupportLibrary = true  
   }  
 }  

 compile 'com.android.support:design:23.4.0'
Sayooj Valsan
źródło
10
Pytanie brzmi „jak zmienić kolor wypełnienia wektora”, a nie „jak używać zasobu wektorowego”
Leo Droidcoder
5

Aktualizacja: AppCompatwsparcie

Inne odpowiedzi podejrzewające, czy android:tintbędą działać tylko na urządzeniach 21+, AppCompat ( wersja 23.2.0 i nowsze ) zapewnia teraz kompatybilną wstecz obsługę atrybutu tint.

Tak więc, działaniem byłoby użycie AppCompatImageViewi app:srcCompat(w przestrzeni nazw AppCompat) zamiast android:src(przestrzeń nazw Androida).

Oto przykład (AndroidX: to jest androidx.appcompat.widget.AppCompatImageView ;)):

<android.support.v7.widget.AppCompatImageView
        android:id="@+id/credits_material_icon"
        android:layout_width="20dp"
        android:layout_height="20dp"
        android:layout_marginBottom="8dp"
        android:layout_marginLeft="16dp"
        android:layout_marginStart="16dp"
        android:scaleType="fitCenter"
        android:tint="#ffd2ee"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:srcCompat="@drawable/ic_dollar_coin_stack" />

I nie zapomnij włączyć obsługi grafiki wektorowej w Gradle:

vectorDrawables.useSupportLibrary = true 
Manish Kumar Sharma
źródło
Tylko aktualizacja. Obecnie AppCompatImageViewjest poniżejandroidx.appcompat.widget.AppCompatImageView
Roc Boronat
2

Dodaj tę bibliotekę do Gradle, aby umożliwić rysowanie wektorów kolorów na starych urządzeniach z Androidem.

compile 'com.android.support:palette-v7:26.0.0-alpha1'

i ponownie zsynchronizuj gradle. Myślę, że to rozwiąże problem.

Siddhartha Maji
źródło
2

Jeśli wektory nie pokazują indywidualnie ustawionych kolorów przy użyciu fillColor, mogą być ustawione na domyślny parametr widgetu.

Spróbuj dodać app:itemIconTint="@color/lime"do pliku activity_main.xml, aby ustawić domyślny typ koloru ikon widżetów.

<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/drawer_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    tools:openDrawer="start">

    <include
        layout="@layout/app_bar_main"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    <android.support.design.widget.NavigationView
        android:id="@+id/nav_view"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        android:fitsSystemWindows="true"
        app:headerLayout="@layout/nav_header_main"
        app:itemIconTint="@color/lime"
        app:menu="@menu/activity_main_drawer" />

</android.support.v4.widget.DrawerLayout>

VectorDrawable @ developers.android

lubi
źródło
W Android Studio 3.6 zmiana w svg xml w tym polu: android: fillColor = "# FFFFC400"
a_subscriber
1

jeśli chcesz obsługiwać starszą wersję przed lolipopem

użyj tego samego kodu XML z pewnymi zmianami

zamiast normalnego ImageView --> AppCompatImageView

zamiast android:src --> app:srcCompat

oto przykład

<android.support.v7.widget.AppCompatImageView
        android:layout_width="48dp"
        android:layout_height="48dp"
        android:id="@+id/button"
        app:srcCompat="@drawable/ic_more_vert_24dp"
        android:tint="@color/primary" />

nie zapomnij zaktualizować swojego gradle jako wzmianka @ Sayooj Valsan

// Gradle Plugin 2.0+  
 android {  
   defaultConfig {  
     vectorDrawables.useSupportLibrary = true  
   }  
 }  

 compile 'com.android.support:design:23.4.0'

Zauważ, że żadnemu wektorowi nie wolno nigdy podawać odniesienia do koloru takiego jak ten, android:fillColor="@color/primary"podając jego wartość szesnastkową.

Mina Fawzy
źródło
Dlatego nigdy nie należy używać @colordo fillcolor?
HoseinIT
0

Dla tych, którzy nie używają znaku ImageView, poniższe działały dla mnie na zwykłym View(i dlatego zachowanie powinno się powielać na każdym rodzaju widoku)

<View
    android:background="@drawable/ic_reset"
    android:backgroundTint="@color/colorLightText" />
Bonton255
źródło