View组件显示的内容能够经过cache机制保存为bitmap, 使用到的api有android
void setDrawingCacheEnabled(boolean flag),
Bitmap getDrawingCache(boolean autoScale),
void buildDrawingCache(boolean autoScale),
void destroyDrawingCache()web
android 为了提升滚动等各方面的绘制速度,能够为每个view创建一个缓存,使用 View.buildDrawingCache为本身的view 创建相应的缓存,
这个所谓的缓存,实际上就是一个Bitmap对象。只是 这个 bitmap 对象能够有多种格式而已,如
Bitmap.Config.ARGB_8888;
Bitmap.Config.ARGB_4444;
Bitmap.Config.RGB_565;
默认的格式是Bitmap.Config.ARGB_8888.,但大多数嵌入式设备使用的显示格式都是Bitmap.Config.RGB_565. 对于后者, 并无
alpha 值,因此绘制的时候不须要计算alpha合成,速递当让快些。其次,RGB_565能够直接使用优化了的memcopy函数,效率相对高出许多。 canvas
view的getDrawingCache得到数据始终为nullwindows
setDrawingCacheEnabledapi
1 public void setDrawingCacheEnabled(boolean enabled) { 2 setFlags(enabled ? DRAWING_CACHE_ENABLED : 0, DRAWING_CACHE_ENABLED); 3 }
DRAWING_CACHE_ENABLED是否支持设置缓存
先看getDrawingCache的源码app
1 public Bitmap getDrawingCache(boolean autoScale) { 2 if ((mViewFlags & WILL_NOT_CACHE_DRAWING) == WILL_NOT_CACHE_DRAWING) { 3 return null; 4 } 5 if ((mViewFlags & DRAWING_CACHE_ENABLED) == DRAWING_CACHE_ENABLED) { 6 buildDrawingCache(autoScale); 7 } 8 return autoScale ? (mDrawingCache == null ? null : mDrawingCache.get()) : 9 (mUnscaledDrawingCache == null ? null : mUnscaledDrawingCache.get()); 10 }
1) (mViewFlags & WILL_NOT_CACHE_DRAWING) == WILL_NOT_CACHE_DRAWING 这个值为truedom
2) (mViewFlags & DRAWING_CACHE_ENABLED) == DRAWING_CACHE_ENABLEDide
若是 false,buildDrawingCache没执行函数
3) buildDrawingCache执行失败
这些在源码中均可以看到,在得到缓存数据的时候,跟背景色(drawingCacheBackgroundColor),透明度isOpaque,use32BitCache这些有关系,看是细看这些东西都是表面的,是系统在buildDrawingCache的时候,根据View或都系统设置而来的;有些属性是不能更改的;这样一来当一个固定大小的View在不一样的设备上生成的图片就可能有所不一样,我同事这边存在的问题就是,设置View的固定大小为1360*768,而我View转换为Bitmap及getDrawingCache的设备分辨率为1024*600,而源码里能够看到这样代码:
1 if (width <= 0 || height <= 0 || 2 // Projected bitmap size in bytes 3 (width * height * (opaque && !use32BitCache ? 2 : 4) > 4 ViewConfiguration.get(mContext).getScaledMaximumDrawingCacheSize())) { 5 destroyDrawingCache(); 6 mCachingFailed = true; 7 return; 8 }
当咱们在buildDrawingCache的时候,系统给了咱们默认最大的DrawingCacheSize为屏幕宽*高*4;而个人View的CacheSize大小超过了某些设备默认值,就会致使得到为空;开始想着用反射的方法去改变这些属性,或者设置背景颜色来改变图片质量,这样一来CacheSize大小 就可能会变小,可是这样始终不能达到效果;
最终解决方案:
查看系统buildDrawingCache方法能够看到:
1 public void buildDrawingCache(boolean autoScale) {
//若是没有buildDrawingCache,则执行 2 if ((mPrivateFlags & DRAWING_CACHE_VALID) == 0 || (autoScale ? 3 (mDrawingCache == null || mDrawingCache.get() == null) : 4 (mUnscaledDrawingCache == null || mUnscaledDrawingCache.get() == null))) { 5 6 if (ViewDebug.TRACE_HIERARCHY) { 7 ViewDebug.trace(this, ViewDebug.HierarchyTraceType.BUILD_CACHE); 8 } 9 if (Config.DEBUG && ViewDebug.profileDrawing) { 10 EventLog.writeEvent(60002, hashCode()); 11 } 12 13 int width = mRight - mLeft; 14 int height = mBottom - mTop; 15 16 final AttachInfo attachInfo = mAttachInfo; 17 final boolean scalingRequired = attachInfo != null && attachInfo.mScalingRequired; 18 19 if (autoScale && scalingRequired) { 20 width = (int) ((width * attachInfo.mApplicationScale) + 0.5f); 21 height = (int) ((height * attachInfo.mApplicationScale) + 0.5f); 22 } 23 24 final int drawingCacheBackgroundColor = mDrawingCacheBackgroundColor; 25 final boolean opaque = drawingCacheBackgroundColor != 0 || isOpaque(); 26 final boolean translucentWindow = attachInfo != null && attachInfo.mTranslucentWindow; 27 //不知足这些条件不执行
28 if (width <= 0 || height <= 0 || 29 // Projected bitmap size in bytes 30 (width * height * (opaque && !translucentWindow ? 2 : 4) > 31 ViewConfiguration.get(mContext).getScaledMaximumDrawingCacheSize())) { 32 destroyDrawingCache(); 33 return; 34 } 35 36 boolean clear = true; 37 Bitmap bitmap = autoScale ? (mDrawingCache == null ? null : mDrawingCache.get()) : 38 (mUnscaledDrawingCache == null ? null : mUnscaledDrawingCache.get()); 39 40 if (bitmap == null || bitmap.getWidth() != width || bitmap.getHeight() != height) { 41 Bitmap.Config quality; 42 if (!opaque) { 43 switch (mViewFlags & DRAWING_CACHE_QUALITY_MASK) { 44 case DRAWING_CACHE_QUALITY_AUTO: 45 quality = Bitmap.Config.ARGB_8888; 46 break; 47 case DRAWING_CACHE_QUALITY_LOW: 48 quality = Bitmap.Config.ARGB_4444; 49 break; 50 case DRAWING_CACHE_QUALITY_HIGH: 51 quality = Bitmap.Config.ARGB_8888; 52 break; 53 default: 54 quality = Bitmap.Config.ARGB_8888; 55 break; 56 } 57 } else { 58 // Optimization for translucent windows 59 // If the window is translucent, use a 32 bits bitmap to benefit from memcpy() 60 quality = translucentWindow ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565; 61 } 62 63 // Try to cleanup memory 64 if (bitmap != null) bitmap.recycle(); 65 66 try { 67 bitmap = Bitmap.createBitmap(width, height, quality); 68 bitmap.setDensity(getResources().getDisplayMetrics().densityDpi); 69 if (autoScale) { 70 mDrawingCache = new SoftReference<Bitmap>(bitmap); 71 } else { 72 mUnscaledDrawingCache = new SoftReference<Bitmap>(bitmap); 73 } 74 if (opaque && translucentWindow) bitmap.setHasAlpha(false); 75 } catch (OutOfMemoryError e) { 76 // If there is not enough memory to create the bitmap cache, just 77 // ignore the issue as bitmap caches are not required to draw the 78 // view hierarchy 79 if (autoScale) { 80 mDrawingCache = null; 81 } else { 82 mUnscaledDrawingCache = null; 83 } 84 return; 85 } 86 87 clear = drawingCacheBackgroundColor != 0; 88 } 89 90 Canvas canvas; 91 if (attachInfo != null) { 92 canvas = attachInfo.mCanvas; 93 if (canvas == null) { 94 canvas = new Canvas(); 95 } 96 canvas.setBitmap(bitmap); 97 // Temporarily clobber the cached Canvas in case one of our children 98 // is also using a drawing cache. Without this, the children would 99 // steal the canvas by attaching their own bitmap to it and bad, bad 100 // thing would happen (invisible views, corrupted drawings, etc.) 101 attachInfo.mCanvas = null; 102 } else { 103 // This case should hopefully never or seldom happen 104 canvas = new Canvas(bitmap); 105 } 106 107 if (clear) { 108 bitmap.eraseColor(drawingCacheBackgroundColor); 109 } 110 111 computeScroll(); 112 final int restoreCount = canvas.save(); 113 114 if (autoScale && scalingRequired) { 115 final float scale = attachInfo.mApplicationScale; 116 canvas.scale(scale, scale); 117 } 118 119 canvas.translate(-mScrollX, -mScrollY); 120 121 mPrivateFlags |= DRAWN; 122 mPrivateFlags |= DRAWING_CACHE_VALID; 123 124 // Fast path for layouts with no backgrounds 125 if ((mPrivateFlags & SKIP_DRAW) == SKIP_DRAW) { 126 if (ViewDebug.TRACE_HIERARCHY) { 127 ViewDebug.trace(this, ViewDebug.HierarchyTraceType.DRAW); 128 } 129 mPrivateFlags &= ~DIRTY_MASK; 130 dispatchDraw(canvas); 131 } else { 132 draw(canvas); 133 } 134 135 canvas.restoreToCount(restoreCount); 136 137 if (attachInfo != null) { 138 // Restore the cached Canvas for our siblings 139 attachInfo.mCanvas = canvas; 140 } 141 } 142 }
安卓提供了方法,这个方法没有提供一个外部访问方法
1 /** 2 * Create a snapshot of the view into a bitmap. We should probably make 3 * some form of this public, but should think about the API. 4 */ 5 Bitmap createSnapshot(Bitmap.Config quality, int backgroundColor, boolean skipChildren) { 6 int width = mRight - mLeft; 7 int height = mBottom - mTop; 8 9 final AttachInfo attachInfo = mAttachInfo; 10 final float scale = attachInfo != null ? attachInfo.mApplicationScale : 1.0f; 11 width = (int) ((width * scale) + 0.5f); 12 height = (int) ((height * scale) + 0.5f); 13 14 Bitmap bitmap = Bitmap.createBitmap(width > 0 ? width : 1, height > 0 ? height : 1, quality); 15 if (bitmap == null) { 16 throw new OutOfMemoryError(); 17 } 18 19 bitmap.setDensity(getResources().getDisplayMetrics().densityDpi); 20 21 Canvas canvas; 22 if (attachInfo != null) { 23 canvas = attachInfo.mCanvas; 24 if (canvas == null) { 25 canvas = new Canvas(); 26 } 27 canvas.setBitmap(bitmap); 28 // Temporarily clobber the cached Canvas in case one of our children 29 // is also using a drawing cache. Without this, the children would 30 // steal the canvas by attaching their own bitmap to it and bad, bad 31 // things would happen (invisible views, corrupted drawings, etc.) 32 attachInfo.mCanvas = null; 33 } else { 34 // This case should hopefully never or seldom happen 35 canvas = new Canvas(bitmap); 36 } 37 38 if ((backgroundColor & 0xff000000) != 0) { 39 bitmap.eraseColor(backgroundColor); 40 } 41 42 computeScroll(); 43 final int restoreCount = canvas.save(); 44 canvas.scale(scale, scale); 45 canvas.translate(-mScrollX, -mScrollY); 46 47 // Temporarily remove the dirty mask 48 int flags = mPrivateFlags; 49 mPrivateFlags &= ~DIRTY_MASK; 50 51 // Fast path for layouts with no backgrounds 52 if ((mPrivateFlags & SKIP_DRAW) == SKIP_DRAW) { 53 dispatchDraw(canvas); 54 } else { 55 draw(canvas); 56 } 57 58 mPrivateFlags = flags; 59 60 canvas.restoreToCount(restoreCount); 61 62 if (attachInfo != null) { 63 // Restore the cached Canvas for our siblings 64 attachInfo.mCanvas = canvas; 65 } 66 67 return bitmap; 68 }
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) { return null; } Bitmap bitmap = Bitmap.createBitmap(cacheBitmap); v.destroyDrawingCache(); v.setWillNotCacheDrawing(willNotCache); v.setDrawingCacheBackgroundColor(color); return bitmap; }