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; }