解析View的getDrawingCache方法

来源:http://www.cnblogs.com/zhangmiao14/archive/2017/04/27/6772461.html
-Advertisement-
Play Games

1. View 的getDrawingCache方法 有時候需要將某個view的內容以圖片的方式保存下來,感覺就和截圖差不多,可以使用View 的getDrawingCache方法,返回一個Bitmap對象。 2. View的getDrawingCache的具體實現 查看View的getDrawin ...


1. View 的getDrawingCache方法

  有時候需要將某個view的內容以圖片的方式保存下來,感覺就和截圖差不多,可以使用View 的getDrawingCache方法,返回一個Bitmap對象。

2. View的getDrawingCache的具體實現

  查看View的getDrawingCache()方法

/** 
 * <p>Calling this method is equivalent to calling <code>getDrawingCache(false)</code>.</p> 
 * 
 * @return A non-scaled bitmap representing this view or null if cache is disabled. 
 * 
 * @see #getDrawingCache(boolean) 
 */ 
public Bitmap getDrawingCache() { 
    return getDrawingCache(false); 
}

  看代碼繼續調用了getDrawingCache(false)方法,繼續查看getDrawingCache(false)方法。

/** 
 * <p>Returns the bitmap in which this view drawing is cached. The returned bitmap 
 * is null when caching is disabled. If caching is enabled and the cache is not ready, 
 * this method will create it. Calling {@link #draw(android.graphics.Canvas)} will not 
 * draw from the cache when the cache is enabled. To benefit from the cache, you must 
 * request the drawing cache by calling this method and draw it on screen if the 
 * returned bitmap is not null.</p> 
 * 
 * <p>Note about auto scaling in compatibility mode: When auto scaling is not enabled, 
 * this method will create a bitmap of the same size as this view. Because this bitmap 
 * will be drawn scaled by the parent ViewGroup, the result on screen might show 
 * scaling artifacts. To avoid such artifacts, you should call this method by setting 
 * the auto scaling to true. Doing so, however, will generate a bitmap of a different 
 * size than the view. This implies that your application must be able to handle this 
 * size.</p> 
 * 
 * @param autoScale Indicates whether the generated bitmap should be scaled based on 
 *        the current density of the screen when the application is in compatibility 
 *        mode. 
 * 
 * @return A bitmap representing this view or null if cache is disabled. 
 * 
 * @see #setDrawingCacheEnabled(boolean) 
 * @see #isDrawingCacheEnabled() 
 * @see #buildDrawingCache(boolean) 
 * @see #destroyDrawingCache() 
 */ 
public Bitmap getDrawingCache(boolean autoScale) { 
    if ((mViewFlags & WILL_NOT_CACHE_DRAWING) == WILL_NOT_CACHE_DRAWING) { 
        return null; 
    } 
    if ((mViewFlags & DRAWING_CACHE_ENABLED) == DRAWING_CACHE_ENABLED) { 
        buildDrawingCache(autoScale); 
    } 
    return autoScale ? mDrawingCache : mUnscaledDrawingCache; 
}

  查看getDrawingCache(false)方法,如果該視圖的標誌是WILL_NOT_CACHE_DEAWING(表示該view沒有任何繪圖緩存)則直接返回null,如果視圖的標誌是DRWING_CACHE_ENABLED(表示該view將自己的繪圖緩存成一個bitmap),則調用buildDrawingCache(autoScale)方法。

  因為傳遞過來的autoScale為false,則返回的Bitmap是mUnscaledDrawingCache。

  查看buildDrawingCache(autoScale)方法:

