是否将视图转换为位图而不在Android中显示?


133

我将尝试解释我到底需要做什么。

我有3个独立的屏幕,分别是A,B,C。还有一个名为“主屏幕”的屏幕,其中所有三个屏幕的位图都应在“图库”视图中显示,并且用户可以选择要进入的视图。

通过将所有代码仅放置在HomeScreen Activity中,我已经能够获取所有3个屏幕的位图并将其显示在Gallery视图中。现在,这使代码变得非常复杂,我想简化一下。

因此,我可以从HomeScreen调用另一个Activity而不显示它,而只是获取该屏幕的位图。例如,假设我只调用HomeScreen,它调用了活动A,B,C,则没有显示来自A,B,C的活动。它只是通过getDrawingCache()给出了该屏幕的位图。然后我们可以在HomeScreen的Gallery视图中显示这些位图。

我希望我已经很清楚地解释了这个问题。

请让我知道这是否确实可行。


1
我不确定,但我认为您将无法做到。问题在于活动是要显示给用户的。您可以启动该活动,然后立即将其隐藏,但是该活动在瞬间仍然对用户可见。它的显示时间足以引起注意,因此屏幕多次闪烁会使该应用看起来不专业。但是,可能有一个命令可以不显示活动就开始活动。我只是不知道它是否存在。
史蒂夫·海利

5
实际上,我能够做到这一点。
sunil 2011年

哦,您怎么称呼该活动而不显示它?我可以将当​​前活动的布局作为模板来生成位图,同时向其提供不同的内容吗?
zionpi

检查这篇文章中的答案,我发现了某种解决方案:stackoverflow.com/questions/36424381/…–
Wackaloon

Answers:


212

有一种方法可以做到这一点。您必须创建一个位图和一个画布并调用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_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 2011年

4
我实际上必须更改此设置v.layout(v.getLeft(), v.getTop(), v.getRight(), v.getBottom());才能使其正常工作,但是感谢您的代码:)
triad 2012年

3
我必须使用v.getWidth()而不是v.getLayoutParams()。width以及类似的高度。否则,现在可以工作。
David Manpearl

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);效果更好
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;
}

如何在我的主要活动课程中使用它?
2014年

已弃用
Ch Vas

20

我知道这可能是一个过时的问题,但是我在获取任何一种适用于我的解决方案时遇到了问题。具体来说,我发现,如果在对视图进行夸大之后对视图进行了任何更改,则这些更改将不会合并到渲染的位图中。

这是最终为我的案例工作的方法。但是,请注意。在打电话之前,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()为我解决了!
Sven Jacobs

6

Android KTX中有一个很棒的Kotlin扩展功能: View.drawToBitmap(Bitmap.Config)


3
如果视图未在布局中显示,则此方法将不起作用。错误:“IllegalStateException异常:查看需要调用drawToBitmap(前进行布局)”
瓦尔

2

希望这可以帮助

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


getDrawingCache()在API级别28中不推荐使用更新方法。因此,请针对API级别> 28寻找其他替代方法。


1
getDrawingCache目前已弃用。
大卫·米格尔

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。仅添加ImageViews。
Khemraj19年

@Khemraj我不明白这个问题。
Android开发人员

我的错是我的TextView在位图中不存在。因为我应用了浅色主题,所以谢谢您的答复。
Khemraj19年

1
@Khemraj对不起,但我还是不明白。现在还好吗?
Android开发人员

是的,兄弟,我不知道你为什么不让我:)。我在布局中有一个TextView,想要在Bitmap中进行转换。布局具有一个ImageView和一个TextView。ImageView正在转换为Bitmap。但是TextView没有出现在Bitmap中。那是问题。之后,我意识到我应用了一个使TextView文本颜色变为白色的主题。我修好了它。现在一切都好。谢谢。
Khemraj

0

布局或视图到位图:

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.