Android View转换为Bitmap实现应用内截屏功能

目录
  • 前言
  • 一、getDrawingCache
  • 二、黑屏问题
  • 三、源码分析
  • 四、View转Canvas转Bitmap

前言

安卓设备一般都自带截图功能,但是用户体验有不好之处。就是会连带着状态栏、、时间日期、其他不必要页面中信息,等等与用户想截屏的内容不符的信息也会被保存下来。通常,截图后用户会再次裁剪一次才能想把真正需求分享出去。

因此,咱们技术研发会遇到针对性的会做一些应用内的截屏功能。

一、getDrawingCache

getDrawingCache()是其中一种截图手段,使用方便,主要针对应用内截图。

1、创建View

fun getShareView() : View {
     val shareView: View =
            LayoutInflater.from(context).inflate(R.layout.share_layout, null)
     //内容...
     return  shareView
}

注意:一般大家实现思路都是点击事件里进行创建View绘制,很可能会遇到网络图片还未加载完的情况。因此,建议做延迟处理,或在点击前前置创建好。

2、测试和绘制

 public static void layoutView(View v, int width, int height) {
        v.layout(0, 0, width, height);
        int measuredWidth = View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY);
        int measuredHeight = View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY);
        v.measure(measuredWidth, measuredHeight);
        v.layout(0, 0, v.getMeasuredWidth(), v.getMeasuredHeight());
    }

如果不走这个方法,bitmap转换时会没有视图(黑屏情况)。

调用方法:

// 设置视图的dp宽高
layoutView(share_view, dp2px(210), dp2px(180));
  public static int dp2px(float dp) {
        float scale = Resources.getSystem().getDisplayMetrics().density;
        return (int) (dp * scale + 0.5f);
    }

3、转换Bitmap

 public static Bitmap getCacheBitmapFromView(View view) {
        final boolean drawingCacheEnabled = true;
        view.setDrawingCacheEnabled(drawingCacheEnabled);
      //设置背景色  //view.setBackgroundColor(CommonUtils.getContext().getResources().getColor(R.color.half_white));
        view.buildDrawingCache(drawingCacheEnabled);
        final Bitmap drawingCache = view.getDrawingCache();
        Bitmap bitmap;
        if (drawingCache != null) {
            bitmap = Bitmap.createBitmap(drawingCache);
            view.setDrawingCacheEnabled(false);
        } else {
            bitmap = null;
        }
        return bitmap;
    }

二、黑屏问题

一般情况下,上面的代码能够正常实现效果。但有时候,生成Bitmap会出现问题(Bitmap全黑色)。主要原因是drawingCache的值大于系统给定的值。我们可以看一下buildDrawingCache()方法中的一段代码:

    //所要cache的view绘制的宽度和高度
if (width <= 0 || height <= 0 ||
    //计算的是当前所需要的drawingCache 大小
    (width * height * (opaque && !translucentWindow ? 2 : 4) >
    //得到的是系统所提供的最大的DrawingCache的值
   ViewConfiguration.get(mContext).getScaledMaximumDrawingCacheSize())) {
    destroyDrawingCache();
    return;
}

当所需要的drawingCache > 系统所提供的最大DrawingCache值时,生成Bitmap就会出现问题,此时获取的Bitmap就为null。

所以在只需要修改所需的cache值就可以解决问题了。于是我们引入第二种方法:

解决方案:

public static Bitmap convertViewToBitmap(View view){
    view.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
    view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());
    view.buildDrawingCache();
    Bitmap bitmap = view.getDrawingCache();
    return bitmap;
}

view 使用 "getMeasuredWidth()"、 "getMeasuredHeight()"方法计算长宽。此时,Bitmap就能正确获取了。

三、源码分析

    public void buildDrawingCache() {
        buildDrawingCache(false);
    }
    public Bitmap getDrawingCache() {
        return getDrawingCache(false);
    }
    public Bitmap getDrawingCache(boolean autoScale) {
        if ((mViewFlags & WILL_NOT_CACHE_DRAWING) == WILL_NOT_CACHE_DRAWING) {
            return null;
        }
        if ((mViewFlags & DRAWING_CACHE_ENABLED) == DRAWING_CACHE_ENABLED) {
            buildDrawingCache(autoScale);
        }
        return autoScale ? mDrawingCache : mUnscaledDrawingCache;
    }
    public void buildDrawingCache(boolean autoScale) {
        if ((mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == 0 || (autoScale ?
                mDrawingCache == null : mUnscaledDrawingCache == null)) {
            if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
                Trace.traceBegin(Trace.TRACE_TAG_VIEW,
                        "buildDrawingCache/SW Layer for " + getClass().getSimpleName());
            }
            try {
                buildDrawingCacheImpl(autoScale);
            } finally {
                Trace.traceEnd(Trace.TRACE_TAG_VIEW);
            }
        }
    }
    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;
        }
     ..检测drawingCache原有数据操作..
	    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;
            }
	..执行Bitmap写入autoScale ? mDrawingCache : mUnscaledDrawingCache操作..
    }

从以上源码中,可以看到getDrawingcache = null的条件共有四个:

