Ottenere Bitmap dal disegno vettoriale


133

Nella mia applicazione, devo impostare un'icona grande per una notifica. LargeIcon deve essere una bitmap e i miei disegni estraibili sono immagini vettoriali (la nuova funzionalità di Android, vedi questo link ) Il problema è quando provo a decodificare una risorsa che è un'immagine vettoriale, ottengo un valore null restituito.

Ecco l'esempio di codice:

if (BitmapFactory.decodeResource(arg0.getResources(), R.drawable.vector_menu_objectifs) == null)
        Log.d("ISNULL", "NULL");
    else
        Log.d("ISNULL", "NOT NULL");

In questo esempio, quando sostituisco R.drawable.vector_menu_objectifs con un'immagine "normale", un png per esempio, il risultato non è nullo (ottengo la bitmap corretta) C'è qualcosa che mi manca?


1
Ebbe figli simile, non è la soluzione ma una soluzione: stackoverflow.com/questions/33548447/...
Than

Risposte:


232

Controllato sull'API: 17, 21, 23

public static Bitmap getBitmapFromVectorDrawable(Context context, int drawableId) {
    Drawable drawable = ContextCompat.getDrawable(context, drawableId);
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
        drawable = (DrawableCompat.wrap(drawable)).mutate();
    }

    Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(),
            drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
    Canvas canvas = new Canvas(bitmap);
    drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
    drawable.draw(canvas);

    return bitmap;
}

AGGIORNARE:

Grado del progetto:

dependencies {
        classpath 'com.android.tools.build:gradle:2.2.0-alpha5'
    }

Grado del modulo:

android {
    compileSdkVersion 23
    buildToolsVersion '23.0.3'
    defaultConfig {
        minSdkVersion 16
        targetSdkVersion 23
        vectorDrawables.useSupportLibrary = true
    }
    ...
}
...

Grazie. La risposta precedente non funzionava con la versione sdk a partire dal 23
Paha,

2
Questo funziona alla grande per me. In esecuzione su API 15 senza problemi
vera

