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;
}

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

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

    ScrollView嵌套子View的getDrawingCache为空的解决方法 问题 将组件的显示布局改为可以滚动的,然后用ScrollView作为了View的父类,发现View的getDrawin ...

  2. 解析6种常用View 的滑动方法

    View 的滑动是Android 实现自定义控件的基础,实现View 滑动有很多种方法,在这里主要讲解6 种滑动方法,分别是layout().offsetLeftAndRight()与offsetTo ...

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

    转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/17357967 不知不觉中,带你一步步深入了解View系列的文章已经写到第四篇了,回 ...

  4. 从源码的角度解析View的事件分发

    有好多朋友问过我各种问题,比如:onTouch和onTouchEvent有什么区别,又该如何使用?为什么给ListView引入了一个滑动菜单的功能,ListView就不能滚动了?为什么图片轮播器里的图 ...

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

    原文网址: 转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/17357967 不知不觉中,带你一步步深入了解View系列的文章已经写到 ...

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

    相关文章 Android View体系(一)视图坐标系 Android View体系(二)实现View滑动的六种方法 Android View体系(三)属性动画 Android View体系(四)从源 ...

  7. spring mvc: 参数方法名称解析器(用参数来解析控制器下的方法)MultiActionController/ParameterMethodNameResolver/ControllerClassNameHandlerMapping

    spring mvc: 参数方法名称解析器(用参数来解析控制器下的方法)MultiActionController/ParameterMethodNameResolver/ControllerClas ...

  8. Android自己定义View的实现方法

    转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/17357967 不知不觉中,带你一步步深入了解View系列的文章已经写到第四篇了.回 ...

  9. 用jquery解析JSON数据的方法以及字符串转换成json的3种方法

    用jquery解析JSON数据的方法,作为jquery异步请求的传输对象,jquery请求后返回的结果是 json对象,这里考虑的都是服务器返回JSON形式的字符串的形式,对于利用JSONObject ...

随机推荐

  1. Android:百度地图 + 百度导航

    地图SDK 开发指南:http://lbsyun.baidu.com/index.php?title=androidsdk/guide/introduction 导航SDK开发指南:http://lb ...

  2. JAVA I/O 字符输出流简要概括

    偷个懒,直接参考上篇字符输入流Reader的形式,其实Reader和Writer本来就大同小异: 字符输出流Writer 本篇将对JAVA I/O流中的字符输出流Writer做个简单的概括: 总得来说 ...

  3. JavaScript 基础阶段测试题

    JavaScript 基础阶段测试题,试试你能得多少分? 一.选择题1.分析下段代码输出结果是( )    var arr = [2,3,4,5,6];    var sum =0;    for(v ...

  4. Apache网页的缓存时间

    配置网页缓存时间概述 通过mod_expires模块配置Apache,使网页能在客户端浏览器缓存一段时间,以避免重复请求,减轻服务端工作压力. 启用mod_expires模块后,会自动生成页面头部信息 ...

  5. 在SSRS的每一页重复显示table header

    现在在做一个关于SSRS报表展示的项目,但是我困顿在如何在table的每一页让table header重复显示.因为我在table属性中勾选了"Report header columns o ...

  6. iOS开发之UIApplication和delegate

    1.概述 所有的移动操作系统都有个致命的缺点:app很容易受到打扰.比如一个来电或者锁屏会导致app进入后台甚至被终止. 还有很多其它类似的情况会导致app受到干扰,在app受到干扰时,会产生一些系统 ...

  7. Mac端SVN工具CornerStone详解

    俗话说:"工欲善其事必先利其器": 对于我们程序员来说,不管你是大神,还是小鱼小虾,进入公司之后,都用过源码管理工具,不然你就不是一个合格的程序员,现在各个公司用于源码管理工具通常 ...

  8. 光环国际PRINCE2培训费是多少?

    光环国际学习PRINCE2新活动 第一重好礼:获得商务背包一个 第二重好礼:获得600元学习代金券一张 第三重好礼:获得高清流程图一张 活动时间:2017年3月20日-3月31日 PRINCE2 (P ...

  9. CentOS 7 安装Broadcom无线网卡驱动

    重新坑了小伙伴一台电脑,用来装centOS练习和做服务器用,哈哈哈 装了了CentOS 7后发现无线网卡读不出来,没有装驱动,网卡是Broadcom ╮(╯_╰)╭ 1.首先确定网卡是否为坑爹类型Br ...

  10. GDOI2014模拟 旅行【SPFA】

    旅行(travel) 从前有一位旅者,他想要游遍天下所有的景点.这一天他来到了一个神奇的王国:在这片土地上,有n个城市,从1到n进行编号.王国中有m条道路,第i条道路连接着两个城市ai,bi,由于年代 ...