/** 
 * <p>Forces the drawing cache to be built if the drawing cache is invalid.</p> 
 * 
 * <p>If you call {@link #buildDrawingCache()} manually without calling 
 * {@link #setDrawingCacheEnabled(boolean) setDrawingCacheEnabled(true)}, you 
 * should cleanup the cache by calling {@link #destroyDrawingCache()} afterwards.</p> 
 * 
 * <p>Note about auto scaling in compatibility mode: When auto scaling is not enabled, 
 * this method will create a bitmap of the same size as this view. Because this bitmap 
 * will be drawn scaled by the parent ViewGroup, the result on screen might show 
 * scaling artifacts. To avoid such artifacts, you should call this method by setting 
 * the auto scaling to true. Doing so, however, will generate a bitmap of a different 
 * size than the view. This implies that your application must be able to handle this 
 * size.</p> 
 * 
 * <p>You should avoid calling this method when hardware acceleration is enabled. If 
 * you do not need the drawing cache bitmap, calling this method will increase memory 
 * usage and cause the view to be rendered in software once, thus negatively impacting 
 * performance.</p> 
 * 
 * @see #getDrawingCache() 
 * @see #destroyDrawingCache() 
 */ 
public void buildDrawingCache(boolean autoScale) { 
    if ((mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == 0 || (autoScale ? 
            mDrawingCache == null : mUnscaledDrawingCache == null)) { 
        if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) { 
            Trace.traceBegin(Trace.TRACE_TAG_VIEW, 
                    "buildDrawingCache/SW Layer for " + getClass().getSimpleName()); 
        } 
        try { 
            buildDrawingCacheImpl(autoScale); 
        } finally { 
            Trace.traceEnd(Trace.TRACE_TAG_VIEW); 
        } 
    } 
}

  如果mPrivateFlags與PFLAG_DRAWING_CACHE_VALID與運算為0,或者mUnscaledDrawingCache為null,則調用buildDrawingCacheImpl(autoScale)方法。

查看buildDrawingCacheImpl(autoScale)方法,

/** 
 * private, internal implementation of buildDrawingCache, used to enable tracing 
 */

 

