Android对View进行截图

本文介绍了如何在Android中使用View的getDrawingCache()方法来截图,并展示了对LinearLayout、ImageView和TextView截图的示例。核心代码涉及视图的尺寸、背景色、缓存计算以及Canvas的使用。当view尺寸超出屏幕分辨率时,可能会导致空指针问题,解决方案是通过Canvas直接绘制Bitmap。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在某些特殊场合需要对View进行截图,使用View里面的getDrawingCache()方法,返回一个Bitmap对象,就可以实现截图的功能。

我们先看一个简单的示例,分别点击三个按钮进行截图,获取到的Bitmap放到下面一个ImageView上面显示,效果图如下:
1.原图
原图
2.对LinearLayout里面的内容(ImageView+TextView)截图
LinearLayout里面的内容截图
3.对ImageView截图
对ImageView截图
4.对TextView截图
TextView截图

代码比较简单:

package com.li.testsnapshot;

import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends Activity implements View.OnClickListener{

    private LinearLayout llContainer;
    private ImageView ivTest;	//示例测试图片
    private TextView tvHint;	//测试文本

    private Button btnSnap;
    private Button btnImgSnap;
    private Button btnTVSnap;

    private ImageView ivShow;
    private Button btnReset;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        initViews();
    }

    private void initViews() {
        llContainer = (LinearLayout) findViewById(R.id.llContainer);
        ivTest = (ImageView) findViewById(R.id.ivTest);
        tvHint = findViewById(R.id.tvHint);

        btnSnap = (Button) findViewById(R.id.btnSnap);
        btnImgSnap = (Button) findViewById(R.id.btnImgSnap);
        btnTVSnap = (Button) findViewById(R.id.btnTVSnap);

        ivShow = (ImageView) findViewById(R.id.ivShow);
        btnReset = (Button) findViewById(R.id.btnReset);

        btnSnap.setOnClickListener(this);
        btnImgSnap.setOnClickListener(this);
        btnTVSnap.setOnClickListener(this);
        btnReset.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()){
            case R.id.btnSnap:
                // 针对Layout整体(ImageView + TextView)截图
                testViewSnapshot(llContainer);
                break;
            case R.id.btnImgSnap:
                // 针对ImageView截图
                testViewSnapshot(ivTest);
                break;
            case R.id.btnTVSnap:
                // 针对TextView截图
                testViewSnapshot(tvHint);
                break;
            case R.id.btnReset:
                // 清除
                reset();
                break;
            default:
                break;
        }
    }

    /**
     * 对View进行截图
     */
    private void testViewSnapshot(View view) {
        //使控件可以进行缓存
        view.setDrawingCacheEnabled(true);
        //获取缓存的 Bitmap
        **Bitmap drawingCache = view.getDrawingCache();**
        //复制获取的 Bitmap
        drawingCache = Bitmap.createBitmap(drawingCache);
        //关闭视图的缓存
        view.setDrawingCacheEnabled(false);

        if (drawingCache != null) {
            ivShow.setImageBitmap(drawingCache);
            Toast.makeText(this, "成功", Toast.LENGTH_SHORT).show();
        } else {
            Toast.makeText(this, "失败", Toast.LENGTH_SHORT).show();
        }
    }

    /**
     * 设置默认显示图片
     */
    private void reset() {
        ivShow.setImageResource(R.mipmap.ic_launcher);
    }
}

布局文件:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="com.li.testsnapshot.MainActivity">

    <LinearLayout
        android:id="@+id/llContainer"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:gravity="center_horizontal"
        android:layout_gravity="center_horizontal"
        android:orientation="vertical">

        <ImageView
            android:id="@+id/ivTest"
            android:layout_width="150dp"
            android:layout_height="150dp"
            android:src="@mipmap/money"/>

        <TextView
            android:id="@+id/tvHint"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="测试截图"/>

    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="15dp"
        android:gravity="center_horizontal"
        android:layout_gravity="center_horizontal"
        android:orientation="horizontal">

        <Button
            android:id="@+id/btnSnap"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="整体截图"/>

        <Button
            android:id="@+id/btnImgSnap"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="图片截图"/>

        <Button
            android:id="@+id/btnTVSnap"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="文本截图"/>
    </LinearLayout>



    <ImageView
        android:id="@+id/ivShow"
        android:layout_width="200dp"
        android:layout_height="200dp"
        android:layout_marginTop="15dp"
        android:layout_gravity="center_horizontal"
        android:src="@mipmap/ic_launcher"/>

    <Button
        android:id="@+id/btnReset"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="15dp"
        android:layout_gravity="center_horizontal"
        android:text="清除"/>

</LinearLayout>

这些截图针对的只是一些普通的控件,至于ScrollView,WebView或者SurfaceView等比较复杂的控件,感兴趣的同学可以自己测试一下。

