Android에서 뷰를 표시하지 않고 비트 맵으로 변환 하시겠습니까?


133

정확히 내가해야 할 일을 설명하려고 노력할 것입니다.

A, B, C라는 세 개의 별도 화면이 있습니다. HomeScreen이라는 또 다른 화면이 있는데, 여기에는 3 개의 화면 비트 맵이 모두 갤러리보기에 표시되고 사용자가 원하는보기를 선택할 수 있습니다.

3 개의 모든 화면의 비트 맵을 가져 와서 모든 화면을 HomeScreen 활동에만 배치하여 갤러리보기에 표시 할 수있었습니다. 자, 이것은 코드를 복잡하게 만들었고 단순화하고 싶습니다.

따라서 HomeScreen에서 다른 활동을 호출하여 표시하지 않고 해당 화면의 비트 맵을 가져올 수 있습니까? 예를 들어 방금 HomeScreen을 호출하고 Activity A, B, C를 호출하고 A, B, C의 활동이 표시되지 않는다고 가정하십시오. getDrawingCache ()에 의해 해당 화면의 비트 맵을 제공합니다. 그런 다음 해당 비트 맵을 HomeScreen의 갤러리보기에 표시 할 수 있습니다.

문제를 매우 명확하게 설명했으면합니다.

이것이 실제로 가능한지 알려주십시오.


1
확실하지는 않지만 그렇게 할 수는 없다고 생각합니다. 문제는 활동이 사용자에게 표시되어야한다는 것입니다. 활동을 시작한 다음 즉시 숨길 수 있지만 활동은 사용자에게 1 초 동안 계속 표시됩니다. 화면이 여러 번 깜박 거리면 앱이 전문적이지 않게 보일 수 있습니다. 그러나 활동을 표시하지 않고 시작하는 명령이있을 수 있습니다. 나는 존재하는 경우 하나를 모른다.
스티브 헤일리

5
실제로, 나는 이것을 할 수 있었다.
sunil

오, 어떻게 그 액티비티를 호출 할 수 있습니까?
zionpi

이 게시물에서 답변을 확인하십시오, 나는 일종의 해결책을 찾았습니다 : stackoverflow.com/questions/36424381/…
Wackaloon

답변:


213

이를 수행 할 수있는 방법이 있습니다. 비트 맵과 캔버스를 생성하고 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;
}

보기가 표시되지 않은 경우 크기가 0이됩니다. 다음과 같이 측정 할 수 있습니다.

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_CONTENTmakeMeasureSpec() (일부 뷰 클래스에서는 작동하지만) 권장되는 방법은 다음과 같습니다.

// 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());이것을 올바르게 작동 시키려면 이것을 변경해야 했지만 코드 덕분에 :)
triad

3
v.getLayoutParams (). width 대신 v.getWidth ()를 사용해야했고 높이는 비슷했습니다. 그렇지 않으면 지금 작동합니다.
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

29

여기 내 해결책이 있습니다.

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

22

이 시도,

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

20

나는 이것이 오래된 문제 일지 모른다는 것을 알고 있지만이 솔루션 중 어느 것이나 나를 위해 일하는 데 문제가있었습니다. 특히, 부풀린 후에 뷰를 변경하면 해당 변경 사항이 렌더링 된 비트 맵에 통합되지 않습니다.

다음은 내 경우에 효과가 있었던 방법입니다. 그러나 한 가지 경고가 있습니다. 전화하기 전에 getViewBitmap(View)뷰를 부풀려서 알려진 치수의 레이아웃을 요청했습니다. 내보기 레이아웃은 내용이 내부에 놓일 때까지 높이 / 너비가 0이되기 때문에 필요했습니다.

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

건배,

남자 이름


건배! 레이아웃을 변경 한 후에는 measure 및 requestLayout을 호출해야합니다.
TheIT

1
이 솔루션에 감사드립니다! 나는 같은 문제가 있었다. 내가 사용하고 있었던 measure()layout()내가 이상한 결과가 있었다, 그래서 나는 내 시야를 채워 전에. 이 전화를 아래로 옮기면 위에 createBitmap()고정됩니다!
Sven Jacobs

6

Android KTX에는 훌륭한 Kotlin 확장 기능이 있습니다. View.drawToBitmap(Bitmap.Config)


3
뷰에 레이아웃이 표시되지 않으면 작동하지 않습니다. 오류 : "IllegalStateException : drawToBitmap ()을 호출하기 전에보기를 배치해야합니다"
Val

2

도움이 되었기를 바랍니다

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


getDrawingCache()API 레벨 28에서는 업데이트 방법이 더 이상 사용되지 않습니다. 따라서 API 레벨> 28에 대한 다른 대안을 찾으십시오.


1
getDrawingCache더 이상 사용되지 않습니다.
David Miguel

1

비트 맵으로 레이아웃 또는보기 :

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

0

나는 이것이 조금 더 낫다고 생각한다.

/**
 * 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가 추가되지 않습니다. ImageView 만 추가됩니다.
Khemraj

@ Khemraj 나는 질문을 이해하지 못한다.
안드로이드 개발자

내 TextView가 비트 맵에 없었던 것은 내 잘못이었습니다. 밝은 색상 테마가 적용되었으므로 답장을 보내 주셔서 감사합니다.
Khemraj 2016 년

1
@Khemraj 죄송하지만 아직 이해가되지 않습니다. 괜찮아요?
안드로이드 개발자

네, 왜 당신이 나를 얻지 못하는지 모르겠습니다 :). 레이아웃에 하나의 TextView가 있었고 비트 맵으로 변환하려고했습니다. 레이아웃에는 하나의 ImageView와 하나의 TextView가 있습니다. ImageView가 비트 맵으로 변환되었습니다. 그러나 TextView는 비트 맵에 나타나지 않았습니다. 그것은 문제였다. 그 후 TextView 텍스트를 흰색으로 만드는 테마를 적용했습니다. 나는 그것을 고쳤다. 그리고 이제 괜찮아. 감사.
Khemraj 2016 년

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

이것이 무엇을하는지 설명하십시오.
Paul Floyd
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.