View轉換爲Bitmap及getDrawingCache

 

 View組件顯示的內容能夠經過cache機制保存爲bitmap, 使用到的api有android

   void  setDrawingCacheEnabled(boolean flag),
    Bitmap  getDrawingCache(boolean autoScale),
    void  buildDrawingCache(boolean autoScale),
    void  destroyDrawingCache()web

 

  •   咱們要獲取它的cache先要經過setDrawingCacheEnable方法把cache開啓,
  •   而後再調用getDrawingCache方法就能夠得到view的cache圖片了。
  • buildDrawingCache方法能夠不用調用,由於調用getDrawingCache方法時,若果cache沒有創建,系統會自動調用buildDrawingCache方法生成cache。若果要更新cache, 必需要調用destoryDrawingCache方法把舊的cache銷燬,才能創建新的。
  •  當調用setDrawingCacheEnabled方法設置爲false, 系統也會自動把原來的cache銷燬。

 

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     }
View Code

 

    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;
    }
相關文章
相關標籤/搜索