Bir görünümü Android'de görüntülemeden Bitmap'e mi dönüştürüyorsunuz?


133

Tam olarak ne yapmam gerektiğini açıklamaya çalışacağım.

A, B, C yazan 3 ayrı ekranım var. 3 ekran bitmapinin tümünün Galeri görünümünde görüntülenmesi gereken ve kullanıcının hangi görünümde gitmek istediğini seçebildiği HomeScreen denilen başka bir ekran var.

Tüm 3 ekranın Bitmap'lerini alabildim ve tüm kodu yalnızca Ana Ekran Etkinliğine yerleştirerek Galeri görünümünde görüntüledim. Şimdi, bu, kodu çok karmaşık hale getirdi ve basitleştirmek isterim.

Yani, HomeScreen'den başka bir Activity'i çağırıp görüntülemeyip sadece o ekranın Bitmap'ini alabilir miyim? Örneğin, sadece HomeScreen'i aradığımı ve Aktivite A, B, C'yi çağırdığını ve A, B, C'deki Aktivitelerin hiçbirinin görüntülenmediğini varsayalım. GetDrawingCache () ile o ekranın Bitmap'ini verir. Ve sonra bu bitmap'leri HomeScreen'deki Galeri görünümünde görüntüleyebiliriz.

Umarım sorunu çok net bir şekilde açıklamışımdır.

Lütfen bunun gerçekten mümkün olup olmadığını bana bildirin.


1
Tam olarak emin değilim ama bence bunu yapamayacaksın. Sorun, etkinliklerin kullanıcıya gösterilmesi gerektiğidir. Aktiviteyi başlatabilir ve ardından hemen gizleyebilirsiniz, ancak aktivite kullanıcı tarafından bir anlığına görünmeye devam edecektir. Fark edilecek kadar uzun süre gösterilir, bu nedenle ekranın birkaç kez titremesi uygulamanın profesyonelce görünmesine neden olur. Bununla birlikte, bir etkinliği görüntülemeden başlatmak için bir komut olması mümkün olabilir; Var olup olmadığını bilmiyorum.
Steve Haley

5
Aslında bunu yapabildim.
sunil

Oh, bu aktiviteyi nasıl çağırabilirsin ama göstermezsin? Mevcut aktivitenin düzenini şablon olarak alıp ona farklı içerik beslerken bitmap oluşturabilir miyim?
zionpi

Yanıtlar:


213

bunu yapmanın bir yolu var. bir Bitmap ve bir Canvas oluşturmanız ve view.draw (canvas) öğesini çağırmanız gerekir;

İşte kod:

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

görünüm, boyutundan önce görüntülenmediyse sıfır olacaktır. Bunu şu şekilde ölçmek mümkündür:

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

DÜZENLEME: göre bu yazı , Passing WRAP_CONTENTiçin değer olarak makeMeasureSpec()hiçbir işe için değil (bazı görünüm sınıfları için o çalışır rağmen) ve tavsiye edilen yöntemdir:

// 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
Bunu denedim ama aldığım tek şey yarı şeffaf bir kara kutu. Bitmap çizimine hazırlamak için görünümde bir şey yapmam gerekir mi?
Bobbake4

4
Bunun v.layout(v.getLeft(), v.getTop(), v.getRight(), v.getBottom());doğru çalışması için bunu değiştirmek zorunda kaldım , ama kod için teşekkürler :)
triad

3
V.getLayoutParams () yerine v.getWidth () kullanmak zorunda kaldım. Genişlik ve yükseklik için benzer. Aksi takdirde, şimdi çalışıyor.
David Manpearl

1
Kullandım v.measure(0, 0); v.getMeasuredWidth(); v.getMeasuredHeight();.
Brais Gabin

7
Bitmap b = Bitmap.createBitmap(v.getWidth(), v.getHeight(), Bitmap.Config.ARGB_8888);Daha iyi çalışıyor
Pierre

29

İşte benim çözümüm:

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

Zevk almak :)


Teşekkürler. Yükseklik belirli bir değerin üzerindeyse bazı cihazlarda sorun yaşıyordum. Tam olarak test etmedim ama bu sorunu çözüyor gibi görünüyor.
BMF

