解析View的getDrawingCache方法

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;
}
时间: 2024-10-06 12:19:58

解析View的getDrawingCache方法的相关文章

ScrollView嵌套子View的getDrawingCache为空的解决方法

ScrollView嵌套子View的getDrawingCache为空的解决方法 问题 将组件的显示布局改为可以滚动的,然后用ScrollView作为了View的父类,发现View的getDrawingCache返回为null了,组件的滚动是必须要实现的,所以探究一下getDrawingCache为空的解决方法. 问题原因 查看日志可以发现 `02-01 14:21:33.512 3461-3461/com.example.sample.job W/View: View too large to

Android自定义View的实现方法,带你一步步深入了解View(四)

转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/17357967 不 知不觉中,带你一步步深入了解View系列的文章已经写到第四篇了,回顾一下,我们一共学习了LayoutInflater的原理分析.视图的绘制流程. 视图的状态及重绘等知识,算是把View中很多重要的知识点都涉及到了.如果你还没有看过我前面的几篇文章,建议先去阅读一下,多了解一些原理方面的东 西. 之前我有承诺过,会在View这个话题上多写几篇博客,讲一讲View的

View的post方法导致的内存泄漏分析

简述: 写这篇文章的缘由是最近项目中查内存泄漏时,发现最终原因是由于异步线程调用View的的post方法导致的. 为何我会使用异步线程调用View的post方法,是因为项目中需要用到很多复杂的自定义布局,需要提前解析进入内存,防止在主线程解析导致卡顿,具体的实现方法是在Application启动的时候,使用异步线程解析这些布局,等需要使用的时候直接从内存中拿来用. 造成内存泄漏的原因,需要先分析View的post方法执行流程,也就是文章前半部分的内容 文章内容: View#post方法作用以及实

Android View体系(八)从源码解析View的layout和draw流程

相关文章 Android View体系(一)视图坐标系 Android View体系(二)实现View滑动的六种方法 Android View体系(三)属性动画 Android View体系(四)从源码解析Scroller Android View体系(五)从源码解析View的事件分发机制 Android View体系(六)从源码解析Activity的构成 Android View体系(七)从源码解析View的measure流程 前言 上一篇文章我们讲了View的measure的流程,接下来我们

Android View体系(八)从源代码解析View的layout和draw流程

相关文章 Android View体系(一)视图坐标系 Android View体系(二)实现View滑动的六种方法 Android View体系(三)属性动画 Android View体系(四)从源代码解析Scroller Android View体系(五)从源代码解析View的事件分发机制 Android View体系(六)从源代码解析Activity的构成 Android View体系(七)从源代码解析View的measure流程 前言 上一篇文章我们讲了View的measure的流程.接

Android View体系(七)从源码解析View的measure流程

相关文章 Android View体系(一)视图坐标系 Android View体系(二)实现View滑动的六种方法 Android View体系(三)属性动画 Android View体系(四)从源码解析Scroller Android View体系(五)从源码解析View的事件分发机制 Android View体系(六)从源码解析Activity的构成 前言 在上一篇我们了解了Activity的构成后,开始了解一下View的工作流程,就是measure.layout和draw.measure

Android View体系(五)从源码解析View的事件分发机制

相关文章 Android View体系(一)视图坐标系 Android View体系(二)实现View滑动的六种方法 Android View体系(三)属性动画 Android View体系(四)从源码解析Scroller 前言 三年前写过事件分发机制的文章但是写的不是很好,所以重新再写一篇,关于事件分发机制的文章已经有很多,但是希望我这篇是最简洁.最易懂的一篇. 1.处理点击事件的方法 View的层级 我们知道View的结构是树形的结构,View可以放在ViewGroup中,这个ViewGro

【转】Android自定义View的实现方法,带你一步步深入了解View(四)

原文网址: 转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/17357967 不知不觉中,带你一步步深入了解View系列的文章已经写到第四篇了,回顾一下,我们一共学习了LayoutInflater的原理分析.视图的绘制流程.视图的状态及重绘等知识,算是把View中很多重要的知识点都涉及到了.如果你还没有看过我前面的几篇文章,建议先去阅读一下,多了解一些原理方面的东西. 之前我有承诺过,会在View这个话题上多写几篇博客,讲一讲Vi

DNS高级应用:子域授权、转发和view的实现方法

DNS高级应用:子域授权.转发和view的实现方法 在上面的功能实现之前我们先来复习一下DNS 一些配置的基本格式: ACL: acl string { address_match_element; address_match_element; ... }; ZONE : #主服务器 zone string optional_class {         type  master ;         file quoted_string;         ...         }; #从服务