¿Convertir una vista a Bitmap sin mostrarla en Android?


133

Trataré de explicar qué es exactamente lo que tengo que hacer.

Tengo 3 pantallas separadas que dicen A, B, C. Hay otra pantalla llamada say HomeScreen donde las 3 pantallas de mapa de bits deben mostrarse en la vista Galería y el usuario puede seleccionar a qué vista quiere ir.

He podido obtener los mapas de bits de las 3 pantallas y mostrarlo en la vista Galería colocando todo el código solo en la Actividad de la pantalla de inicio. Ahora, esto ha complicado mucho el código y me gustaría simplificarlo.

Entonces, ¿puedo llamar a otra Actividad desde HomeScreen y no mostrarla y solo obtener el Bitmap de esa pantalla? Por ejemplo, supongamos que llamo a HomeScreen y llama a la Actividad A, B, C y no se muestra ninguna de las Actividades de A, B, C. Simplemente proporciona el mapa de bits de esa pantalla mediante getDrawingCache (). Y luego podemos mostrar esos mapas de bits en la vista Galería en la pantalla de inicio.

Espero haber explicado el problema muy claramente.

Por favor, avíseme si esto es realmente posible.


1
No estoy completamente seguro, pero creo que no podrás hacerlo. El problema es que las actividades están destinadas a mostrarse al usuario. Puede iniciar la actividad y luego ocultarla de inmediato, pero la actividad seguirá siendo visible para el usuario durante una fracción de segundo. Se muestra el tiempo suficiente para que se note, por lo que el parpadeo de la pantalla varias veces hace que la aplicación parezca poco profesional. Sin embargo, es posible que haya un comando para iniciar una actividad sin mostrarla; Simplemente no sé de uno si existe.
Steve Haley

55
En realidad, pude hacer esto.
sunil

Oh, ¿cómo se puede llamar a esa actividad pero no para mostrarla? ¿Puedo tomar el diseño de la actividad actual como plantilla para generar un mapa de bits mientras se le proporciona contenido diferente?
zionpi

Compruebe la respuesta en esta publicación, encontré algún tipo de solución: stackoverflow.com/questions/36424381/…
Wackaloon

Respuestas:


213

hay una forma de hacer esto. debe crear un mapa de bits y un lienzo y llamar a view.draw (lienzo);

Aquí está el código:

public static Bitmap loadBitmapFromView(View v) {
    Bitmap b = Bitmap.createBitmap( v.getLayoutParams().width, v.getLayoutParams().height, Bitmap.Config.ARGB_8888);                
    Canvas c = new Canvas(b);
    v.layout(v.getLeft(), v.getTop(), v.getRight(), v.getBottom());
    v.draw(c);
    return b;
}

si la vista no se mostró antes de que su tamaño sea cero. Es posible medirlo así:

if (v.getMeasuredHeight() <= 0) {
    v.measure(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
    Bitmap b = Bitmap.createBitmap(v.getMeasuredWidth(), v.getMeasuredHeight(), Bitmap.Config.ARGB_8888);
    Canvas c = new Canvas(b);
    v.layout(0, 0, v.getMeasuredWidth(), v.getMeasuredHeight());
    v.draw(c);
    return b;
}

EDITAR: según esta publicación , pasar WRAP_CONTENTcomo valor a makeMeasureSpec()no sirve para nada (aunque para algunas clases de vista funciona), y el método recomendado es:

// Either this
int specWidth = MeasureSpec.makeMeasureSpec(parentWidth, MeasureSpec.AT_MOST);
// Or this
int specWidth = MeasureSpec.makeMeasureSpec(0 /* any */, MeasureSpec.UNSPECIFIED);
view.measure(specWidth, specWidth);
int questionWidth = view.getMeasuredWidth();

1
Intenté esto, pero todo lo que obtengo es una caja negra semitransparente. ¿Necesito hacer algo en la vista para prepararlo para el dibujo de mapa de bits?
Bobbake4

44
De hecho, tuve que cambiar esto para v.layout(v.getLeft(), v.getTop(), v.getRight(), v.getBottom());que funcione correctamente, pero gracias por el código :)
tríada

3
Tuve que usar v.getWidth () en lugar de v.getLayoutParams (). Width y similar para height. De lo contrario, ahora trabajando.
David Manpearl

1
He utilizado v.measure(0, 0); v.getMeasuredWidth(); v.getMeasuredHeight();.
Brais Gabin

