1.已經顯示出來的:
layoutParams.setMargins(0, 0, 0, 0);
view.setLayoutParams(layoutParams);
這兩天幫同事解決一個問題;
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,而源碼裏可以看到這樣代碼:
- if (width <= 0 || height <= 0 ||
- // Projected bitmap size in bytes
- (width * height * (opaque && !use32BitCache ? 2 : 4) >
- ViewConfiguration.get(mContext).getScaledMaximumDrawingCacheSize())) {
- destroyDrawingCache();
- mCachingFailed = true;
- return;
- }
當我們在buildDrawingCache的時候,系統給了我們默認最大的DrawingCacheSize爲屏幕寬*高*4;而我的View的CacheSize大小超過了某些設備默認值,就會導致獲得爲空;開始想着用反射的方法去改變這些屬性,或者設置背景顏色來改變圖片質量,這樣一來CacheSize大小 就可能會變小,但是這樣始終不能達到效果;
最終解決方案:
查看系統buildDrawingCache方法可以看到:
- public void buildDrawingCache(boolean autoScale) {
- if ((mPrivateFlags & DRAWING_CACHE_VALID) == 0 || (autoScale ?
- mDrawingCache == null : mUnscaledDrawingCache == null)) {
- mCachingFailed = false;
- if (ViewDebug.TRACE_HIERARCHY) {
- ViewDebug.trace(this, ViewDebug.HierarchyTraceType.BUILD_CACHE);
- }
- int width = mRight - mLeft;
- int height = mBottom - mTop;
- final AttachInfo attachInfo = mAttachInfo;
- final boolean scalingRequired = attachInfo != null && attachInfo.mScalingRequired;
- if (autoScale && scalingRequired) {
- width = (int) ((width * attachInfo.mApplicationScale) + 0.5f);
- height = (int) ((height * attachInfo.mApplicationScale) + 0.5f);
- }
- final int drawingCacheBackgroundColor = mDrawingCacheBackgroundColor;
- final boolean opaque = drawingCacheBackgroundColor != 0 || isOpaque();
- final boolean use32BitCache = attachInfo != null && attachInfo.mUse32BitDrawingCache;
- if (width <= 0 || height <= 0 ||
- // Projected bitmap size in bytes
- (width * height * (opaque && !use32BitCache ? 2 : 4) >
- ViewConfiguration.get(mContext).getScaledMaximumDrawingCacheSize())) {
- destroyDrawingCache();
- mCachingFailed = true;
- return;
- }
- boolean clear = true;
- Bitmap bitmap = autoScale ? mDrawingCache : mUnscaledDrawingCache;
- if (bitmap == null || bitmap.getWidth() != width || bitmap.getHeight() != height) {
- Bitmap.Config quality;
- if (!opaque) {
- // Never pick ARGB_4444 because it looks awful
- // Keep the DRAWING_CACHE_QUALITY_LOW flag just in case
- switch (mViewFlags & DRAWING_CACHE_QUALITY_MASK) {
- case DRAWING_CACHE_QUALITY_AUTO:
- quality = Bitmap.Config.ARGB_8888;
- break;
- case DRAWING_CACHE_QUALITY_LOW:
- quality = Bitmap.Config.ARGB_8888;
- break;
- case DRAWING_CACHE_QUALITY_HIGH:
- quality = Bitmap.Config.ARGB_8888;
- break;
- default:
- quality = Bitmap.Config.ARGB_8888;
- break;
- }
- } else {
- // Optimization for translucent windows
- // If the window is translucent, use a 32 bits bitmap to benefit from memcpy()
- quality = use32BitCache ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565;
- }
- // Try to cleanup memory
- if (bitmap != null) bitmap.recycle();
- try {
- bitmap = Bitmap.createBitmap(width, height, quality);
- bitmap.setDensity(getResources().getDisplayMetrics().densityDpi);
- if (autoScale) {
- mDrawingCache = bitmap;
- } else {
- mUnscaledDrawingCache = bitmap;
- }
- if (opaque && use32BitCache) bitmap.setHasAlpha(false);
- } catch (OutOfMemoryError e) {
- // If there is not enough memory to create the bitmap cache, just
- // ignore the issue as bitmap caches are not required to draw the
- // view hierarchy
- if (autoScale) {
- mDrawingCache = null;
- } else {
- mUnscaledDrawingCache = null;
- }
- mCachingFailed = true;
- return;
- }
- clear = drawingCacheBackgroundColor != 0;
- }
- Canvas canvas;
- if (attachInfo != null) {
- canvas = attachInfo.mCanvas;
- if (canvas == null) {
- canvas = new Canvas();
- }
- canvas.setBitmap(bitmap);
- // Temporarily clobber the cached Canvas in case one of our children
- // is also using a drawing cache. Without this, the children would
- // steal the canvas by attaching their own bitmap to it and bad, bad
- // thing would happen (invisible views, corrupted drawings, etc.)
- attachInfo.mCanvas = null;
- } else {
- // This case should hopefully never or seldom happen
- canvas = new Canvas(bitmap);
- }
- if (clear) {
- bitmap.eraseColor(drawingCacheBackgroundColor);
- }
- computeScroll();
- final int restoreCount = canvas.save();
- if (autoScale && scalingRequired) {
- final float scale = attachInfo.mApplicationScale;
- canvas.scale(scale, scale);
- }
- canvas.translate(-mScrollX, -mScrollY);
- mPrivateFlags |= DRAWN;
- if (mAttachInfo == null || !mAttachInfo.mHardwareAccelerated ||
- mLayerType != LAYER_TYPE_NONE) {
- mPrivateFlags |= DRAWING_CACHE_VALID;
- }
- // Fast path for layouts with no backgrounds
- if ((mPrivateFlags & SKIP_DRAW) == SKIP_DRAW) {
- if (ViewDebug.TRACE_HIERARCHY) {
- ViewDebug.trace(this, ViewDebug.HierarchyTraceType.DRAW);
- }
- mPrivateFlags &= ~DIRTY_MASK;
- dispatchDraw(canvas);
- } else {
- draw(canvas);
- }
- canvas.restoreToCount(restoreCount);
- canvas.setBitmap(null);
- if (attachInfo != null) {
- // Restore the cached Canvas for our siblings
- attachInfo.mCanvas = canvas;
- }
- }
- }
- /**
- * Create a snapshot of the view into a bitmap. We should probably make
- * some form of this public, but should think about the API.
- */
- Bitmap createSnapshot(Bitmap.Config quality, int backgroundColor, boolean skipChildren) {
- int width = mRight - mLeft;
- int height = mBottom - mTop;
- final AttachInfo attachInfo = mAttachInfo;
- final float scale = attachInfo != null ? attachInfo.mApplicationScale : 1.0f;
- width = (int) ((width * scale) + 0.5f);
- height = (int) ((height * scale) + 0.5f);
- Bitmap bitmap = Bitmap.createBitmap(width > 0 ? width : 1, height > 0 ? height : 1, quality);
- if (bitmap == null) {
- throw new OutOfMemoryError();
- }
- Resources resources = getResources();
- if (resources != null) {
- bitmap.setDensity(resources.getDisplayMetrics().densityDpi);
- }
- Canvas canvas;
- if (attachInfo != null) {
- canvas = attachInfo.mCanvas;
- if (canvas == null) {
- canvas = new Canvas();
- }
- canvas.setBitmap(bitmap);
- // Temporarily clobber the cached Canvas in case one of our children
- // is also using a drawing cache. Without this, the children would
- // steal the canvas by attaching their own bitmap to it and bad, bad
- // things would happen (invisible views, corrupted drawings, etc.)
- attachInfo.mCanvas = null;
- } else {
- // This case should hopefully never or seldom happen
- canvas = new Canvas(bitmap);
- }
- if ((backgroundColor & 0xff000000) != 0) {
- bitmap.eraseColor(backgroundColor);
- }
- computeScroll();
- final int restoreCount = canvas.save();
- canvas.scale(scale, scale);
- canvas.translate(-mScrollX, -mScrollY);
- // Temporarily remove the dirty mask
- int flags = mPrivateFlags;
- mPrivateFlags &= ~DIRTY_MASK;
- // Fast path for layouts with no backgrounds
- if ((mPrivateFlags & SKIP_DRAW) == SKIP_DRAW) {
- dispatchDraw(canvas);
- } else {
- draw(canvas);
- }
- mPrivateFlags = flags;
- canvas.restoreToCount(restoreCount);
- canvas.setBitmap(null);
- if (attachInfo != null) {
- // Restore the cached Canvas for our siblings
- attachInfo.mCanvas = canvas;
- }
- return bitmap;
- }
生成DrawingCache的過程貌似就是利用獲得View的Canvas然後畫到bitmap上,直接返回對應 的bitmap,這樣一來,就是我們用getDrawingCache獲得的bitmap;跟我們直接將View畫到bitmap貌似區別 不是很大,受啓發;如下:
自己生成Bitmap;
- public static Bitmap loadBitmapFromView(View v, boolean isParemt) {
- if (v == null) {
- return null;
- }
- Bitmap screenshot;
- screenshot = Bitmap.createBitmap(v.getWidth(), v.getHeight(), HDConstantSet.BITMAP_QUALITY);
- Canvas c = new Canvas(screenshot);
- v.draw(c);
- return screenshot;
- }
這樣也就將View生成了我們需要的bitmap了,但是有些情況下:比如ViewPager在用getDrawingCache和我自己生成的Bitmap時候,會有區別,ViewPager第一屏是正常的,滑動到第二屏幕的時候,我手動生成的Bitmap不見了,而系統getDrawingCache方法生成 的Bitmap是可見的,鬱悶,,,詳細看了一下系統buildDrawingCache訪求,發現在Canvas繪製Bitmap之後,多了一個步驟:
- computeScroll();
- final int restoreCount = canvas.save();
- canvas.scale(scale, scale);
- canvas.translate(-mScrollX, -mScrollY);
很明顯,系統Canvas,對默認位置進行了移動,即啓發:我們在用哥滑動View獲得它的Bitmap時候,獲得的是整個View的區域(包括隱藏的),如果想得到當前區域,需要重新定位到當前可顯示的區域;自己的代碼修改:
- public static Bitmap loadBitmapFromView(View v, boolean isParemt) {
- if (v == null) {
- return null;
- }
- Bitmap screenshot;
- screenshot = Bitmap.createBitmap(v.getWidth(), v.getHeight(), HDConstantSet.BITMAP_QUALITY);
- Canvas c = new Canvas(screenshot);
- c.translate(-v.getScrollX(), -v.getScrollY());
- v.draw(c);
- return screenshot;
- }
完美解決用自己生成 的Bitmap替換系統的getDrawingCache()方法;
當然系統getDrawingCache()考慮的因素很多,這一些我們也可以自己直接定義,比如透明磁,大小 ,圖片質量等;最重要就是
- c.translate(-v.getScrollX(), -v.getScrollY());