ビューをAndroidに表示せずにビットマップに変換しますか?


133

私が正確に何をする必要があるかを説明しようとします。

A、B、Cの3つの個別の画面があります。say HomeScreenと呼ばれる別の画面があり、3つの画面すべてのビットマップがギャラリービューに表示され、ユーザーはどのビューで移動したいかを選択できます。

3つの画面すべてのビットマップを取得し、すべてのコードをHomeScreenアクティビティのみに配置することで、それをギャラリービューに表示できました。今、これはコードをかなり複雑にしました、そして私はそれを単純化したいと思います。

したがって、HomeScreenから別のアクティビティを呼び出して表示せずに、その画面のビットマップを取得できますか?たとえば、HomeScreenを呼び出すだけで、アクティビティA、B、Cが呼び出され、A、B、Cのアクティビティは何も表示されないとします。getDrawingCache()によってその画面のビットマップを提供するだけです。次に、これらのビットマップをホーム画面のギャラリービューに表示できます。

問題を非常に明確に説明できたと思います。

これが実際に可能かどうか教えてください。


1
完全にはわかりませんが、あなたにはそれができないと思います。問題は、アクティビティがユーザーに表示されることを意図していることです。アクティビティを開始してすぐに非表示にすることができますが、アクティビティはユーザーに一瞬表示されます。気付くまで十分に長く表示されるため、画面が数回ちらつくと、アプリが専門外のように見えます。ただし、アクティビティを表示せずに開始するコマンドがある可能性があります。存在するかどうかはわかりません。
スティーブヘイリー

5
実際、私はこれを行うことができました。
sunil '23年

ああ、どうやってそのアクティビティを呼び出してそれを表示しないのですか?現在のアクティビティのレイアウトをテンプレートとして使用して、さまざまなコンテンツをフィードしながらビットマップを生成できますか?
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;
}

サイズがゼロになる前にビューが表示されなかった場合 このように測定することは可能です:

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());これを正しく機能させるには、実際にこれを変更する必要がありましたが、コードに感謝します:)
トライアド

3
高さについては、v.getLayoutParams()。widthなどの代わりにv.getWidth()を使用する必要がありました。それ以外の場合は、現在機能しています。
David Manpearl 2012

1
使用しましたv.measure(0, 0); v.getMeasuredWidth(); v.getMeasuredHeight();
Brais Gabin 2013年

7
Bitmap b = Bitmap.createBitmap(v.getWidth(), v.getHeight(), Bitmap.Config.ARGB_8888);うまく機能する
ピエール

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 2013年

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、2014

これは非推奨
Ch Vas

20

これは古い問題である可能性があることは知っていますが、これらの解決策のいずれかが機能するようにするのに問題がありました。具体的には、インフレーション後にビューに変更を加えると、それらの変更がレンダリングされたビットマップに組み込まれないことがわかりました。

これが私のケースでうまくいった方法です。ただし、注意点が1つあります。電話をかける前に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

乾杯、

レヴィ


乾杯!レイアウトを変更した後、それらを表示するには、measureとrequestLayoutを呼び出す必要があるようです
TheIT

1
この解決策をありがとう!私も同じ問題を抱えていました。私が使用measure()layout()ていて、ビューを作成する前に奇妙な結果が出ました。これらの呼び出しを下に移動すると、上でcreateBitmap()修正されます!
スベンジェイコブス

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

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

上記のコードを使用すると、ビュー自体の1つを使用する場合、ビットマップのサイズを指定する必要はありません(width&heightに0を使用)。

また、特別なビュー(SurfaceView、Surface、Windowなど)をビットマップに変換する場合は、代わりにPixelCopyクラスの使用を検討する必要があります。ただし、API 24以降が必要です。以前はどうすればいいのかわかりません。


どんな考えでも、TextViewはビットマップに追加されません。ImageViewsのみが追加されます。
ケムラジ

@Khemraj私は質問を理解していません。
アンドロイド開発者

ビットマップにTextViewがなかったのは私のせいでした。淡い色のテーマを適用していただきましたので、お返事ありがとうございます。
ケムラジ

1
@Khemraj申し訳ありませんが、それでもわかりません。大丈夫ですか?
Android開発者、

はい、仲間です。なぜあなたが私を取得していないのかわかりません:)。Bitmapで変換したいレイアウトのTextViewが1つありました。レイアウトには1つのImageViewと1つのTextViewがありました。ImageViewはビットマップに変換されていました。しかし、TextViewはビットマップに表示されませんでした。それが問題でした。その後、TextViewのテキストを白くするというテーマが適用されていることに気付きました。それを私が直した。そして今は大丈夫です。ありがとう。
ケムラジ

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.