77
Bitmap b = Bitmap.createBitmap(v.getWidth(), v.getHeight(), Bitmap.Config.ARGB_8888);Funciona mejor
Pierre

29

Aquí está mi solución:

public static Bitmap getBitmapFromView(View view) {
    Bitmap returnedBitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(),Bitmap.Config.ARGB_8888);
    Canvas canvas = new Canvas(returnedBitmap);
    Drawable bgDrawable =view.getBackground();
    if (bgDrawable!=null) 
        bgDrawable.draw(canvas);
    else 
        canvas.drawColor(Color.WHITE);
    view.draw(canvas);
    return returnedBitmap;
}

Disfruta :)


Gracias. Estaba teniendo problemas en algunos dispositivos si la altura superaba un cierto valor. No lo he probado completamente, pero esto parece resolver eso.
BMF

22

Prueba esto,

/**
 * Draw the view into a bitmap.
 */
public static Bitmap getViewBitmap(View v) {
    v.clearFocus();
    v.setPressed(false);

    boolean willNotCache = v.willNotCacheDrawing();
    v.setWillNotCacheDrawing(false);

    // Reset the drawing cache background color to fully transparent
    // for the duration of this operation
    int color = v.getDrawingCacheBackgroundColor();
    v.setDrawingCacheBackgroundColor(0);

    if (color != 0) {
        v.destroyDrawingCache();
    }
    v.buildDrawingCache();
    Bitmap cacheBitmap = v.getDrawingCache();
    if (cacheBitmap == null) {
        Log.e(TAG, "failed getViewBitmap(" + v + ")", new RuntimeException());
        return null;
    }

    Bitmap bitmap = Bitmap.createBitmap(cacheBitmap);

    // Restore the view
    v.destroyDrawingCache();
    v.setWillNotCacheDrawing(willNotCache);
    v.setDrawingCacheBackgroundColor(color);

    return bitmap;
}

¿Cómo lo uso de mi clase de actividad principal?
Si8

Esto está en desuso
Ch Vas

20

Sé que esto puede ser un problema rancio, pero estaba teniendo problemas para que alguna de estas soluciones funcione para mí. Específicamente, descubrí que si se realizaban cambios en la vista después de inflarlos, esos cambios no se incorporarían al mapa de bits representado.

Aquí está el método que terminó funcionando para mi caso. Con una advertencia, sin embargo. antes de llamar getViewBitmap(View), inflé mi vista y le pedí que diseñara con dimensiones conocidas. Esto era necesario ya que mi diseño de vista lo haría cero altura / ancho hasta que el contenido se colocara dentro.

View view = LayoutInflater.from(context).inflate(layoutID, null);
//Do some stuff to the view, like add an ImageView, etc.
view.layout(0, 0, width, height);

Bitmap getViewBitmap(View view)
{
    //Get the dimensions of the view so we can re-layout the view at its current size
    //and create a bitmap of the same size 
    int width = view.getWidth();
    int height = view.getHeight();

    int measuredWidth = View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY);
    int measuredHeight = View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY);

    //Cause the view to re-layout
    view.measure(measuredWidth, measuredHeight);
    view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());

    //Create a bitmap backed Canvas to draw the view into
    Bitmap b = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
    Canvas c = new Canvas(b);

    //Now that the view is laid out and we have a canvas, ask the view to draw itself into the canvas
    view.draw(c);

    return b;
}

La "salsa mágica" para mí se encontró aquí: https://groups.google.com/forum/#!topic/android-developers/BxIBAOeTA1Q

Salud,

Levi


¡Salud! Parece que uno debe llamar a measure y requestLayout después de cualquier cambio en el diseño para que se muestren
TheIT

1
¡Gracias por esta solución! Yo tuve el mismo problema. Estaba usando measure()y layout()antes de llenar mi vista, así que tuve resultados extraños. Mover estas llamadas hacia abajo, arriba lo createBitmap()arregló para mí!
Sven Jacobs el

6

Hay una gran función de extensión de Kotlin en Android KTX: View.drawToBitmap(Bitmap.Config)


3
Esto no funcionará si la vista no se presenta en el diseño. Error: "IllegalStateException: la vista debe establecerse antes de llamar a drawToBitmap ()"
Val

2

Espero que esto ayude

View view="some view instance";        
view.setDrawingCacheEnabled(true);
Bitmap bitmap=view.getDrawingCache();
view.setDrawingCacheEnabled(false);

