Android View转换为Bitmap,实现截屏效果

这篇具有很好参考价值的文章主要介绍了Android View转换为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指的是视图自身的顶边到父视图顶边的距离。文章来源地址https://www.toymoban.com/news/detail-405234.html

四、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,实现截屏效果的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Android 实现屏幕录制和截屏

    在移动开发中,实现屏幕录制和截屏是常见的需求。对于 Android 应用而言,实现屏幕录制和截屏可以帮助开发者更好地测试和调试自己的应用,同时还能够提供一些特定场景下的用户体验。 Android 应用程序可以通过使用 MediaProjection API 来实现屏幕录制功能。使用此 API 可以获

    2024年02月15日
    浏览(25)
  • Android 截屏实现的几种方式

    image.png image.png 1、View 截屏 View 截图是将当前 View 界面截取下来,而对于屏幕上其他信息比如:状态栏或其他应用的界面将无法截取。 1.1 截取除了导航栏之外的屏幕 1.2 截取某个控件或者区域 2、WebView 截屏 WebView 截屏有四种方式 2.1 使用 capturePicture() 方法(已废弃) private

    2024年02月06日
    浏览(27)
  • Baumer工业相机堡盟工业相机如何通过NEOAPI SDK实现Bitmap的图像转换功能(C++)

    Baumer工业相机堡盟相机是一种高性能、高质量的工业相机,可用于各种应用场景,如物体检测、计数和识别、运动分析和图像处理。 Baumer的万兆网相机拥有出色的图像处理性能,可以实时传输高分辨率图像。此外,该相机还具有快速数据传输、低功耗、易于集成以及高度可扩

    2024年01月19日
    浏览(36)
  • Baumer工业相机堡盟工业相机如何通过NEOAPI SDK实现相机图像转换为Bitmap图像功能(C#)

    Baumer工业相机堡盟相机是一种高性能、高质量的工业相机,可用于各种应用场景,如物体检测、计数和识别、运动分析和图像处理。 Baumer的万兆网相机拥有出色的图像处理性能,可以实时传输高分辨率图像。此外,该相机还具有快速数据传输、低功耗、易于集成以及高度可扩

    2024年01月19日
    浏览(34)
  • UE4实现截屏并保存到相册Android/iOS兼容

    通过Edit-Plugins-NewPlugin创建3个空的Plugin: MyNative插件,实现截屏功能,并提供对外调用的接口 MyNativeAndroid插件,实现Android端保存图片到相册功能 MyNativeIos插件,实现iOS端保存图片到相册功能 1.在MyNative.uplugin注册引用到2个插件MyNativeAndroid和MyNativeIos 2.在MyNative.Build.cs分平台引用

    2024年02月15日
    浏览(37)
  • unity 利用Scroll View实现滑动翻页效果

    1.在Hierarchy视图右键创建UI-Scroll View。 Scrollbar可根据自己需求选择是否删除,我这里制作的翻页日历用不上我就删除了。 connect节点挂上Grid Layout Group组件,参数属性可参考unity API。 下面是具体实现代码  onLeft和onRight绑定左右翻页按钮事件  

    2024年01月25日
    浏览(27)
  • App防止恶意截屏功能的方法:iOS、Android和鸿蒙系统的实现方案

    防止应用被截图是一个比较常见的需求,主要是出于安全考虑。下面将分别为iOS(苹果系统)、Android(安卓系统)及HarmonyOS(鸿蒙系统)提供防止截屏的方法和示例代码。 在企业内部使用的应用中,防止员工恶意截屏是一个重要的安全需求。本文将详细介绍iOS、Android和鸿蒙

    2024年02月04日
    浏览(30)
  • 微信小程序实现锚点效果 scroll-view的scroll-into-view属性

    小程序中实现锚点效果,可以直接使用scroll-view的scroll-into-view属性就可以实现锚点效果,比较方便简单。那么需要用到scroll-view那些参数呢,下面具体讲讲: scroll-x | scroll-y:设置滚动刚想 scroll-into-view:子元素id(id不能以数字开头)。设置哪个方向可滚动,则在哪个方向滚动

    2024年02月13日
    浏览(31)
  • [uniapp] scroll-view 简单实现 u-tabbar效果

    效果图 方案 官方scroll-view 进行封装 配合属性 scroll-left Number/String 设置横向滚动条位置 即可 scroll-into-view 属性尝试过,方案较难实现 踩坑 1.scroll-view 横向失败 安装官网的解释 使用竖向滚动时,需要给 scroll-view 一个固定高度,通过 css 设置 height;使用横向滚动时,需要给scr

    2024年02月11日
    浏览(29)
  • Android View实现滑动的方式

    实现View的滑动有三种方式 通过View本身提供的scrollTo/scrollBy方法实现滑动 通过动画给View施加平移效果来实现滑动 通过改变View LayoutParams使得View重新布局从而实现滑动 scrollTo:通过传递的参数实现绝对滑动 scrollBy:通过传递的参数实现相对滑动 scrollTo和scrollBy只能改变View内容

    2024年02月16日
    浏览(38)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包