Convertendo uma visualização em Bitmap sem exibi-la no Android?


133

Vou tentar explicar exatamente o que preciso fazer.

Eu tenho três telas separadas, digamos A, B, C. Existe outra tela chamada, dizer HomeScreen, onde todos os bitmap das três telas devem ser exibidos na visualização da Galeria e o usuário pode selecionar em qual visualização deseja ir.

Consegui obter os Bitmaps de todas as três telas e exibi-los na visualização da Galeria, colocando todo o código apenas na Atividade da Tela Inicial. Agora, isso complicou muito o código e eu gostaria de simplificá-lo.

Então, posso chamar outra Atividade da Tela inicial e não exibi-la e apenas obter o Bitmap dessa tela. Por exemplo, digamos que eu apenas chamo HomeScreen e chama Atividade A, B, C e nenhuma das Atividades de A, B, C é exibida. Apenas fornece o bitmap dessa tela por getDrawingCache (). E então podemos exibir esses bitmaps na visualização da Galeria na Tela inicial.

Espero ter explicado o problema com muita clareza.

Por favor, deixe-me saber se isso é realmente possível.


1
Não tenho muita certeza, mas acho que você não será capaz de fazer isso. O problema é que as atividades devem ser exibidas ao usuário. Você pode iniciar a atividade e ocultá-la imediatamente, mas a atividade ainda estará visível para o usuário por uma fração de segundo. É mostrado o tempo suficiente para ser notado, portanto, fazer a tela piscar várias vezes faz com que o aplicativo pareça não profissional. No entanto, pode ser possível que exista um comando para iniciar uma atividade sem exibi-la; Só não sei se existe.
Steve Haley

5
Na verdade, eu fui capaz de fazer isso.
Sunil

Ah, como você pode chamar essa atividade, mas não para mostrá-la? Posso usar o layout da atividade atual como modelo para gerar bitmap enquanto alimenta conteúdo diferente?
Zionpi

Verifique a resposta neste post, encontrei algum tipo de solução: stackoverflow.com/questions/36424381/…
#

Respostas:


213

existe uma maneira de fazer isso. você precisa criar um bitmap e um canvas e chamar view.draw (canvas);

aqui está o 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;
}

se a vista não foi exibida antes do tamanho ser zero. É possível medir assim:

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

EDIT: de acordo com este post , Passar WRAP_CONTENTcomo valor para makeMeasureSpec()não faz nenhum bem (embora para algumas classes de exibição funcione), e o método recomendado é:

// 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
Eu tentei isso, mas tudo o que recebo é uma caixa preta semi transparente. Preciso fazer algo na vista para prepará-lo para o desenho de bitmap?
Bobbake4

4
Eu realmente tive que mudar isso para v.layout(v.getLeft(), v.getTop(), v.getRight(), v.getBottom());obter isso funcione corretamente, mas graças para o código :)
tríade

3
Eu tive que usar v.getWidth () em vez de v.getLayoutParams (). Width e similares para height. Caso contrário, agora trabalhando.
David Manpearl

1
Eu usei v.measure(0, 0); v.getMeasuredWidth(); v.getMeasuredHeight();.
Brais Gabin

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

29

aqui está a minha solução:

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

Aproveitar :)


Obrigado. Eu estava tendo problemas em alguns dispositivos se a altura estivesse além de um determinado valor. Eu não testei completamente, mas isso parece resolver isso.
BMF

22

Tente isso,

/**
 * 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;
}

Como eu o uso na minha classe de atividade Principal?
Si8

Isto está obsoleto #
Ch Vas

20

Sei que esse pode ser um problema antigo, mas estava com problemas para que qualquer uma dessas soluções funcionasse para mim. Especificamente, descobri que, se alguma alteração fosse feita na exibição depois de inflada, essas alterações não seriam incorporadas ao bitmap renderizado.

Aqui está o método que acabou funcionando no meu caso. Com uma ressalva, no entanto. antes de ligar, getViewBitmap(View)eu inflado minha visualização e solicitei o layout com dimensões conhecidas. Isso foi necessário, já que meu layout de exibição tornaria zero altura / largura até que o conteúdo fosse colocado 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;
}

O "molho mágico" para mim foi encontrado aqui: https://groups.google.com/forum/#!topic/android-developers/BxIBAOeTA1Q

Felicidades,

Levi


Felicidades! Parece que se deve chamar medida e requestLayout após quaisquer alterações no layout para que eles sejam exibidos
theit

1
Obrigado por esta solução! Eu tive o mesmo problema. Eu estava usando measure()e layout()antes de preencher minha opinião, obtive resultados estranhos. Mover essas chamadas para baixo, acima createBitmap()corrigido para mim!
precisa saber é o seguinte

6

Há uma excelente função de extensão Kotlin no Android KTX: View.drawToBitmap(Bitmap.Config)


3
Isso não funcionará se a exibição não for apresentada no layout. Erro: "IllegalStateException: a exibição precisa ser organizada antes de chamar drawToBitmap ()"
Val

2

Espero que isto ajude

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

O
getDrawingCache() método Update foi descontinuado no nível da API 28. Portanto, procure outra alternativa para o nível da API> 28.


1
getDrawingCacheestá atualmente obsoleto.
David Miguel

1

Layout ou visualização em bitmap:

 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 chamada:

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

0

Eu acho que isso é um pouco melhor:

/**
 * 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 o código acima, você não precisa especificar o tamanho do bitmap (use 0 para largura e altura) se quiser usar o da própria exibição.

Além disso, se você deseja converter vistas especiais (SurfaceView, Surface ou Window, por exemplo) em um bitmap, considere usar a classe PixelCopy . Requer API 24 e acima. Eu não sei como fazer isso antes.


Qualquer ideia, nenhum TextView é adicionado ao bitmap. Apenas ImageViews são adicionados.
Khemraj

@ Khemraj Eu não entendo a pergunta.
desenvolvedor android

Foi minha culpa que o TextView não estava lá no bitmap. Como eu apliquei um tema de cores claras, obrigado pela resposta.
Khemraj

1
@ Khemraj Desculpe, mas ainda não entendo. Tudo bem agora?
desenvolvedor android

Sim mano, eu não sei por que você não está me entendendo :). Eu tinha um TextView no layout que queria converter em Bitmap. O layout tinha um ImageView e um TextView. O ImageView estava sendo convertido em Bitmap. Mas o TextView não estava aparecendo no Bitmap. Isso foi problema. Depois disso, percebi que tinha um tema aplicado que tornava o texto do TextView em branco. Eu consertei isso. E tudo bem agora. Obrigado.
precisa saber é o seguinte

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

Por favor, explique o que isso faz.
Paul Floyd
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.