Chuyển đổi chế độ xem sang Bitmap mà không hiển thị trong Android?


133

Tôi sẽ cố gắng giải thích chính xác những gì tôi cần làm.

Tôi có 3 màn hình riêng biệt nói A, B, C. Có một màn hình khác gọi là HomeScreen, trong đó tất cả 3 màn hình bitmap sẽ được hiển thị trong chế độ xem Thư viện và người dùng có thể chọn chế độ xem mà mình muốn đi.

Tôi đã có thể lấy Bitmap của cả 3 màn hình và hiển thị nó trong chế độ xem Thư viện bằng cách chỉ đặt tất cả mã trong Hoạt động của HomeScreen. Bây giờ, điều này đã làm phức tạp mã rất nhiều và tôi muốn đơn giản hóa nó.

Vì vậy, tôi có thể gọi một Hoạt động khác từ HomeScreen và không hiển thị nó và chỉ nhận Bitmap của màn hình đó. Ví dụ: giả sử tôi chỉ gọi HomeScreen và nó gọi Hoạt động A, B, C và không có Hoạt động nào từ A, B, C được hiển thị. Nó chỉ cung cấp Bitmap của màn hình đó bằng getDrawingCache (). Và sau đó chúng ta có thể hiển thị các ảnh bitmap đó trong chế độ xem Thư viện trong HomeScreen.

Tôi hy vọng tôi đã giải thích vấn đề rất rõ ràng.

Xin vui lòng cho tôi biết nếu điều này thực sự có thể.


1
Tôi không hoàn toàn chắc chắn, nhưng tôi nghĩ bạn sẽ không thể làm điều đó. Vấn đề là các hoạt động được hiển thị cho người dùng. Bạn có thể bắt đầu hoạt động và sau đó ẩn nó ngay lập tức, nhưng hoạt động sẽ vẫn hiển thị cho người dùng trong một giây. Nó được hiển thị đủ lâu để được chú ý vì vậy việc màn hình nhấp nháy nhiều lần khiến ứng dụng trông không chuyên nghiệp. Tuy nhiên, có thể có một lệnh để bắt đầu một hoạt động mà không hiển thị nó; Tôi chỉ không biết nếu nó tồn tại.
Steve Haley

5
Trên thực tế, tôi đã có thể làm điều này.
CN

Ồ, làm thế nào bạn có thể gọi hoạt động đó nhưng không hiển thị nó? Tôi có thể lấy bố cục của hoạt động hiện tại làm mẫu để tạo bitmap trong khi cung cấp nội dung khác cho nó không?
zionpi

Kiểm tra câu trả lời trong bài đăng này, tôi đã tìm thấy một số loại giải pháp: stackoverflow.com/questions/36424381/
Kiếm

Câu trả lời:


212

có một cách để làm điều này bạn phải tạo Bitmap và Canvas và gọi view.draw (canvas);

đây là mã:

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

nếu chế độ xem không được hiển thị trước kích thước của nó sẽ bằng không. Có thể đo nó như thế này:

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: theo bài đăng này , Truyền WRAP_CONTENTgiá trị thành makeMeasureSpec()không làm gì tốt (mặc dù đối với một số lớp xem nó hoạt động) và phương thức được đề xuất là:

// 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
Tôi đã thử điều này nhưng tất cả những gì tôi nhận được là một hộp đen bán trong suốt. Tôi có cần phải làm một cái gì đó trên khung nhìn để sẵn sàng cho bản vẽ bitmap không?
Bobbake4

4
Tôi thực sự đã phải thay đổi điều này để làm v.layout(v.getLeft(), v.getTop(), v.getRight(), v.getBottom());cho nó hoạt động chính xác, nhưng cảm ơn vì mã :)
bộ ba

3
Tôi đã phải sử dụng v.getWidth () thay vì v.getLayoutParams (). Chiều rộng và tương tự cho chiều cao. Nếu không, bây giờ làm việc.
David Manpearl

1
Tôi đã sử dụng v.measure(0, 0); v.getMeasuredWidth(); v.getMeasuredHeight();.
Brais Gabin

7
Bitmap b = Bitmap.createBitmap(v.getWidth(), v.getHeight(), Bitmap.Config.ARGB_8888);Hoạt động tốt hơn
Pierre

29

đây là giải pháp của tôi:

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

Thưởng thức :)


Cảm ơn. Tôi đã gặp sự cố trên một số thiết bị nếu chiều cao vượt quá một giá trị nhất định. Tôi đã không kiểm tra đầy đủ nhưng điều này dường như để giải quyết điều đó.
BMF