1、(mViewFlags & WILL_NOT_CACHE_DRAWING) == WILL_NOT_CACHE_DRAWING为true

2、没有设置setDrawingCacheEnabled(true)

3、width <= 0 || height <= 0 || projectedBitmapSize > drawingCacheSize为true

4、OutOfMemory

除了第一个条件,其他的都是buildDrawingCache执行时才会触发。下面来分析下条件三。既然子布局可以正常显示,那么一定是满足width>0和height>0的, drawingCacheSize肯定是一个固定值,就是当前设备系统所允许的最大绘制缓存值。projectedBitmapSize的计算方式为width * height * (opaque && !use32BitCache ? 2 : 4),顾名思义,就是当前计划缓存的图片大小,(opaque && !use32BitCache ? 2 : 4)不可能为0,也不可能是导致计划缓存值变大的主因,width就是屏幕的宽,这个没有变动的条件,那么可以肯定就是height出现了异常,对于视图高度的计算,android源码表示如下:

@ViewDebug.ExportedProperty(category = "layout")
public final int getHeight() {
  return mBottom - mTop;
}

一个View的高度getHeight()就是底-高,其中mBottom指的是视图自身的底边到父视图顶边的距离,mTop指的是视图自身的顶边到父视图顶边的距离。

四、View转Canvas转Bitmap

    Bitmap bitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(), Bitmap.Config.ARGB_8888);
    Canvas canvas = new Canvas(bitmap);
    view.measure(View.MeasureSpec.makeMeasureSpec(view.getWidth(), View.MeasureSpec.EXACTLY), View.MeasureSpec.makeMeasureSpec(view.getHeight(), View.MeasureSpec.EXACTLY));
    view.layout((int) view.getX(), (int) view.getY(), (int) view.getX() + view.getMeasuredWidth(), (int) view.getY() + view.getMeasuredHeight());
    view.draw(canvas);
    return bitmap;

