การแปลงมุมมองเป็น Bitmap โดยไม่แสดงใน Android?


137

ฉันจะพยายามอธิบายว่าฉันต้องทำอะไร

ฉันมี 3 หน้าจอแยกกันว่า A, B, C มีหน้าจออื่นที่เรียกว่า HomeScreen ซึ่งควรแสดงบิตแมปทั้ง 3 หน้าจอในมุมมองแกลเลอรีและผู้ใช้สามารถเลือกได้ว่าต้องการไปที่มุมมองใด

ฉันสามารถรับบิตแมปของทั้ง 3 หน้าจอและแสดงในมุมมองแกลเลอรีโดยวางรหัสทั้งหมดในกิจกรรมหน้าจอหลักเท่านั้น ตอนนี้โค้ดมีความซับซ้อนมากและฉันต้องการทำให้มันง่ายขึ้น

ดังนั้นฉันสามารถเรียกใช้กิจกรรมอื่นจากหน้าจอหลักและไม่แสดงและรับ Bitmap ของหน้าจอนั้นได้ ตัวอย่างเช่นสมมติว่าฉันเรียกว่า HomeScreen และเรียกว่ากิจกรรม A, B, C และจะไม่มีการแสดงกิจกรรมใด ๆ จาก A, B, C เพียงแค่ให้ Bitmap ของหน้าจอนั้นโดย getDrawingCache () จากนั้นเราสามารถแสดงบิตแมปเหล่านั้นในมุมมองแกลเลอรีในหน้าจอหลัก

ฉันหวังว่าฉันจะอธิบายปัญหาได้ชัดเจนมาก

โปรดแจ้งให้เราทราบหากเป็นไปได้จริง


1
ฉันไม่แน่ใจทั้งหมด แต่ฉันคิดว่าคุณจะไม่สามารถทำเช่นนั้นได้ ปัญหาคือกิจกรรมต่างๆถูกกำหนดให้แสดงต่อผู้ใช้ คุณสามารถเริ่มกิจกรรมแล้วซ่อนได้ทันที แต่ผู้ใช้จะยังมองเห็นกิจกรรมได้ภายในเสี้ยววินาที แสดงนานพอที่จะสังเกตเห็นได้ดังนั้นการที่หน้าจอกะพริบหลาย ๆ ครั้งทำให้แอปดูไม่เป็นมืออาชีพ อย่างไรก็ตามอาจเป็นไปได้ว่ามีคำสั่งให้เริ่มกิจกรรมโดยไม่ต้องแสดง ฉันไม่รู้ว่ามันมีอยู่จริงหรือเปล่า
Steve Haley

5
ที่จริงฉันสามารถทำได้
sunil

โอ้คุณจะเรียกกิจกรรมนั้น แต่ไม่แสดงได้อย่างไรฉันสามารถใช้เค้าโครงของกิจกรรมปัจจุบันเป็นเทมเพลตเพื่อสร้างบิตแมปขณะป้อนเนื้อหาที่แตกต่างกันได้หรือไม่
zionpi

ตรวจสอบคำตอบในโพสต์นี้ฉันพบวิธีแก้ปัญหาบางอย่าง: stackoverflow.com/questions/36424381/…
Wackaloon

คำตอบ:


215

มีวิธีการทำเช่นนี้ คุณต้องสร้าง Bitmap และ Canvas และ call view.draw (canvas);

นี่คือรหัส:

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

หากมุมมองไม่แสดงก่อนขนาดจะเป็นศูนย์ เป็นไปได้ที่จะวัดได้ดังนี้:

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

แก้ไข: ตามที่โพสต์นี้ , การส่งผ่านWRAP_CONTENTเป็นค่าที่จะmakeMeasureSpec()ไม่ได้ที่จะทำดี ๆ ( แต่สำหรับมุมมองการเรียนบางอย่างมันไม่ทำงาน) และวิธีการที่แนะนำคือ:

// 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
ฉันลองสิ่งนี้ แต่สิ่งที่ฉันได้รับคือกล่องสีดำกึ่งโปร่งใส ฉันจำเป็นต้องทำอะไรบางอย่างในมุมมองเพื่อให้พร้อมสำหรับการวาดบิตแมปหรือไม่
Bobbake4

4
ฉันต้องเปลี่ยนสิ่งนี้เพื่อv.layout(v.getLeft(), v.getTop(), v.getRight(), v.getBottom());ให้สิ่งนี้ทำงานได้อย่างถูกต้อง แต่ขอบคุณสำหรับรหัส :)
สาม

4
ฉันต้องใช้ v.getWidth () แทน v.getLayoutParams () ความกว้างและความสูงที่คล้ายกัน ไม่งั้นตอนนี้ทำงาน
David Manpearl

1
ฉันใช้v.measure(0, 0); v.getMeasuredWidth(); v.getMeasuredHeight();.
Brais Gabin

7
Bitmap b = Bitmap.createBitmap(v.getWidth(), v.getHeight(), Bitmap.Config.ARGB_8888);ทำงานได้ดีขึ้น
Pierre

31

นี่คือทางออกของฉัน:

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

สนุก :)


