drawingcache解析 通过view的绘制缓存得到bitmap,从而实现view内容截图
来源:互联网 发布:舰娘 mac魔改 编辑:程序博客网 时间:2024/06/11 19:15
android为了提高滚动等各方面的绘制速度,可以为每一个view建立一个缓存,使用 View.buildDrawingCache为自己的view建立相应的缓存,这个cache就是一个bitmap对象。利用这个功能可以对整个屏幕视图进行截屏并生成Bitmap,也可以获得指定的view的Bitmap对象。在有的时候还会影响性能,例如如果自己实现一个Gallery效果,可能就会使用到view缓存。animateCache和scrollingCache用于动画和滚动的缓存,使用不当也会造成性能下降。
要获得一个view的bitmap对象涉及到三个方法:setDrawingCacheEnabled、buildDrawingCache和getDrawingCache。所有的View都有这三种方法。大部分view如果没有设置setDrawingCacheEnabled(true);来启用View的DrawingCache功能的话,那默认是不启用。
启用DrawingCache的话,使用getDrawingCache方法时,会先自动去调用buildDrawingCache方法建立DrawingCache,再将结果返回;不启用DrawingCache的话,使用getDrawingCache方法时,会返回上一次使用buildDrawingCache方法所产生的结果。如果在此之前都沒有使用过buildDrawingCache来建立DrawingCache的话,那么getDrawingCache就会返回null。如果一开始没有启用DrawingCache,也是可以事先使用buildDrawingCache来建立DrawingCache,避免getDrawingCache返回null。
getDrawingCache源码如下:
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; }
可以看出getDrawingCache在设置了DrawingCache的情况下自动调用buildDrawingCache。
照刚才所说的,那么要获得最新的DrawingCache有两种方式:
方式一:
view.setDrawingCacheEnabled(true);Bitmap drawingCache = view.getDrawingCache();
方式二:
view.buildDrawingCache();Bitmap drawingCache = view.getDrawingCache();
在调用setDrawingCacheEnabled(true);以后就不要再调用buildDrawingCache方法了,以下写法应该避免,会两次建立DrawingCache:
view.setDrawingCacheEnabled(true);view.buildDrawingCache();Bitmap drawingCache = view.getDrawingCache();
buildDrawingCache建立drawingCache的同时,会将上次的DrawingCache回收掉,在源码中buildDrawingCache会调用destroyDrawingCache方法对之前的DrawingCache回收,源码如下:
/** * <p>Frees the resources used by the drawing cache. If you call * {@link #buildDrawingCache()} manually without calling * {@link #setDrawingCacheEnabled(boolean) setDrawingCacheEnabled(true)}, you * should cleanup the cache with this method afterwards.</p> * * @see #setDrawingCacheEnabled(boolean) * @see #buildDrawingCache() * @see #getDrawingCache() */ public void destroyDrawingCache() { if (mDrawingCache != null) { mDrawingCache.recycle(); mDrawingCache = null; } if (mUnscaledDrawingCache != null) { mUnscaledDrawingCache.recycle(); mUnscaledDrawingCache = null; } }
因此不必在buildDrawingCache方法之前,或者DrawingCache启用状态下调用getDrawingCache方法之前,自己手动调用destroyDrawingCache。会导致RumtimeException:java.lang.RuntimeException: Canvas: trying to use a recycled bitmap android.graphics.Bitmap@4b8eb8
下面的写法是错误写法:
if(view.getDrawingCache() != null){ view.getDrawingCache().recycle();; } view.buildDrawingCache(); Bitmap drawingCache = view.getDrawingCache();
图片质量控制
对于Bitmap对象可以有多种格式,如:
Bitmap.Config.ARGB_8888;
Bitmap.Config.ARGB_4444;
Bitmap.Config.ARGB_8888;
Bitmap.Config.ARGB_8888;
Bitmap.Config.RGB_565;
默认的格式是Bitmap.Config.ARGB_8888,但大多数嵌入式设备使用的显示格式都是Bitmap.Config.RGB_565. RGB_565并没有alpha值,所以绘制的时候不需要计算alpha合成,速度快些。其次,RGB_565可以直接使用优化了的memcopy函数,效率相对高出许多。
可以用以下方法查看bitmap格式:
final Bitmap cache = mContent.getDrawingCache(); if (cache != null) { Config cfg = cache.getConfig(); Log.d(TAG, "----------------------- cache.getConfig() = " + cfg); }
随着Android API越来越高,DrawingCache的质量也越来越,在大部分的情况下都是使用体积最大且运算速度最慢的ARGB_8888,过去View所提供的setDrawingCacheQuality方法已經沒有实际作用了,不管设定哪种质量,都还是会使用ARGB_8888。
getDrawingCache返回空
一种可能是view没有初始化完成,onCreate中view还没有初始化自己的宽高,所以getDrawingCache();返回空。可以参考viewTreeObserver解析这篇来获取view宽高。
下面给出两种方法:
public static Bitmap convertViewToBitmap(View view){ view.measure(View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED), View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)); view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight()); view.buildDrawingCache(); Bitmap bitmap = view.getDrawingCache(); return bitmap; }
第二种方法利用ViewTreeObserver
ViewTreeObserver vto = view.getViewTreeObserver(); vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { view.getViewTreeObserver().removeGlobalOnLayoutListener(this); Bitmap cac = view.getDrawingCache(); if(cac != null) { Bitmap.Config cfg = cac.getConfig(); Log.e("====", "not null"+cfg); mImageView2.setImageBitmap(cac); } else { Log.e("====", "null"); } } });
如果很确定View已经有过measure和layout且也调用buildDrawingCache(无论自动或者手动)方法了,但是getDrawingCache还是返回null,那就是因为要绘制的DrawingCache太大了,超过Android系统设定的drawingCacheSize,这时,就只能放弃使用DrawingCache了。
Android系统设定的DrawingCache大小上限,在不同的裝置上有不同的设定,甚至有可能差了好几倍,如果要查看数值的話可以使用以下方式来取得drawingCacheSize:
ViewConfiguration.get(context).getScaledMaximumDrawingCacheSize();
getDrawingCache的替代方法
如果不用getDrawingCache想自己建立出Bitmap也是可以的,代码如下:
public Bitmap getMagicDrawingCache(View view) { Bitmap bitmap = (Bitmap) view.getTag(R.id.cacheBitmapKey); Boolean dirty = (Boolean) view.getTag(R.id.cacheBitmapDirtyKey); int viewWidth = view.getWidth(); int viewHeight = view.getHeight(); if (bitmap == null || bitmap.getWidth() != viewWidth || bitmap.getHeight() != viewHeight) { if (bitmap != null && !bitmap.isRecycled()) { bitmap.recycle(); } bitmap = Bitmap.createBitmap(viewWidth, viewHeight, bitmap_quality); view.setTag(R.id.cacheBitmapKey, bitmap); dirty = true; } if (dirty == true || !quick_cache) { bitmap.eraseColor(getResources().getColor(android.R.color.transparent)); Canvas canvas = new Canvas(bitmap); view.draw(canvas); view.setTag(R.id.cacheBitmapDirtyKey, false); } return bitmap; }
如果要加入View不在Activity或是Fragment的RootView中的判断的话,代码如下:
public Bitmap getMagicDrawingCache2(View view) { Bitmap bitmap = (Bitmap) view.getTag(R.id.cacheBitmapKey); Boolean dirty = (Boolean) view.getTag(R.id.cacheBitmapDirtyKey); if (view.getWidth() + view.getHeight() == 0) { view.measure(View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED), View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)); view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight()); } int viewWidth = view.getWidth(); int viewHeight = view.getHeight(); if (bitmap == null || bitmap.getWidth() != viewWidth || bitmap.getHeight() != viewHeight) { if (bitmap != null && !bitmap.isRecycled()) { bitmap.recycle(); } bitmap = Bitmap.createBitmap(viewWidth, viewHeight, bitmap_quality); view.setTag(R.id.cacheBitmapKey, bitmap); dirty = true; } if (dirty == true || !quick_cache) { bitmap.eraseColor(getResources().getColor(android.R.color.transparent)); Canvas canvas = new Canvas(bitmap); view.draw(canvas); view.setTag(R.id.cacheBitmapDirtyKey, false); } return bitmap; }
其中,cacheBitmapKey和cacheBitmapDirtyKey是不同的整数,分别用来指定View的Tag ID。cacheBitmapKey的位置会存放使用这个方法建立出来的DrawingCache;cacheBitmapDirtyKey的位置会存放这个View的DrawingCache是否已经是脏数据(dirty)而需要使用View的draw方法重新绘制。DrawingCache所用的Bitmap只在没有Bitmap或是Bitmap的大小和View的大小不合的时候才重新建立,在建立新的Bitmap前会先將先前的Bitmap进行recycle,新的Bitmap的参考会再被存入至View的Tag中。他们的定义如下:
<?xml version="1.0" encoding="utf-8"?><resources> <item type="id" name="cacheBitmapKey"></item> <item type="id" name="cacheBitmapDirtyKey"></item></resources>
另外需要设置bitmap_quality和quick_cache:
Bitmap.Config bitmap_quality = Bitmap.Config.ARGB_8888 ; boolean quick_cache = false ;
quick_cache若设置为false,则不论DrawingCache是否dirty,都进行重绘,只有在View常常变化的时候才需要这样做。bitmap_quality可以设置为Bitmap.Config.RGB_565或是Bitmap.Config.ARGB_8888,Bitmap.Config.ARGB_4444已经随着Android API升级家而慢慢被禁用了。
个人学得作者对view绘制缓存分析很透,学习了!
http://souly.cn/%E6%8A%80%E6%9C%AF%E5%8D%9A%E6%96%87/2016/01/05/DrawingCache%E8%A7%A3%E6%9E%90/
- drawingcache解析 通过view的绘制缓存得到bitmap,从而实现view内容截图
- View 的 DrawingCache
- View DrawingCache的理解
- View DrawingCache的理解
- 得到view的信息以及 view绘制流程解析
- View DrawingCache
- 对View DrawingCache的理解
- 对View DrawingCache的理解
- 对View DrawingCache的理解
- 对View DrawingCache的理解
- 对View DrawingCache的理解
- 对View DrawingCache的理解
- 对View DrawingCache的理解
- 对View DrawingCache的理解
- 对View DrawingCache的理解
- 对View DrawingCache的理解
- 对View DrawingCache的理解
- 对View DrawingCache的理解
- 原码乘法、补码乘法
- Erlang中的图形化检测工具总结
- [CSAPP] 存储器层次结构(二)
- 打印订单--bcty365
- ST_VTR_SYS_PARAM*类型的实参跟ST_VTR_SYS_PARAM*类型的形参不兼容
- drawingcache解析 通过view的绘制缓存得到bitmap,从而实现view内容截图
- tomcat 的三个端口号(一台电脑运行两个tomcat的情况)
- listview 的优化
- 第十五篇:大球联赛与小球联赛
- The End
- 添加自定义数据到TensorBoard显示
- 如何缩小APK包的尺寸
- 百度图像搜索探秘
- Redis监控工具 sentinel