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

Posted

技术标签:

【中文标题】将视图转换为位图而不在 Android 中显示?【英文标题】:Converting a view to Bitmap without displaying it in Android? 【发布时间】:2011-02-17 13:41:39 【问题描述】:

我将尝试解释我究竟需要做什么。

我有 3 个单独的屏幕,分别是 A、B、C。还有另一个称为 HomeScreen 的屏幕,其中所有 3 个屏幕位图都应显示在画廊视图中,用户可以选择他想去哪个视图。

通过仅将所有代码放在 HomeScreen Activity 中,我已经能够获取所有 3 个屏幕的位图并将其显示在画廊视图中。现在,这使代码复杂了很多,我想简化它。

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

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

请让我知道这是否真的可行。

【问题讨论】:

我不完全确定,但我认为您将无法做到这一点。问题是活动是要显示给用户的。您可以启动该活动,然后立即将其隐藏,但该活动仍对用户可见一瞬间。它显示的时间足够长以引起注意,因此屏幕闪烁几次会使应用程序看起来不专业。但是,可能有一个命令可以启动一个活动而不显示它;我只是不知道它是否存在。 其实我可以做到的。 哦,你怎么能调用那个activity却不显示呢?我能不能以当前activity的布局为模板生成位图,同时给它提供不同的内容? 在这篇文章中查看答案,我找到了某种解决方案:***.com/questions/36424381/… 以上答案对我没有任何帮助。这仅适用于***.com/a/26086145/8554111 【参考方案1】:

有办法做到这一点。你必须创建一个位图和一个画布并调用 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;

编辑:根据this post,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();

【讨论】:

我试过了,但我得到的只是一个半透明的黑盒子。我需要在视图上做些什么来为位图绘制做好准备吗? 我实际上必须将其更改为 v.layout(v.getLeft(), v.getTop(), v.getRight(), v.getBottom()); 才能使其正常工作,但感谢代码 :) 我不得不使用 v.getWidth() 而不是 v.getLayoutParams().width 和类似的高度。否则,现在工作。 我用过v.measure(0, 0); v.getMeasuredWidth(); v.getMeasuredHeight(); Bitmap b = Bitmap.createBitmap(v.getWidth(), v.getHeight(), Bitmap.Config.ARGB_8888); 效果更好【参考方案2】:

这是我的解决方案:

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;

享受:)

【讨论】:

谢谢。如果高度超过某个值,我会在某些设备上遇到问题。我还没有完全测试,但这似乎可以解决这个问题。【参考方案3】:

我知道这可能是一个过时的问题,但我在让这些解决方案中的任何一个为我工作时遇到了问题。 具体来说,我发现如果在视图膨胀后对视图进行任何更改,这些更改将不会合并到渲染的位图中。

这是最终适用于我的案例的方法。然而,有一个警告。在致电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 才能显示 感谢您的解决方案!我有同样的问题。我在填充视图之前使用了measure()layout(),所以我得到了奇怪的结果。将这些调用向下移动,在createBitmap() 上方为我修复!【参考方案4】:

试试这个,

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

【讨论】:

如何在我的主要活动类中使用它? 已弃用【参考方案5】:

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

【讨论】:

如果视图没有出现在布局中,这将不起作用。错误:“IllegalStateException:需要在调用 drawToBitmap() 之前布局视图” 简单易行。谢谢兄弟【参考方案6】:

布局或视图到位图:

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

【讨论】:

感谢translate!它帮助我在视图中剪辑了一个区域。【参考方案7】:

希望对你有帮助

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

更新 getDrawingCache() 方法在 API 级别 28 中已弃用。因此请寻找 API 级别 > 28 的其他替代方法。

【讨论】:

getDrawingCache 目前已弃用。【参考方案8】:

我觉得这样好一点:

/**
 * 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 我不明白这个问题。 这是我的错,我的 TextView 不在位图中。因为我应用了浅色主题,谢谢回复。 @Khemraj 抱歉,我还是不明白。现在还好吗? 是的,兄弟,我不知道你为什么不找我:)。我在布局中有一个 TextView,我想在 Bitmap 中进行转换。 Layout 有一个 ImageView 和一个 TextView。 ImageView 正在转换为位图。但是 TextView 没有出现在 Bitmap 中。那是个问题。之后,我意识到我应用了一个主题,使 TextView 文本颜色为白色。我修好了它。现在一切都好。谢谢。【参考方案9】:

我几天前才用过这个:

fun generateBitmapFromView(view: View): Bitmap 
    val specWidth = View.MeasureSpec.makeMeasureSpec(1324, View.MeasureSpec.AT_MOST)
    val specHeight = View.MeasureSpec.makeMeasureSpec(521, View.MeasureSpec.AT_MOST)
    view.measure(specWidth, specHeight)
    val width = view.measuredWidth
    val height = view.measuredHeight
    val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
    val canvas = Canvas(bitmap)
    view.layout(view.left, view.top, view.right, view.bottom)
    view.draw(canvas)
    return bitmap

此代码基于此gist

【讨论】:

当我有 florent 的 shapofview 时有帮助,当硬件加速器为 true 时它不起作用【参考方案10】:
private fun getBitmapFromView(view: View): Bitmap

    val returnedBitmap = Bitmap.createBitmap(view.width,
        view.height
    ,Bitmap.Config.ARGB_8888)

    val canvas = Canvas(returnedBitmap)

    //background image
    val bgDrawable = view.background

    //background image is selected
    if (bgDrawable != null)
        bgDrawable.draw(canvas)
    
    else
        canvas.drawColor(Color.WHITE)
    
    
    view.draw(canvas)
    
    return returnedBitmap


【讨论】:

【参考方案11】:

我也有类似的需求,并根据提供的代码开发了一些东西,特别是 @Azhagthott 的代码和全新的 androidx.core.view.drawToBitmap,希望这在这里也有希望,

val view = MyCustomView(context)
view.measure(
    View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.AT_MOST),
    View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.AT_MOST)
)
view.layout(0, 0, width, height)
val bitmap = view.drawToBitmap()

【讨论】:

【参考方案12】:
view.setDrawingCacheEnabled(true);
Bitmap bitmap = Bitmap.createBitmap(view.getDrawingCache());
view.setDrawingCacheEnabled(false);

【讨论】:

请解释这是做什么的。

以上是关于将视图转换为位图而不在 Android 中显示?的主要内容,如果未能解决你的问题,请参考以下文章

Android将base64编码的字符串转换为图像视图

位图而不是视图上的波纹动画

Android:如何将整个 ImageView 转换为位图?

将 Xamarin 表单页面捕获为位图而不附加到父级

Android 位图到 Base64 字符串

android的canvas如何转换为一张bitmap(位图)