private void buildDrawingCacheImpl(boolean autoScale) {
    mCachingFailed = false;

    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;

    final long projectedBitmapSize = width * height * (opaque && !use32BitCache ? 2 : 4);
    final long drawingCacheSize =
            ViewConfiguration.get(mContext).getScaledMaximumDrawingCacheSize();
    if (width <= 0 || height <= 0 || projectedBitmapSize > drawingCacheSize) {
        if (width > 0 && height > 0) {
            Log.w(VIEW_LOG_TAG, getClass().getSimpleName() + " not displayed because it is"
                    + " too large to fit into a software layer (or drawing cache), needs "
                    + projectedBitmapSize + " bytes, only "
                    + drawingCacheSize + " available");
        }
        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:
                case DRAWING_CACHE_QUALITY_LOW:
                case DRAWING_CACHE_QUALITY_HIGH:
                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(mResources.getDisplayMetrics(),
                    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 |= PFLAG_DRAWN;
    if (mAttachInfo == null || !mAttachInfo.mHardwareAccelerated ||
            mLayerType != LAYER_TYPE_NONE) {
        mPrivateFlags |= PFLAG_DRAWING_CACHE_VALID;
    }

    // Fast path for layouts with no backgrounds
    if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
        mPrivateFlags &= ~PFLAG_DIRTY_MASK;
        dispatchDraw(canvas);
        if (mOverlay != null && !mOverlay.isEmpty()) {
            mOverlay.getOverlayView().draw(canvas);
        }
    } else {
        draw(canvas);
    }

    canvas.restoreToCount(restoreCount);
    canvas.setBitmap(null);

    if (attachInfo != null) {
        // Restore the cached Canvas for our siblings
        attachInfo.mCanvas = canvas;
    }
}


  代碼中AttachInfo類是連接到他的父window時給view的一組信息。

  參數autoScale的值為false,可以忽略一些if判斷語句。

  方法分為三步:

    第一步:得到view的寬width與高height。

int width = mRight - mLeft;
int height = mBottom - mTop;

    第二步,生成bitmap。

bitmap = Bitmap.createBitmap(mResources.getDisplayMetrics(),width, height, quality);
bitmap.setDensity(getResources().getDisplayMetrics().densityDpi);

  use32BitCache數據代表的是view是否需要使用32-bit繪圖緩存,當window為半透明的時候,使用32位繪圖緩存。

    第三步,繪製canvas。

canvas.setBitmap(bitmap);

canvas = new Canvas(bitmap);

draw(canvas);

3. 自定義view的getDrawingCache方法

  有時候自定義的view雖然繼承View,但是調用View的getDrawingCache()方法的時候會出現一些問題,返回的bitmap為null,這個時候就需要自己寫一個getDrawingCache方法,可以參考buildDrawingCacheImpl方法去實現,實現如下:

public Bitmap getBitmap() { 
    Bitmap bitmap = null; 
    int width = getRight() - getLeft(); 
    int height = getBottom() - getTop(); 
    final boolean opaque = getDrawingCacheBackgroundColor() != 0 || isOpaque(); 
    Bitmap.Config quality; 
    if (!opaque) { 
        switch (getDrawingCacheQuality()) { 
            case DRAWING_CACHE_QUALITY_AUTO: 
            case DRAWING_CACHE_QUALITY_LOW: 
            case DRAWING_CACHE_QUALITY_HIGH: 
            default: 
                quality = Bitmap.Config.ARGB_8888; 
                break; 
        } 
    } else { 
        quality = Bitmap.Config.RGB_565; 
    } 
    if (opaque) bitmap.setHasAlpha(false); 
    bitmap = Bitmap.createBitmap(getResources().getDisplayMetrics(), 
            width, height, quality); 
    bitmap.setDensity(getResources().getDisplayMetrics().densityDpi); 
    boolean clear = getDrawingCacheBackgroundColor() != 0; 
    Canvas canvas = new Canvas(bitmap); 
    if (clear) { 
        bitmap.eraseColor(getDrawingCacheBackgroundColor()); 
    } 
    computeScroll(); 
    final int restoreCount = canvas.save(); 
    canvas.translate(-getScrollX(), -getScrollY()); 
    draw(canvas); 
    canvas.restoreToCount(restoreCount); 
    canvas.setBitmap(null); 
    return bitmap; 
}

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • Gradle Gradle本地路徑設置 如果在AndroidStudio內設置了使用local的Gradle路徑,就直接放那邊,啥問題都不會有。如果使用推薦的設置,那麼更新的時候很有可能會有問題。 線上下載Gradle的速度超慢,遇到Gradle問題不要打開Android Studio,極易卡死,直 ...
  • 大部分app打開pdf文件是通過intent調起手機中能打開pdf文件的工具,來查看pdf文件,如果需求是,用戶在app內下載好pdf文件後,不通過第三方的工具,本地打開。 這樣的需求要怎麼實現呢?上網查了一些資料,發現了一個很好用PDF開源庫。 使用起來也很簡單,首先添加PDFView的引用 ...
  • 給 iOS 應用添加推送功能是一件比較麻煩的事情,本篇文章收集了集成 jpush react native 的常見問題,目的是為了幫助用戶更好地排查問題 1. 收不到推送 確保是在真機上測試,而不是在模擬器 自己的應用已經在 Apple developer 給應用配置推送功能,創建推送證書 (並且保 ...
  • 原文首發我的主力博客 http://anforen.com/wp/2017/04/android_ksoap2_unexpected_type_position_end_document_null_java_io_inputstreamreader/ jpg圖片文件較小時,比如200KB左右,一般不 ...
  • 找了好幾個都不能使用 試了一下這個 可以使用 放地址:http://www.orsoon.com/Mac/85386.html ...
  • 空指針異常: 04-27 01:13:57.270: E/AndroidRuntime(4942): FATAL EXCEPTION: main04-27 01:13:57.270: E/AndroidRuntime(4942): Process: com.itheima.mobilesafe74, ...
  • 轉載請註明出處: http://www.cnblogs.com/cnwutianhao/p/6772759.html MVP架構模式 大家都不陌生,Google 也給出過相應的參考 Sample, 但是有的人會有疑問為啥 GitHub 上面大神寫的 MVP架構模式 和 Google 的不太一樣。 G ...
  • 獲取新的設備UDID "蒲公英獲取UDID" 新設備中打開以上網址,按照安裝提示即可快速獲取UDID 開發者賬號中註冊新設備 更新項目相關的描述文件 確認之後重新下載描述文件。 確保xcode安裝(雙擊)的本地的描述文件是最新的 為了確保我們現在使用的描述文件與以前的不存在衝突,所以需要把舊的與應用 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...