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?

liltof
fuente
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
    }
    ...
}
...
Alexey
fuente
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");
    }
}
snodnipper
fuente
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.

David Rawson
fuente
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

Eselfar
fuente
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)
Gunhan
fuente
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;
}   
tomasmark79
fuente
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;
}
antaki93
fuente
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

Phan Van Linh
fuente
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));
Amina
fuente
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)
Hans
fuente
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;
}
Estid Felipe Lozano Reyes
fuente