4
AppCompatDrawableManagerè contrassegnato come @RestrictTo(LIBRARY_GROUP)tale è interno e non dovresti usarlo (l'API può cambiare senza preavviso). Usa ContextCompatinvece.
mradzinski,

non funziona Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.graphics.Bitmap.setHasAlpha(boolean)' on a null object referencesu questa lineaBitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
user25

5
Ho avuto problemi con questa soluzione. Per me usando: AppCompatResources invece di ContextCompat risolto: Drawable drawable = AppCompatResources.getDrawable (context, drawableId);
Mike T,

64

Puoi usare il seguente metodo:

@TargetApi(Build.VERSION_CODES.LOLLIPOP)
private static Bitmap getBitmap(VectorDrawable vectorDrawable) {
    Bitmap bitmap = Bitmap.createBitmap(vectorDrawable.getIntrinsicWidth(),
            vectorDrawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
    Canvas canvas = new Canvas(bitmap);
    vectorDrawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
    vectorDrawable.draw(canvas);
    return bitmap;
}

che a volte unisco a:

private static Bitmap getBitmap(Context context, int drawableId) {
    Drawable drawable = ContextCompat.getDrawable(context, drawableId);
    if (drawable instanceof BitmapDrawable) {
        return ((BitmapDrawable) drawable).getBitmap();
    } else if (drawable instanceof VectorDrawable) {
        return getBitmap((VectorDrawable) drawable);
    } else {
        throw new IllegalArgumentException("unsupported drawable type");
    }
}

2
speriamo che @liltof ritorni e lo contrassegni come risposta. L'unica cosa da notare è che entrambi i metodi vogliono il wrapper targetAPi, ma Android Studio te lo dirà.
roberto tomás,

1
Ho trascorso circa due giorni cercando di farlo ora pensando al suo problema con il file svg. Grazie!
sparkly_frog,

1
non funziona Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.graphics.Bitmap.setHasAlpha(boolean)' on a null object referencesu questa lineaBitmap bitmap = Bitmap.createBitmap(vectorDrawable.getIntrinsicWidth(), vectorDrawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
user25

45

Se desideri utilizzare Android KTX per Kotlin, puoi utilizzare il metodo di estensione Drawable#toBitmap()per ottenere lo stesso effetto delle altre risposte:

val bitmap = AppCompatResources.getDrawable(requireContext(), drawableId).toBitmap() 

o

val bitmap = AppCompatResources.getDrawable(context, drawableId).toBitmap() 

Per aggiungere questo e altri utili metodi di estensione, dovrai aggiungere quanto segue a livello di modulo build.gradle

repositories {
    google()
}

dependencies {
    implementation "androidx.core:core-ktx:1.2.0"
}

Vedi qui per le ultime istruzioni per aggiungere la dipendenza al tuo progetto.

Si noti che questo funzionerà per qualsiasi sottoclasse di Drawablee se Drawableè un BitmapDrawablecollegamento verrà utilizzato per utilizzare il sottostante Bitmap.


Questa è la soluzione più semplice qui per Kotlin.
Adam Hurwitz,

1
Per me funziona perfettamente VectorDrawable.
ubuntudroid,

Questa è l'opzione migliore .. Androidx predefinito fornisce funzionalità
Reshma

27

Sulla base delle risposte precedenti, può essere semplificato in questo modo per abbinare sia VectorDrawable che BitmapDrawable ed essere compatibile con almeno API 15.

public static Bitmap getBitmapFromDrawable(Context context, @DrawableRes int drawableId) {
    Drawable drawable = AppCompatResources.getDrawable(context, drawableId);

    if (drawable instanceof BitmapDrawable) {
        return ((BitmapDrawable) drawable).getBitmap();
    } else if (drawable instanceof VectorDrawableCompat || drawable instanceof VectorDrawable) {
        Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);
        drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
        drawable.draw(canvas);

        return bitmap;
    } else {
        throw new IllegalArgumentException("unsupported drawable type");
    }
}

Quindi devi aggiungere il tuo file gradle:

android {
    defaultConfig {
        vectorDrawables.useSupportLibrary = true
    }
}

Su pre-Lollipop utilizzerà VectorDrawableCompat e su Lollipop utilizzerà VectorDrawable.

MODIFICARE

Ho modificato la condizione seguendo il commento di @ user3109468


1
istanza drawable di VectorDrawable || istanza drawable di VectorDrawableCompat dovrebbe avere i lati invertiti. Risultati in una classe non trovata quando VectorDrawable non esiste, quando VectorDrawableCompat deve essere verificato per primo perché esiste.
Warrick,

il controllo del tipo di VertorDrawable può essere descritto come(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && drawable instanceof VectorDrawable)
chmin

6

Complimenti a @Alexey

Ecco la Kotlinversione che utilizza le estensioni aContext

fun Context.getBitmapFromVectorDrawable(drawableId: Int): Bitmap? {
    var drawable = ContextCompat.getDrawable(this, drawableId) ?: return null

    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
        drawable = DrawableCompat.wrap(drawable).mutate()
    }

    val bitmap = Bitmap.createBitmap(
            drawable.intrinsicWidth,
            drawable.intrinsicHeight,
            Bitmap.Config.ARGB_8888) ?: return null
    val canvas = Canvas(bitmap)
    drawable.setBounds(0, 0, canvas.width, canvas.height)
    drawable.draw(canvas)

    return bitmap
}

Esempio di utilizzo in Activity:

val bitmap = this.getBitmapFromVectorDrawable(R.drawable.ic_done_white_24dp)

1

Testato su API 16 - JellyBean con Drawable vettoriali

public static Bitmap getBitmapFromVectorDrawable(Context context, int drawableId) {
    Drawable drawable = AppCompatResources.getDrawable(context, drawableId);
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
        drawable = (DrawableCompat.wrap(drawable)).mutate();
    }

    Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(),
            drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
    Canvas canvas = new Canvas(bitmap);
    drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
    drawable.draw(canvas);

    return bitmap;
}   

1

Utilizzare il seguente codice per convertire l'immagine con le proporzioni corrette (ad es. Per l'icona di notifica):

