zoukankan      html  css  js  c++  java
  • 解析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; 
    }
  • 相关阅读:
    Ajax缓存处理
    单例模式
    正则表达式基础
    springmvc请求参数的绑定和获取
    springmvc返回视图(解析)
    RequestMapping请求映射方式
    springmvc注解基本入门
    springmvc简介
    Mybatis入门-动态sql
    Mybatis映射配置文件Mapper.xml详解
  • 原文地址:https://www.cnblogs.com/zhangmiao14/p/6772461.html
Copyright © 2011-2022 走看看