View的getDrawingCache為空白,解決辦法

來源:互聯網
上載者:User

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

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());

這行一代碼需要理解 ;

上面個人見解,如有理解不對的地方,大神留言指導啊........

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.