到此这篇关于Android View转换为Bitmap实现应用内截屏功能的文章就介绍到这了,更多相关Android View转换为Bitmap内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Android利用反射机制调用截屏方法和获取屏幕宽高的方法

    想要在应用中进行截屏,可以直接调用 View 的 getDrawingCache 方法,但是这个方法截图的话是没有状态栏的,想要整屏截图就要自己来实现了. 还有一个方法可以调用系统隐藏的 screenshot 方法,来进行截屏,这种方法截图是整屏的. 通过调用 SurfaceControl.screenshot() / Surface.screenshot() 截屏,在 API Level 大于 17 使用 SurfaceControl ,小于等于 17 使用 Surface,但是 screen

  • Android实现长截屏功能

    本文实例为大家分享了Android实现长截屏功能的具体代码,供大家参考,具体内容如下 1.MainActivity public class MainActivity extends AppCompatActivity { ScrollView scrollView; String sdRoot = Environment.getExternalStorageDirectory().getPath(); @Override protected void onCreate(Bundle saved

  • Android实现全屏截图或长截屏功能

    本文实例为大家分享了Android实现全屏截图或长截屏功能的具体代码,供大家参考,具体内容如下 全屏截图: /** * 传入的activity是要截屏的activity */ public static Bitmap getViewBitmap(Activity activity) { // View是你需要截图的View View view = activity.getWindow().getDecorView(); //这两句必须写,否则getDrawingCache报空指针 view.se

  • 浅谈Android截屏和指定View生成截图

    当前页面截图(截取整个屏幕) 截取当前Activity页面的截图,可以通过窗体最底层的decorView进行缓存,然后根据这个缓存对象生成一张图片.有的需要不需要状态栏,也可以指定生成图片的宽高,把状态栏去除. /** * 截取当前窗体的截图,根据[isShowStatusBar]判断是否包含当前窗体的状态栏 * 原理是获取当前窗体decorView的缓存生成图片 */ fun captureWindow(activity: Activity, isShowStatusBar: Boolean)

  • Android实现截屏功能

    导言 目前截屏的方法很多,root不适用,要么其他方法就是有局限性,而其中官方给出的方案最好-MediaProjection 介绍 Android 5.0以后开放的录屏API,取视频中的一帧数据,这样就可以实现截屏 步骤 在activity中授权,在service中完成初始化并截图,当然可以后台定时截图,但是6.0系统会有内存溢出的bug 1:build.gradle compileSdkVersion 21 buildToolsVersion '27.0.3' defaultConfig {

  • Android实现截屏与截长图功能

    本文实例为大家分享了Android实现截屏与截长图功能展示的具体代码,供大家参考,具体内容如下 Demo在GitHub的地址:ScreenShoot Demo在CSDN上的下载地址:Android实现截屏与截长图功能 在Android开发中,有时候会遇到需要截屏分享到朋友圈或者QQ,截屏有截取当前屏幕,也有需要截取不仅一个屏幕,可能会很长. 截取当前屏幕并保存到内存卡的方法: // 获取指定Activity的截屏,保存到png文件 public static Bitmap takeScreenS

  • Android Activity 不能被截屏的解决方法

    在Activity 添加即可 getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE); 以上这篇Android Activity 不能被截屏的解决方法就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持我们. 您可能感兴趣的文章: Android截屏方案实现原理解析 Android截屏分享功能 Android 下调试手机截屏的方法 Android 实现截屏功能的实例 android长截屏原理及实现代码 Andr

  • Android源码解析之截屏事件流程

    今天这篇文章我们主要讲一下Android系统中的截屏事件处理流程.用过android系统手机的同学应该都知道,一般的android手机按下音量减少键和电源按键就会触发截屏事件(国内定制机做个修改的这里就不做考虑了).那么这里的截屏事件是如何触发的呢?触发之后android系统是如何实现截屏操作的呢?带着这两个问题,开始我们的源码阅读流程. 我们知道这里的截屏事件是通过我们的按键操作触发的,所以这里就需要我们从android系统的按键触发模块开始看起,由于我们在不同的App页面,操作音量减少键和电

  • android实现手机截屏并保存截图功能

    本文实例为大家分享了android实现手机截屏并保存截图功能的具体代码,供大家参考,具体内容如下 一.准备一张图片 拷贝screenshot_panel.9.png放在目录drawable-xhdpi下 二.activity_main.xml 代码如下: <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.androi

  • Android View转换为Bitmap实现应用内截屏功能

    目录 前言 一.getDrawingCache 二.黑屏问题 三.源码分析 四.View转Canvas转Bitmap 前言 安卓设备一般都自带截图功能,但是用户体验有不好之处.就是会连带着状态栏..时间日期.其他不必要页面中信息,等等与用户想截屏的内容不符的信息也会被保存下来.通常,截图后用户会再次裁剪一次才能想把真正需求分享出去. 因此,咱们技术研发会遇到针对性的会做一些应用内的截屏功能. 一.getDrawingCache getDrawingCache()是其中一种截图手段,使用方便,主要

  • 解析Android中View转换为Bitmap及getDrawingCache=null的解决方法

    1.前言 Android中经常会遇到把View转换为Bitmap的情形,比如,对整个屏幕视图进行截屏并生成图片:Coverflow中需要把一页一页的view转换为Bitmap.以便实现复杂的图形效果(阴影.倒影效果等):再比如一些动态的实时View为便于观察和记录数据.需要临时生成静态的Bitmap. 2.实现方法 1)下面是笔者经常用的一个转换方法 public static Bitmap convertViewToBitmap(View view, int bitmapWidth, int

  • Android 将view 转换为Bitmap出现空指针问题解决办法

    Android 将view 转换为Bitmap出现空指针问题解决办法 在做Android 项目的时候,有时候可能有这样的需求,将一个View 或者一个布局文件转换成一个Bitmap  对象. 方法其实大都差不多.但这其中有一些小细节需要注意一下.最近在项目中用到了这个功能,现在分享一下,希望能帮助到遇到果这个 问题的人. 首先是转换 的代码: /** * 将View(布局) 转换为bitmap * @param view * @return */ public static Bitmap cre

  • android截屏功能实现代码

    android开发中通过View的getDrawingCache方法可以达到截屏的目的,只是缺少状态栏! 原始界面 截屏得到的图片 代码实现 1. 添加权限(AndroidManifest.xml文件里) 复制代码 代码如下: <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> 2. 添加1个Button(activity_main.xml文件) <RelativeL

  • Android 实现截屏功能的实例

    Android 实现截屏功能的实例 实现代码: public class ScreenShot { // 获取指定Activity的截屏,保存到png文件 private static Bitmap takeScreenShot(Activity activity) { // View是你需要截图的View View view = activity.getWindow().getDecorView(); view.setDrawingCacheEnabled(true); view.buildD

  • JavaScript截屏功能的实现代码

    最近参与了网易炉石盒子的相关页面开发,在做卡组分享页(地址:炉石盒子卡组分享),有个需求:用户可以把这个卡组以图片的形式分享给好友.最初的的做法是使用服务器把该页面转换成图片,然后把图片地址返回给前端.嗯,这样也挺好的啊,而且服务器还可以对转换出来的图片进行缓存,下次请求可以直接返回图片地址了.原理上是毫无毛病的.然而,问题来了,后台转换的图片和页面内容偶尔不一致,有时候会少了一一些内容,PM姐姐就很不爽了,说这个问题一定要解决.反正页面转成图片的接口是后台做的,关我luan事啊!就在暗暗自喜的

  • asp.net截屏功能实现截取web页面

    本文实例展示了asp.net截屏功能实现截取web页面的方法,代码简洁易懂,分享给大家供大家参考. 具体实现代码如下: using System.Drawing; //打开该页面 System.Diagnostics.Process.Start("IEXPLORE.EXE", "http://localhost:56/Spacxxe.html"); System.Threading.Thread.Sleep(3000); //截屏 //创建并设置画布大小 Syste

  • Java实现的简单网页截屏功能示例

    本文实例讲述了Java实现的简单网页截屏功能.分享给大家供大家参考,具体如下: package awtDemo; import java.awt.AWTException; import java.awt.Desktop; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Image; import java.awt.Rectangle; import java.awt.Robot; import java.

随机推荐