Obteniendo mapa de bits del vector dibujable


132

En mi aplicación, tengo que configurar un ícono grande para una notificación. LargeIcon debe ser un mapa de bits, y mis dibujos son imágenes vectoriales (la nueva característica en Android, vea este enlace ) El problema es que cuando intento decodificar un recurso que es una imagen vectorial, me devuelve un valor nulo.

Aquí está la muestra de código:

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

En este ejemplo, cuando reemplazo R.drawable.vector_menu_objectifs con una imagen "normal", un png por ejemplo, el resultado no es nulo (obtengo el mapa de bits correcto) ¿Hay algo que me falta?


1
Tema similar tenido, no solución, sino una solución: stackoverflow.com/questions/33548447/...
Que

Respuestas:


231

Comprobado en 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;
}

ACTUALIZAR:

Proyecto gradle:

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

Módulo gradle:

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

Gracias. La respuesta anterior no funciona con la versión SDK a partir del 23
Paha

2
Esto funciona muy bien para mi. Se ejecuta en API 15 sin problemas
vera el

44
AppCompatDrawableManagerestá marcado como @RestrictTo(LIBRARY_GROUP)interno y no debe usarlo (su API puede cambiar sin previo aviso). Usar en su ContextCompatlugar.
mradzinski

no funciona Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.graphics.Bitmap.setHasAlpha(boolean)' on a null object referenceen esta líneaBitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
user25

55
Tuve problemas con esta solución. Para mí usando: AppCompatResources en lugar de ContextCompat lo arregló: Drawable drawable = AppCompatResources.getDrawable (context, drawableId);
Mike T

64

Puedes usar el siguiente método:

@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;
}

que a veces combino con:

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
espero que @liltof regrese y marque esto como respuesta. Una cosa a tener en cuenta es que ambos métodos quieren el envoltorio targetAPi, pero Android Studio te lo dirá.
roberto tomás

1
He pasado unos dos días tratando de hacer esto ahora pensando que es un problema de archivo svg. ¡Gracias!
sparkly_frog

1
no funciona Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.graphics.Bitmap.setHasAlpha(boolean)' on a null object referenceen esta líneaBitmap bitmap = Bitmap.createBitmap(vectorDrawable.getIntrinsicWidth(), vectorDrawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
user25

45

Si estás dispuesto a usar Android KTX para Kotlin, puede usar el método de extensión Drawable#toBitmap()para lograr el mismo efecto que las otras respuestas:

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

o

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

Para agregar este y otros métodos de extensión útiles, deberá agregar lo siguiente a su nivel de módulo build.gradle

repositories {
    google()
}

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

Ver aquí las últimas instrucciones para agregar la dependencia a su proyecto.

Tenga en cuenta que esto funcionará para cualquier subclase de Drawabley, si Drawablees un BitmapDrawable, será un atajo para usar el subyacente Bitmap.


Esta es la solución más simple aquí para Kotlin.
Adam Hurwitz

1
Para mí funciona perfectamente bien con VectorDrawable.
ubuntudroid

Esta es la mejor opción ... androidx predeterminado proporciona funcionalidad
Reshma

27

En base a las respuestas anteriores, puede simplificarse así para que coincida con VectorDrawable y BitmapDrawable y para ser compatible con al menos 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");
    }
}

Luego debe agregar su archivo gradle:

android {
    defaultConfig {
        vectorDrawables.useSupportLibrary = true
    }
}

En pre-Lollipop usará VectorDrawableCompat y en Lollipop usará VectorDrawable.

EDITAR

He editado la condición siguiendo el comentario de @ user3109468


1
instancia dibujable de VectorDrawable || La instancia dibujable de VectorDrawableCompat debe tener los lados intercambiados. Resultados en una clase no encontrada cuando VectorDrawable no existe, cuando VectorDrawableCompat se debe verificar primero porque sí existe.
Warrick

la verificación de tipo de VertorDrawable se puede describir como(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && drawable instanceof VectorDrawable)
chmin

6

Felicitaciones a @Alexey

Aquí está la Kotlinversión que usa extensiones paraContext

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
}

Ejemplo de uso en Activity:

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

1

Probado en API 16 - JellyBean con Vector Drawables

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

Use el siguiente código para convertir la imagen con la relación de aspecto correcta (por ejemplo, para el icono de notificación):

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

Si tu vectorimagen intrinsicWidthyintrinsicHeight es pequeño y se intenta mostrar el mapa de bits a una gran vista, a continuación, podrás ver el resultado es la falta de definición.

En ese caso, puede proporcionar un nuevo ancho / alto para su mapa de bits para obtener la mejor imagen (o puede aumentar el tamaño del vector en xml, pero proporcionar el desireWidthy desireHeightpuede ser más flexible).

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
}

Espero que ayude


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

Si desea poder escalar su salida al tamaño de salida deseado, pruebe el siguiente fragmento:

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

Esto le proporciona el mapa de bits en el tamaño que desee. Además, le permite mantener o no la transparencia dependiendo de cada imagen para un mejor rendimiento con aquellos que no la necesitan.

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;
}
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.