ขอบคุณ. ฉันประสบปัญหากับอุปกรณ์บางอย่างหากความสูงเกินค่าที่กำหนด ฉันยังไม่ได้ทดสอบอย่างเต็มที่ แต่ดูเหมือนจะแก้ปัญหาได้
BMF

23

ลองสิ่งนี้

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

ฉันจะใช้มันจากชั้นกิจกรรมหลักของฉันได้อย่างไร
Si8

สิ่งนี้เลิกใช้แล้ว
Ch Vas

22

ฉันรู้ว่านี่อาจเป็นปัญหาค้าง แต่ฉันมีปัญหาในการหาวิธีแก้ปัญหาเหล่านี้ให้ได้ผล โดยเฉพาะอย่างยิ่งฉันพบว่าหากมีการเปลี่ยนแปลงใด ๆ กับมุมมองหลังจากที่มันสูงเกินจริงการเปลี่ยนแปลงเหล่านั้นจะไม่รวมอยู่ในบิตแมปที่แสดงผล

นี่คือวิธีการที่ได้ผลสำหรับกรณีของฉัน ด้วยข้อแม้หนึ่งข้ออย่างไรก็ตาม ก่อนที่จะเรียกgetViewBitmap(View)ฉันทำให้มุมมองของฉันสูงขึ้นและขอให้จัดวางด้วยมิติข้อมูลที่ทราบ สิ่งนี้จำเป็นเนื่องจากเค้าโครงมุมมองของฉันจะทำให้ความสูง / ความกว้างเป็นศูนย์จนกว่าเนื้อหาจะถูกวางไว้ข้างใน

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

พบ "ซอสวิเศษ" สำหรับฉันที่นี่: https://groups.google.com/forum/#!topic/android-developers/BxIBAOeTA1Q

ไชโย

ลีวาย


ไชโย! ดูเหมือนว่าจะต้องเรียกใช้การวัดผลและ requestLayout หลังจากการเปลี่ยนแปลงใด ๆ ในรูปแบบเพื่อให้แสดง
TheIT

1
ขอบคุณสำหรับการแก้ปัญหานี้! ผมมีปัญหาเหมือนกัน. ฉันใช้measure()และlayout()ก่อนที่จะเติมข้อมูลมุมมองของฉันดังนั้นฉันจึงได้ผลลัพธ์ที่แปลก การย้ายสายเหล่านี้ลงด้านบนcreateBitmap()แก้ไขให้ฉัน!
Sven Jacobs

9

มีฟังก์ชั่นขยาย Kotlin ที่ยอดเยี่ยมใน Android KTX: View.drawToBitmap(Bitmap.Config)


7
สิ่งนี้จะใช้ไม่ได้หากไม่มีการนำเสนอมุมมองในเค้าโครง ข้อผิดพลาด: "IllegalStateException: ดูความต้องการที่จะออกมาวางก่อนที่จะเรียก drawToBitmap ()"
วาล

3

เค้าโครงหรือดูบิตแมป:

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

วิธีการโทร:

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

2

หวังว่านี่จะช่วยได้

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


getDrawingCache()เลิกใช้วิธีการอัปเดตใน API ระดับ 28 ดังนั้นให้มองหาทางเลือกอื่นสำหรับระดับ API> 28


2
getDrawingCacheปัจจุบันเลิกใช้งานแล้ว
David Miguel

1

ฉันคิดว่ามันดีกว่าเล็กน้อย:

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

การใช้โค้ดด้านบนคุณไม่จำเป็นต้องระบุขนาดของบิตแมป (ใช้ 0 สำหรับความกว้างและความสูง) หากคุณต้องการใช้มุมมองใดมุมมอง

นอกจากนี้หากคุณต้องการแปลงมุมมองพิเศษ (เช่น SurfaceView, Surface หรือ Window) เป็นบิตแมปคุณควรพิจารณาใช้คลาสPixelCopyแทน ต้องใช้ API 24 ขึ้นไปแม้ว่า เมื่อก่อนไม่รู้จะทำยังไง


ความคิดใด ๆ ไม่มีการเพิ่ม TextView ในบิตแมป เพิ่มเฉพาะ ImageViews
เขมราช

@ เขมราชไม่เข้าใจคำถาม
นักพัฒนา android

เป็นความผิดของฉัน TextView ของฉันไม่มีในบิตแมป เนื่องจากฉันใช้ชุดรูปแบบสีอ่อนขอบคุณสำหรับการตอบกลับ
เขมราช

1
@ เขมราชขออภัยยังไม่เข้าใจ ตกลงตอนนี้?
นักพัฒนา Android

ใช่ฉันไม่รู้ว่าทำไมคุณไม่ได้รับฉัน :) ฉันมี TextView หนึ่งรายการในเลย์เอาต์ซึ่งฉันต้องการแปลงเป็น Bitmap Layout มีหนึ่ง ImageView และหนึ่ง TextView ImageView กำลังถูกแปลงเป็น Bitmap แต่ TextView ไม่ปรากฏใน Bitmap นั่นคือปัญหา หลังจากนั้นฉันก็รู้ว่าฉันใช้ธีมที่ทำให้ข้อความ TextView เป็นสีขาว ฉันซ่อมมัน. และตกลงตอนนี้ ขอบคุณ.
เขมราช

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.