View的getDrawingCache为空,解决办法

这两天帮同事解决一个问题;

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 caseswitch (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 memoryif (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 hierarchyif (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 happencanvas = 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 backgroundsif ((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 siblingsattachInfo.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 happencanvas = 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 maskint flags = mPrivateFlags;mPrivateFlags &= ~DIRTY_MASK;// Fast path for layouts with no backgroundsif ((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 siblingsattachInfo.mCanvas = canvas;}return bitmap;}生成DrawingCache的过程貌似就是利用获得View的Canvas然后画到bitmap上,直接返回对应 的bitmap,这样一来,就是我们用getDrawingCache获得的bitmap;跟我们直接将View画到bitmap貌似区别 不是很大,受启发;如下:我不去想是否能够成功,既然选择了远方,便只顾风雨兼程!

View的getDrawingCache为空,解决办法

相关文章:

你感兴趣的文章:

标签云: