view轉bitmap

1.已經顯示出來的:

 //在將View轉爲BitMap的時候,View有一個私有變量mDrawingCache,就是所謂的cache
 //清空緩存,釋放資源,mDrawingCache將調用recycle
 mListView.destroyDrawingCache();
 //設置視圖是否開啓緩存
 mListView.setDrawingCacheEnabled(true);
 //會調用buildDrawingCache,將View轉換爲bitmap並賦值給mDrawingCache
 Bitmap bitmap = mListView.getDrawingCache(true);
 //bitmap轉爲Drawable對象
 Drawable drawable=new BitmapDrawable(null,bitmap);
 mImageView.setBackground(drawable);
 這裏有一點需要注意的是:被轉爲Drawable的View不能被隱藏,一旦被隱藏,它剛剛創建的Bitmap緩存將被回收,後面的setBackground也就會 
 報錯。如果想隱藏該View,可以通過另外一種方式,將剛纔的bitmap拷貝一份給一個新的bitmap對象就可以了:
 Bitmap bitmap=Bitmap.createBitmap(mListView.getDrawingCache(true));
 這樣就可以隱藏View了。如果使用這種方式,那麼以下兩個方法也就可以調用了:
 mListView.destroyDrawingCache();
 mListView.setDrawingCacheEnabled(false);


2.將未顯示的View轉爲Drawable對象
就是我們通過LayoutInflate加載的視圖,他還沒有在界面中展示的,所以在轉換之前需要設置寬和高,代碼大致如下:
mListView.measure(
                  View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
                  View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
mListView.layout(0, 0, mListView.getMeasuredWidth(), mListView.getMeasuredHeight());
//下面的代碼和上面的是一樣的
mListView.setDrawingCacheEnabled(true);
Bitmap bitmap=Bitmap.createBitmap(mListView.getDrawingCache(true));
Drawable drawable=new BitmapDrawable(null,bitmap);
mListView.destroyDrawingCache();
mListView.setDrawingCacheEnabled(false);
mImageView.setBackground(drawable);
對於這種未顯示的View,如果View的寬和高在佈局文件中設置,在一些Android版本上getDrawingCache獲取的可能爲null,可以通過下面的方式解決,在代碼中設置View的寬和高參數:
RelativeLayout.LayoutParams layoutParams=new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT,LayoutParams.MATCH_PARENT);
layoutParams.setMargins(0, 0, 0, 0);
view.setLayoutParams(layoutParams);
這裏有一篇文章給出了另外一種解決方式:
http://blog.csdn.net/huangbiao86/article/details/9053429

這兩天幫同事解決一個問題;