public static Bitmap getBitmapFromVector(Context context, int drawableId) {
    Drawable drawable = ContextCompat.getDrawable(context, drawableId);
    int width = drawable.getIntrinsicWidth();
    int height = drawable.getIntrinsicHeight();
    Bitmap bitmap;
    if (width < height) {    //make a square
        bitmap = Bitmap.createBitmap(height, height, Bitmap.Config.ARGB_8888);
    } else {
        bitmap = Bitmap.createBitmap(width, width, Bitmap.Config.ARGB_8888);
    }
    Canvas canvas = new Canvas(bitmap);
    drawable.setBounds(0, 0,
            drawable.getIntrinsicWidth(),    //use dimensions of Drawable
            drawable.getIntrinsicHeight()
    );
    drawable.draw(canvas);
    return bitmap;
}

0

Se la tua vectorimmagine intrinsicWidtheintrinsicHeight è piccolo e si tenta di visualizzare l'immagine bitmap ad una grande vista, poi si vedrà il risultato è sfocatura.

In tal caso, puoi fornire una nuova larghezza / altezza per la tua bitmap per ottenere l'immagine migliore (oppure puoi aumentare la dimensione del vettore in xml, ma fornire desireWidthe desireHeightpotrebbe essere più flessibile).

private fun getBitmap(drawableId: Int, desireWidth: Int? = null, desireHeight: Int? = null): Bitmap? {
    val drawable = AppCompatResources.getDrawable(context, drawableId) ?: return null
    val bitmap = Bitmap.createBitmap(
        desireWidth ?: drawable.intrinsicWidth,
        desireHeight ?: drawable.intrinsicHeight,
        Bitmap.Config.ARGB_8888
    )
    val canvas = Canvas(bitmap)
    drawable.setBounds(0, 0, canvas.width, canvas.height)
    drawable.draw(canvas)
    return bitmap
}

Spero che sia d'aiuto


0
Drawable layerDrawable = (Drawable) imageBase.getDrawable();
Bitmap bitmap = Bitmap.createBitmap(layerDrawable.getIntrinsicWidth(),
        layerDrawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
layerDrawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
layerDrawable.draw(canvas);  
imageTeste.setImageBitmap(addGradient(bitmap));

0

Se vuoi essere in grado di ridimensionare l'output alla dimensione di output desiderata, prova il frammento seguente:

fun getBitmapFromVectorDrawable(context: Context, drawableId: Int, outputSize: OutputSize? = null): Bitmap? {
    var drawable = ContextCompat.getDrawable(context, drawableId) ?: return null
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
        drawable = DrawableCompat.wrap(drawable).mutate()
    }

    var targetBitmap: Bitmap
    if (outputSize != null) {
        targetBitmap = Bitmap.createBitmap(outputSize.width,
                outputSize.height, Bitmap.Config.ARGB_8888)
    } else {
        targetBitmap = Bitmap.createBitmap(drawable.intrinsicWidth,
            drawable.intrinsicHeight, Bitmap.Config.ARGB_8888)
    }

    val canvas = Canvas(targetBitmap)
    val scaleX =  targetBitmap.width.toFloat()/drawable.intrinsicWidth.toFloat()
    val scaleY =  targetBitmap.height.toFloat()/drawable.intrinsicHeight.toFloat()
    canvas.scale(scaleX, scaleY)
    drawable.draw(canvas)

    return targetBitmap
}

class OutputSize(val width: Int, val height: Int)

0

Questo ti dà la bitmap nella dimensione che desideri. Inoltre, consente di mantenere o meno la trasparenza a seconda di ogni immagine per prestazioni migliori con quelle che non ne hanno bisogno.

public static Bitmap drawableToBitmap(Resources res, int drawableId,
        int width, int height, boolean keepAlpha) {
    Drawable drawable = res.getDrawable(drawableId);
    Bitmap bmp = createBitmap(width, height, keepAlpha ?
            Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565);
    Canvas cvs = new Canvas(bmp);
    drawable.setBounds(0, 0, width, height);
    drawable.draw(cvs);
    return bmp;
}
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.