解析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;
}
解析View的getDrawingCache方法的更多相关文章
- ScrollView嵌套子View的getDrawingCache为空的解决方法
ScrollView嵌套子View的getDrawingCache为空的解决方法 问题 将组件的显示布局改为可以滚动的,然后用ScrollView作为了View的父类,发现View的getDrawin ...
- 解析6种常用View 的滑动方法
View 的滑动是Android 实现自定义控件的基础,实现View 滑动有很多种方法,在这里主要讲解6 种滑动方法,分别是layout().offsetLeftAndRight()与offsetTo ...
- Android自定义View的实现方法,带你一步步深入了解View(四)
转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/17357967 不知不觉中,带你一步步深入了解View系列的文章已经写到第四篇了,回 ...
- 从源码的角度解析View的事件分发
有好多朋友问过我各种问题,比如:onTouch和onTouchEvent有什么区别,又该如何使用?为什么给ListView引入了一个滑动菜单的功能,ListView就不能滚动了?为什么图片轮播器里的图 ...
- 【转】Android自定义View的实现方法,带你一步步深入了解View(四)
原文网址: 转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/17357967 不知不觉中,带你一步步深入了解View系列的文章已经写到 ...
- Android View体系(八)从源代码解析View的layout和draw流程
相关文章 Android View体系(一)视图坐标系 Android View体系(二)实现View滑动的六种方法 Android View体系(三)属性动画 Android View体系(四)从源 ...
- spring mvc: 参数方法名称解析器(用参数来解析控制器下的方法)MultiActionController/ParameterMethodNameResolver/ControllerClassNameHandlerMapping
spring mvc: 参数方法名称解析器(用参数来解析控制器下的方法)MultiActionController/ParameterMethodNameResolver/ControllerClas ...
- Android自己定义View的实现方法
转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/17357967 不知不觉中,带你一步步深入了解View系列的文章已经写到第四篇了.回 ...
- 用jquery解析JSON数据的方法以及字符串转换成json的3种方法
用jquery解析JSON数据的方法,作为jquery异步请求的传输对象,jquery请求后返回的结果是 json对象,这里考虑的都是服务器返回JSON形式的字符串的形式,对于利用JSONObject ...
随机推荐
- 2208: [Jsoi2010]连通数
2208: [Jsoi2010]连通数 Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 1371 Solved: 557[Submit][Status ...
- 1083: [SCOI2005]繁忙的都市
1083: [SCOI2005]繁忙的都市 Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 1319 Solved: 878[Submit][Stat ...
- Spring Boot启动过程(四):Spring Boot内嵌Tomcat启动
之前在Spring Boot启动过程(二)提到过createEmbeddedServletContainer创建了内嵌的Servlet容器,我用的是默认的Tomcat. private void cr ...
- webots自学笔记(五)使用物理插件ODE建立铰链
原创文章,来自"博客园,_阿龙clliu" http://www.cnblogs.com/clliu/,转载请注明原文章出处. 在一些三维制图软件或仿真软件里,都有运动副的概念,w ...
- (14)jdk1.5开始的一些新特性:静态导入,增强for循环,可变参数,自动装箱/拆箱,枚举类型
Jdk1.5新特性之静态导入 jdk1.5新特性值静态导入 静态导入的作用:简化缩写 静态导入的作用:可以作用一个类的所有静态成员. 静态导入的格式:import static 包名.类名.静态的成员 ...
- 浅谈!SQL语句中LEFT JOIN ON WHERE和LEFT JOIN ON AND的区别
今天的工作学习之路是一个数据库的小知识,当时没有区分出所以然,特此记录分享一下子. 众所周知,数据库的表都是单独存在的,但是当我们进行联合查询(多表查询)时,我们获得数据库返回的值时就好像在一张表里一 ...
- 使用Android Studio导入第三方库项目
在使用Android Studio开发时,用到了第三方库SlidingMenu(现在已经不推荐使用了),尽管如此,但具体怎么导入第三方库还是需要知道的,在查阅各种资料后,知道了一种比较容易可行的方法 ...
- shell中的特殊变量和函数传参
shell中的特殊变量 $? :上一个命令的执行状态返回值 $#::参数的个数 $*:参数列表,所有的变量作为一个字符串 $@:参数列表,每个变量作为单个字符串 $1-9,${10}:位置参数 $$: ...
- Linux学习之sudo命令
在学习Linux用户管理时,我们不得不需要了解一个命令,那就是sudo.sudo的作用是切换身份,以其他身份来执行命令. 那么为什么在Linux系统中我们需要来切换身份呢?原因有以下几个方面 1.养成 ...
- java设计模式之职责链模式
责任链模式 设计模式很多时候都是看见名字都知道意思,责任链,见名知义为请求创建一系列处理对象. 此模式基于请求的类型将请求的发送方和接收方分离.通常每个接收器包含对另一个接收器的引用.如果一个对象不能 ...