23

Bunu dene,

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

Ana aktivite sınıfımdan nasıl kullanırım?
Si8

Bu kullanımdan kaldırıldı
Ch Vas

20

Bunun eski bir sorun olabileceğini biliyorum, ancak bu çözümlerden herhangi birinin işime yaramasını sağlamakta sorun yaşıyordum. Özellikle, şişirildikten sonra görünümde herhangi bir değişiklik yapılırsa, bu değişikliklerin işlenen bitmap'e dahil edilmeyeceğini buldum.

İşte benim durumum için işe yarayan yöntem. Ancak bir uyarı ile. aramadan önce getViewBitmap(View)görüşümü şişirdim ve bilinen boyutlara sahip bir yerleşim planını istedim. Görünüm düzenim içeriğin içine yerleştirilinceye kadar sıfır yükseklik / genişlik yapacağından bu gerekliydi.

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

Benim için "sihirli sos" burada bulundu: https://groups.google.com/forum/#!topic/android-developers/BxIBAOeTA1Q

Alkış,

Levi


Şerefe! Görünüşe göre, yerleşim düzeninde herhangi bir değişiklik yapıldıktan sonra görüntülenmesi için ölçü ve requestLayout
çağrılmalıdır

1
Bu çözüm için teşekkürler! Ben de aynı sorunu yaşadım. Ben kullanıyordum measure()ve layout()ben garip sonuçlar vardı benim görünümü doldurulur önce. Bu çağrıları aşağı taşımak, yukarıda createBitmap()benim için düzeltildi!
Sven Jacobs

6

Android KTX'te harika bir Kotlin eklenti işlevi var: View.drawToBitmap(Bitmap.Config)


4
Görünüm düzende sunulmazsa bu çalışmayacaktır. Hata: "IllegalStateException: DrawToBitmap () çağrılmadan önce görünümün düzenlenmesi gerekiyor"
Val

2

Bu yardımcı olur umarım

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

Güncelleme
getDrawingCache() yöntemi, API seviyesi 28'de kullanımdan kaldırılmıştır. Bu nedenle, API seviyesi> 28 için başka bir alternatif arayın.


1
getDrawingCacheşu anda kullanımdan kaldırıldı.
David Miguel

1

Mizanpaj veya görünümden bitmap'e:

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

Arama Yöntemi:

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

0

Sanırım bu biraz daha iyi:

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

Yukarıdaki kodu kullanarak, görünümün kendisinden birini kullanmak istiyorsanız, bit eşlemin boyutunu belirtmeniz gerekmez (genişlik ve yükseklik için 0 kullanın).

Ayrıca, özel görünümleri (örneğin SurfaceView, Surface veya Window) bir bitmap'e dönüştürmek istiyorsanız, bunun yerine PixelCopy sınıfını kullanmayı düşünmelisiniz . Yine de API 24 ve üstü gerektirir. Daha önce nasıl yapacağımı bilmiyorum.


Herhangi bir fikir, bitmap'e hiçbir TextView eklenmez. Yalnızca Görüntü Görünümleri eklenir.
Khemraj

@Khemraj Soruyu anlamıyorum.
android geliştiricisi

TextView'umun bitmap'te olmaması benim hatamdı. Açık renk teması uyguladığım için yanıtınız için teşekkürler.
Khemraj

1
@Khemraj Üzgünüm ama hala anlamadım. Şimdi tamam mı?
android geliştiricisi

Evet kardeşim, beni neden anlamıyorsun bilmiyorum :). Bitmap'e dönüştürmek istediğim düzende bir TextView vardı. Layout'ta bir ImageView ve bir TextView vardı. ImageView, Bitmap'e dönüştürülüyordu. Ancak TextView, Bitmap'te görünmüyordu. Sorun buydu. Bundan sonra TextView metin rengini beyaz yapan bir tema uyguladığımı fark ettim. Onardım. Ve şimdi her şey yolunda. Teşekkürler.
Khemraj

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

Lütfen bunun ne yaptığını açıklayın.
Paul Floyd
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.