View转换为Bitmap及getDrawingCache

 

 View组件显示的内容可以通过cache机制保存为bitmap, 使用到的api有

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

 

  •   我们要获取它的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函数,效率相对高出许多。 

 

view的getDrawingCache获得数据始终为null

setDrawingCacheEnabled

1 public void setDrawingCacheEnabled(boolean enabled) {
2         setFlags(enabled ? DRAWING_CACHE_ENABLED : 0, DRAWING_CACHE_ENABLED);
3     }

DRAWING_CACHE_ENABLED是否支持设置

先看getDrawingCache的源码

 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  这个值为true

2) (mViewFlags & DRAWING_CACHE_ENABLED) == DRAWING_CACHE_ENABLED

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

 


 

 

原文地址:https://www.cnblogs.com/mingfeng002/p/3305051.html