View.getDrawingCache獲得數據始終爲null,但是在某些設備上並不爲null,糾結夠 久啊,網上說了一些原因:

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,而我的設備分辨率爲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) {  
  2.         if ((mPrivateFlags & DRAWING_CACHE_VALID) == 0 || (autoScale ?  
  3.                 mDrawingCache == null : mUnscaledDrawingCache == null)) {  
  4.             mCachingFailed = false;  
  5.   
  6.             if (ViewDebug.TRACE_HIERARCHY) {  
  7.                 ViewDebug.trace(this, ViewDebug.HierarchyTraceType.BUILD_CACHE);  
  8.             }  
  9.   
  10.             int width = mRight - mLeft;  
  11.             int height = mBottom - mTop;  
  12.   
  13.             final AttachInfo attachInfo = mAttachInfo;  
  14.             final boolean scalingRequired = attachInfo != null && attachInfo.mScalingRequired;  
  15.   
  16.             if (autoScale && scalingRequired) {  
  17.                 width = (int) ((width * attachInfo.mApplicationScale) + 0.5f);  
  18.                 height = (int) ((height * attachInfo.mApplicationScale) + 0.5f);  
  19.             }  
  20.   
  21.             final int drawingCacheBackgroundColor = mDrawingCacheBackgroundColor;  
  22.             final boolean opaque = drawingCacheBackgroundColor != 0 || isOpaque();  
  23.             final boolean use32BitCache = attachInfo != null && attachInfo.mUse32BitDrawingCache;  
  24.   
  25.             if (width <= 0 || height <= 0 ||  
  26.                      // Projected bitmap size in bytes  
  27.                     (width * height * (opaque && !use32BitCache ? 2 : 4) >  
  28.                             ViewConfiguration.get(mContext).getScaledMaximumDrawingCacheSize())) {  
  29.                 destroyDrawingCache();  
  30.                 mCachingFailed = true;  
  31.                 return;  
  32.             }  
  33.   
  34.             boolean clear = true;  
  35.             Bitmap bitmap = autoScale ? mDrawingCache : mUnscaledDrawingCache;  
  36.   
  37.             if (bitmap == null || bitmap.getWidth() != width || bitmap.getHeight() != height) {  
  38.                 Bitmap.Config quality;  
  39.                 if (!opaque) {  
  40.                     // Never pick ARGB_4444 because it looks awful  
  41.                     // Keep the DRAWING_CACHE_QUALITY_LOW flag just in case  
  42.                     switch (mViewFlags & DRAWING_CACHE_QUALITY_MASK) {  
  43.                         case DRAWING_CACHE_QUALITY_AUTO:  
  44.                             quality = Bitmap.Config.ARGB_8888;  
  45.                             break;  
  46.                         case DRAWING_CACHE_QUALITY_LOW:  
  47.                             quality = Bitmap.Config.ARGB_8888;  
  48.                             break;  
  49.                         case DRAWING_CACHE_QUALITY_HIGH:  
  50.                             quality = Bitmap.Config.ARGB_8888;  
  51.                             break;  
  52.                         default:  
  53.                             quality = Bitmap.Config.ARGB_8888;  
  54.                             break;  
  55.                     }  
  56.                 } else {  
  57.                     // Optimization for translucent windows  
  58.                     // If the window is translucent, use a 32 bits bitmap to benefit from memcpy()  
  59.                     quality = use32BitCache ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565;  
  60.                 }  
  61.   
  62.                 // Try to cleanup memory  
  63.                 if (bitmap != null) bitmap.recycle();  
  64.   
  65.                 try {  
  66.                     bitmap = Bitmap.createBitmap(width, height, quality);  
  67.                     bitmap.setDensity(getResources().getDisplayMetrics().densityDpi);  
  68.                     if (autoScale) {  
  69.                         mDrawingCache = bitmap;  
  70.                     } else {  
  71.                         mUnscaledDrawingCache = bitmap;  
  72.                     }  
  73.                     if (opaque && use32BitCache) bitmap.setHasAlpha(false);  
  74.                 } catch (OutOfMemoryError e) {  
  75.                     // If there is not enough memory to create the bitmap cache, just  
  76.                     // ignore the issue as bitmap caches are not required to draw the  
  77.                     // view hierarchy  
  78.                     if (autoScale) {  
  79.                         mDrawingCache = null;  
  80.                     } else {  
  81.                         mUnscaledDrawingCache = null;  
  82.                     }  
  83.                     mCachingFailed = true;  
  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.             if (mAttachInfo == null || !mAttachInfo.mHardwareAccelerated ||  
  123.                     mLayerType != LAYER_TYPE_NONE) {  
  124.                 mPrivateFlags |= DRAWING_CACHE_VALID;  
  125.             }  
  126.   
  127.             // Fast path for layouts with no backgrounds  
  128.             if ((mPrivateFlags & SKIP_DRAW) == SKIP_DRAW) {  
  129.                 if (ViewDebug.TRACE_HIERARCHY) {  
  130.                     ViewDebug.trace(this, ViewDebug.HierarchyTraceType.DRAW);  
  131.                 }  
  132.                 mPrivateFlags &= ~DIRTY_MASK;  
  133.                 dispatchDraw(canvas);  
  134.             } else {  
  135.                 draw(canvas);  
  136.             }  
  137.   
  138.             canvas.restoreToCount(restoreCount);  
  139.             canvas.setBitmap(null);  
  140.   
  141.             if (attachInfo != null) {  
  142.                 // Restore the cached Canvas for our siblings  
  143.                 attachInfo.mCanvas = canvas;  
  144.             }  
  145.         }  
  146.     }  
  147.   
  148.     /** 
  149.      * Create a snapshot of the view into a bitmap.  We should probably make 
  150.      * some form of this public, but should think about the API. 
  151.      */  
  152.     Bitmap createSnapshot(Bitmap.Config quality, int backgroundColor, boolean skipChildren) {  
  153.         int width = mRight - mLeft;  
  154.         int height = mBottom - mTop;  
  155.   
  156.         final AttachInfo attachInfo = mAttachInfo;  
  157.         final float scale = attachInfo != null ? attachInfo.mApplicationScale : 1.0f;  
  158.         width = (int) ((width * scale) + 0.5f);  
  159.         height = (int) ((height * scale) + 0.5f);  
  160.   
  161.         Bitmap bitmap = Bitmap.createBitmap(width > 0 ? width : 1, height > 0 ? height : 1, quality);  
  162.         if (bitmap == null) {  
  163.             throw new OutOfMemoryError();  
  164.         }  
  165.   
  166.         Resources resources = getResources();  
  167.         if (resources != null) {  
  168.             bitmap.setDensity(resources.getDisplayMetrics().densityDpi);  
  169.         }  
  170.   
  171.         Canvas canvas;  
  172.         if (attachInfo != null) {  
  173.             canvas = attachInfo.mCanvas;  
  174.             if (canvas == null) {  
  175.                 canvas = new Canvas();  
  176.             }  
  177.             canvas.setBitmap(bitmap);  
  178.             // Temporarily clobber the cached Canvas in case one of our children  
  179.             // is also using a drawing cache. Without this, the children would  
  180.             // steal the canvas by attaching their own bitmap to it and bad, bad  
  181.             // things would happen (invisible views, corrupted drawings, etc.)  
  182.             attachInfo.mCanvas = null;  
  183.         } else {  
  184.             // This case should hopefully never or seldom happen  
  185.             canvas = new Canvas(bitmap);  
  186.         }  
  187.   
  188.         if ((backgroundColor & 0xff000000) != 0) {  
  189.             bitmap.eraseColor(backgroundColor);  
  190.         }  
  191.   
  192.         computeScroll();  
  193.         final int restoreCount = canvas.save();  
  194.         canvas.scale(scale, scale);  
  195.         canvas.translate(-mScrollX, -mScrollY);  
  196.   
  197.         // Temporarily remove the dirty mask  
  198.         int flags = mPrivateFlags;  
  199.         mPrivateFlags &= ~DIRTY_MASK;  
  200.   
  201.         // Fast path for layouts with no backgrounds  
  202.         if ((mPrivateFlags & SKIP_DRAW) == SKIP_DRAW) {  
  203.             dispatchDraw(canvas);  
  204.         } else {  
  205.             draw(canvas);  
  206.         }  
  207.   
  208.         mPrivateFlags = flags;  
  209.   
  210.         canvas.restoreToCount(restoreCount);  
  211.         canvas.setBitmap(null);  
  212.   
  213.         if (attachInfo != null) {  
  214.             // Restore the cached Canvas for our siblings  
  215.             attachInfo.mCanvas = canvas;  
  216.         }  
  217.   
  218.         return bitmap;  
  219.     }  

生成DrawingCache的過程貌似就是利用獲得View的Canvas然後畫到bitmap上,直接返回對應 的bitmap,這樣一來,就是我們用getDrawingCache獲得的bitmap;跟我們直接將View畫到bitmap貌似區別 不是很大,受啓發;如下:

自己生成Bitmap;

  1. public static Bitmap loadBitmapFromView(View v, boolean isParemt) {  
  2.         if (v == null) {  
  3.             return null;  
  4.         }  
  5.         Bitmap screenshot;  
  6.         screenshot = Bitmap.createBitmap(v.getWidth(), v.getHeight(), HDConstantSet.BITMAP_QUALITY);  
  7.         Canvas c = new Canvas(screenshot);  
  8.         v.draw(c);  
  9.         return screenshot;  
  10.     }  

這樣也就將View生成了我們需要的bitmap了,但是有些情況下:比如ViewPager在用getDrawingCache和我自己生成的Bitmap時候,會有區別,ViewPager第一屏是正常的,滑動到第二屏幕的時候,我手動生成的Bitmap不見了,而系統getDrawingCache方法生成 的Bitmap是可見的,鬱悶,,,詳細看了一下系統buildDrawingCache訪求,發現在Canvas繪製Bitmap之後,多了一個步驟:
  1. computeScroll();  
  2.        final int restoreCount = canvas.save();  
  3.        canvas.scale(scale, scale);  
  4.        canvas.translate(-mScrollX, -mScrollY);  

很明顯,系統Canvas,對默認位置進行了移動,即啓發:我們在用哥滑動View獲得它的Bitmap時候,獲得的是整個View的區域(包括隱藏的),如果想得到當前區域,需要重新定位到當前可顯示的區域;自己的代碼修改:


  1. public static Bitmap loadBitmapFromView(View v, boolean isParemt) {  
  2.         if (v == null) {  
  3.             return null;  
  4.         }  
  5.         Bitmap screenshot;  
  6.         screenshot = Bitmap.createBitmap(v.getWidth(), v.getHeight(), HDConstantSet.BITMAP_QUALITY);  
  7.         Canvas c = new Canvas(screenshot);  
  8.         c.translate(-v.getScrollX(), -v.getScrollY());  
  9.         v.draw(c);  
  10.         return screenshot;  
  11.     }  

完美解決用自己生成 的Bitmap替換系統的getDrawingCache()方法;

當然系統getDrawingCache()考慮的因素很多,這一些我們也可以自己直接定義,比如透明磁,大小 ,圖片質量等;最重要就是

  1. c.translate(-v.getScrollX(), -v.getScrollY());  
這行一代碼需要理解 ;

發佈了11 篇原創文章 · 獲贊 1 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章