El
getDrawingCache() método de actualización está en desuso en el nivel de API 28. Por lo tanto, busque otra alternativa para el nivel de API> 28.


1
getDrawingCacheActualmente está en desuso.
David Miguel

1

Diseño o vista al mapa de bits:

 private Bitmap createBitmapFromLayout(View tv) {      
    int spec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
    tv.measure(spec, spec);
    tv.layout(0, 0, tv.getMeasuredWidth(), tv.getMeasuredHeight());
    Bitmap b = Bitmap.createBitmap(tv.getMeasuredWidth(), tv.getMeasuredWidth(),
            Bitmap.Config.ARGB_8888);
    Canvas c = new Canvas(b);
    c.translate((-tv.getScrollX()), (-tv.getScrollY()));
    tv.draw(c);
    return b;
}

Método de llamada:

Bitmap src = createBitmapFromLayout(View.inflate(this, R.layout.sample, null)/* or pass your view object*/);

0

Creo que esto es un poco mejor:

/**
 * draws the view's content to a bitmap. code initially based on :
 * http://nadavfima.com/android-snippet-inflate-a-layout-draw-to-a-bitmap/
 */
@Nullable
public static Bitmap drawToBitmap(final View viewToDrawFrom, int width, int height) {
    boolean wasDrawingCacheEnabled = viewToDrawFrom.isDrawingCacheEnabled();
    if (!wasDrawingCacheEnabled)
        viewToDrawFrom.setDrawingCacheEnabled(true);
    if (width <= 0 || height <= 0) {
        if (viewToDrawFrom.getWidth() <= 0 || viewToDrawFrom.getHeight() <= 0) {
            viewToDrawFrom.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
            width = viewToDrawFrom.getMeasuredWidth();
            height = viewToDrawFrom.getMeasuredHeight();
        }
        if (width <= 0 || height <= 0) {
            final Bitmap bmp = viewToDrawFrom.getDrawingCache();
            final Bitmap result = bmp == null ? null : Bitmap.createBitmap(bmp);
            if (!wasDrawingCacheEnabled)
                viewToDrawFrom.setDrawingCacheEnabled(false);
            return result;
        }
        viewToDrawFrom.layout(0, 0, width, height);
    } else {
        viewToDrawFrom.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
        viewToDrawFrom.layout(0, 0, viewToDrawFrom.getMeasuredWidth(), viewToDrawFrom.getMeasuredHeight());
    }
    final Bitmap drawingCache = viewToDrawFrom.getDrawingCache();
    final Bitmap bmp = ThumbnailUtils.extractThumbnail(drawingCache, width, height);
    final Bitmap result = bmp == null || bmp != drawingCache ? bmp : Bitmap.createBitmap(bmp);
    if (!wasDrawingCacheEnabled)
        viewToDrawFrom.setDrawingCacheEnabled(false);
    return result;
}

Usando el código anterior, no tiene que especificar el tamaño del mapa de bits (use 0 para ancho y alto) si desea usar el de la vista en sí.

Además, si desea convertir vistas especiales (SurfaceView, Surface o Window, por ejemplo) en un mapa de bits, debería considerar usar la clase PixelCopy en su lugar. Sin embargo, requiere API 24 y superior. No sé cómo hacerlo antes.


Cualquier idea, no se agrega TextView en mapa de bits. Solo se agregan ImageViews.
Khemraj

@Khemraj No entiendo la pregunta.
Desarrollador de Android

Fue mi culpa que mi TextView no estuviera allí en el mapa de bits. Debido a que me aplicaron un tema de color claro, gracias por responder.
Khemraj

1
@Khemraj Lo siento pero aún no entiendo. Todo bien ahora?
Desarrollador de Android

Sí hermano, no sé por qué no me estás recibiendo :). Tenía un TextView en el diseño que quería convertir en Bitmap. El diseño tenía un ImageView y un TextView. ImageView se estaba convirtiendo en Bitmap. Pero TextView no aparecía en Bitmap. Eso fue un problema. Después de eso, me di cuenta de que tenía un tema aplicado que hacía que el texto de TextView fuera de color blanco. Lo arreglé. Y todo bien ahora. Gracias.
Khemraj

-3
view.setDrawingCacheEnabled(true);
Bitmap bitmap = Bitmap.createBitmap(view.getDrawingCache());
view.setDrawingCacheEnabled(false);

Por favor explique qué hace esto.
Paul Floyd
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.