核心是获取缓存的Bitmap。对于getDrawingCache()方法,会调用到View里面的buildDrawingCacheImpl(boolean autoScale)方法,这段代码是核心代码。

    /**
     * private, internal implementation of buildDrawingCache, used to enable tracing
     */
    private void buildDrawingCacheImpl(boolean autoScale) {
        mCachingFailed = false;

        int width = mRight - mLeft;
        int height = mBottom - mTop;

        final AttachInfo attachInfo = mAttachInfo;
        final boolean scalingRequired = attachInfo != null && attachInfo.mScalingRequired;

        if (autoScale && scalingRequired) {
            width = (int) ((width * attachInfo.mApplicationScale) + 0.5f);
            height = (int) ((height * attachInfo.mApplicationScale) + 0.5f);
        }

        final int drawingCacheBackgroundColor = mDrawingCacheBackgroundColor;
        final boolean opaque = drawingCacheBackgroundColor != 0 || isOpaque();
        final boolean use32BitCache = attachInfo != null && attachInfo.mUse32BitDrawingCache;

        final long projectedBitmapSize = width * height * (opaque && !use32BitCache ? 2 : 4);
        final long drawingCacheSize =
                ViewConfiguration.get(mContext).getScaledMaximumDrawingCacheSize();
        if (width <= 0 || height <= 0 || projectedBitmapSize > drawingCacheSize) {
            if (width > 0 && height > 0) {
                Log.w(VIEW_LOG_TAG, getClass().getSimpleName() + " not displayed because it is"
                        + " too large to fit into a software layer (or drawing cache), needs "
                        + projectedBitmapSize + " bytes, only "
                        + drawingCacheSize + " available");
            }
            destroyDrawingCache();
            mCachingFailed = true;
            return;
        }

        boolean clear = true;
        Bitmap bitmap = autoScale ? mDrawingCache : mUnscaledDrawingCache;

        if (bitmap == null || bitmap.getWidth() != width || bitmap.getHeight() != height) {
            Bitmap.Config quality;
            if (!opaque) {
                // Never pick ARGB_4444 because it looks awful
                // Keep the DRAWING_CACHE_QUALITY_LOW flag just in case
                switch (mViewFlags & DRAWING_CACHE_QUALITY_MASK) {
                    case DRAWING_CACHE_QUALITY_AUTO:
                    case DRAWING_CACHE_QUALITY_LOW:
                    case DRAWING_CACHE_QUALITY_HIGH:
                    default:
                        quality = Bitmap.Config.ARGB_8888;
                        break;
                }
            } else {
                // Optimization for translucent windows
                // If the window is translucent, use a 32 bits bitmap to benefit from memcpy()
                quality = use32BitCache ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565;
            }

            // Try to cleanup memory
            if (bitmap != null) bitmap.recycle();

            try {
                bitmap = Bitmap.createBitmap(mResources.getDisplayMetrics(),
                        width, height, quality);
                bitmap.setDensity(getResources().getDisplayMetrics().densityDpi);
                if (autoScale) {
                    mDrawingCache = bitmap;
                } else {
                    mUnscaledDrawingCache = bitmap;
                }
                if (opaque && use32BitCache) bitmap.setHasAlpha(false);
            } catch (OutOfMemoryError e) {
                // If there is not enough memory to create the bitmap cache, just
                // ignore the issue as bitmap caches are not required to draw the
                // view hierarchy
                if (autoScale) {
                    mDrawingCache = null;
                } else {
                    mUnscaledDrawingCache = null;
                }
                mCachingFailed = true;
                return;
            }

            clear = drawingCacheBackgroundColor != 0;
        }

        Canvas canvas;
        if (attachInfo != null) {
            canvas = attachInfo.mCanvas;
            if (canvas == null) {
                canvas = new Canvas();
            }
            canvas.setBitmap(bitmap);
            // Temporarily clobber the cached Canvas in case one of our children
            // is also using a drawing cache. Without this, the children would
            // steal the canvas by attaching their own bitmap to it and bad, bad
            // thing would happen (invisible views, corrupted drawings, etc.)
            attachInfo.mCanvas = null;
        } else {
            // This case should hopefully never or seldom happen
            canvas = new Canvas(bitmap);
        }

        if (clear) {
            bitmap.eraseColor(drawingCacheBackgroundColor);
        }

        computeScroll();
        final int restoreCount = canvas.save();

        if (autoScale && scalingRequired) {
            final float scale = attachInfo.mApplicationScale;
            canvas.scale(scale, scale);
        }

        canvas.translate(-mScrollX, -mScrollY);

        mPrivateFlags |= PFLAG_DRAWN;
        if (mAttachInfo == null || !mAttachInfo.mHardwareAccelerated ||
                mLayerType != LAYER_TYPE_NONE) {
            mPrivateFlags |= PFLAG_DRAWING_CACHE_VALID;
        }

        // Fast path for layouts with no backgrounds
        if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
            mPrivateFlags &= ~PFLAG_DIRTY_MASK;
            dispatchDraw(canvas);
            drawAutofilledHighlight(canvas);
            if (mOverlay != null && !mOverlay.isEmpty()) {
                mOverlay.getOverlayView().draw(canvas);
            }
        } else {
            draw(canvas);
        }

        canvas.restoreToCount(restoreCount);
        canvas.setBitmap(null);

        if (attachInfo != null) {
            // Restore the cached Canvas for our siblings
            attachInfo.mCanvas = canvas;
        }
    }

这段代码的主要作用如下:
1.获取视图的宽、高、背景色等信息。
2.计算所需的cache大小,如果宽高小于或者等于0,cache大小超过系统限制的大小,调用destroyDrawingCache(),清空缓存,返回null。
3.判断标识autoScale,获得不同的Bitmap,配置Bitmap的图像质量,RGB格式等信息。
4.根据上一步Bitmap配置信息,调用canvas.setBitmap(bitmap)或者canvas = new Canvas(bitmap),设置Canvas。
5.Canvas调用dispatchDraw()或者draw()方法绘制,Bitmap保存绘制信息。
最后,getDrawingCache()返回缓存的Bitmap对象mUnscaledDrawingCache。

需要注意的是,view设置的宽高大于手机分辨率的时候,会返回null,产生空指针的问题。可以根据view的宽高,用Canvas绘制Bitmap。示例代码如下:

    public Bitmap getBitmapFromView(View view){
        Bitmap bitmap = null;
        try {
            bitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(), Bitmap.Config.ARGB_8888);
            Canvas canvas = new Canvas(bitmap);
            view.draw(canvas);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return bitmap;
    }

有什么好的意见或建议,欢迎讨论。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值