22

Thử cái này,

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

Làm cách nào để sử dụng nó từ lớp hoạt động chính của tôi?
Si8

Điều này không được chấp nhận
Ch Vas

20

Tôi biết đây có thể là một vấn đề cũ, nhưng tôi đã gặp vấn đề khi có bất kỳ giải pháp nào trong số này để làm việc cho tôi. Cụ thể, tôi thấy rằng nếu có bất kỳ thay đổi nào được thực hiện cho chế độ xem sau khi bị thổi phồng thì những thay đổi đó sẽ không được tích hợp vào bitmap được hiển thị.

Đây là phương pháp đã kết thúc làm việc cho trường hợp của tôi. Với một cảnh báo, tuy nhiên. trước khi gọi getViewBitmap(View)tôi đã thổi phồng quan điểm của mình và yêu cầu nó bố trí với các kích thước đã biết. Điều này là cần thiết vì bố cục chế độ xem của tôi sẽ làm cho nó có chiều cao / chiều rộng bằng không cho đến khi nội dung được đặt bên trong.

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

"Nước sốt ma thuật" đối với tôi đã được tìm thấy ở đây: https://groups.google.com/forum/#!topic/android-developers/BxIBAOeTA1Q

Chúc mừng

Lêvi


Chúc mừng! Dường như người ta phải gọi biện pháp và requestLayout sau khi có bất kỳ thay đổi nào về cách bố trí để chúng được hiển thị
TheIT

1
Cảm ơn giải pháp này! Tôi đã từng gặp vấn đề tương tự. Tôi đã sử dụng measure()layout()trước khi tôi đưa ra quan điểm của mình vì vậy tôi đã có kết quả lạ. Di chuyển các cuộc gọi xuống, ở trên createBitmap()cố định nó cho tôi!
Sven Jacobs

6

Có một chức năng mở rộng Kotlin tuyệt vời trong Android KTX: View.drawToBitmap(Bitmap.Config)


3
Điều này sẽ không hoạt động nếu chế độ xem không được trình bày trong bố cục. Lỗi: "IllegalStateException: Chế độ xem cần được đặt ra trước khi gọi drawToBitmap ()"
Val

2

Hi vọng điêu nay co ich

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


getDrawingCache()Phương pháp cập nhật không được chấp nhận ở cấp API 28. Vì vậy, hãy tìm phương án thay thế khác cho cấp API> 28.


1
getDrawingCachehiện không được chấp nhận
David Miguel

0

Tôi nghĩ rằng điều này là tốt hơn một chút:

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

Sử dụng mã ở trên, bạn không phải chỉ định kích thước của bitmap (sử dụng 0 cho chiều rộng và chiều cao) nếu bạn muốn sử dụng chính chế độ xem.

Ngoài ra, nếu bạn muốn chuyển đổi các chế độ xem đặc biệt (ví dụ SurfaceView, Surface hoặc Window) sang bitmap, bạn nên xem xét sử dụng lớp PixelCopy thay thế. Nó yêu cầu API 24 trở lên mặc dù. Tôi không biết làm thế nào trước đây.


Bất kỳ ý tưởng, không có TextView được thêm vào trong bitmap. Chỉ ImageViews được thêm vào.
Khemraj

@Khemraj Tôi không hiểu câu hỏi.
nhà phát triển Android

Đó là lỗi của tôi, TextView của tôi không có trong bitmap. Bởi vì tôi đã có một chủ đề màu sáng được áp dụng, cảm ơn đã trả lời.
Khemraj

1
@Khemraj Xin lỗi nhưng tôi vẫn không hiểu. Bây giờ tất cả ổn chứ?
nhà phát triển Android

Vâng, tôi không biết tại sao bạn không hiểu tôi :). Tôi đã có một TextView trong bố cục mà tôi muốn chuyển đổi trong Bitmap. Bố cục có một ImageView và một TextView. ImageView đã được chuyển đổi thành Bitmap. Nhưng TextView không xuất hiện trong Bitmap. Đó là vấn đề. Sau đó, tôi nhận ra rằng tôi có một chủ đề được áp dụng là làm cho văn bản TextView có màu trắng. Tôi sửa nó rồi. Và tất cả đều ổn bây giờ. Cảm ơn.
Khemraj

0

Bố cục hoặc xem để 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;
}

Phương thức gọi:

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

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

Hãy giải thích những gì nó làm.
Paul Floyd
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.