前言
我们经常会用到将一个View转换为Bitmap的情景,比如截屏保存到本地,比如一些动态的实时View为便于观察和记录数据、需要临时生成静态的Bitmap等等。
我们一般都会这样写:
public static Bitmap convertViewToBitmap(View view){
view.buildDrawingCache();
Bitmap bitmap = view.getDrawingCache();
return bitmap;
}
一般情况这种是没有问题的,但是view过大的话,就会buildDrawingCache()
失败,当然我们getDrawingCache()
得到的bitmap就为null了!
原因
那么为什么会出现这种情况呢?我们可以从源码中看到:
buildDrawingCache(boolean autoScale)
---->buildDrawingCacheImpl(boolean autoScale)
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;
}
width
和height
是所要cache的view绘制的宽度和高度,所以(width * height * (opaque && !use32BitCache ? 2 : 4)
计算的是当前所需要的cache大小。
ViewConfiguration.get(mContext).getScaledMaximumDrawingCacheSize()
得到的是系统所提供的最大的DrawingCache的值。
当所需要的```drawingCache`` >系统所提供的最大DrawingCache值时,生成Bitmap就会出现问题,此时获取的Bitmap就为null。
解决方案
我在stackoverflow上面找到一个类似的回答:
Android View.getDrawingCache returns null, only null
所以,我们应该这样写:
public static Bitmap convertViewToBitmap(View view){
view.setDrawingCacheEnabled(true);
view.measure(View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());
view.buildDrawingCache();
Bitmap bitmap = view.getDrawingCache();
view.setDrawingCacheEnabled(false);
return bitmap;
}
替代方案
从源码中可以看到buildDrawingCache
过程貌似就是利用获得View的Canvas然后画到bitmap上,直接返回对应的bitmap,这样一来,就是我们用getDrawingCache获得的bitmap;跟我们直接将View画到bitmap区别不是很大,所以我们可以自己直接画:
public static Bitmap convertViewToBitmap(View view) {
Bitmap bitmap= Bitmap.createBitmap(view.getWidth(), view.getHeight(), HDConstantSet.BITMAP_QUALITY);
Canvas canvas = new Canvas(bitmap);
view.draw(canvas);
return bitmap;
}
另外可能要这样的问题:就是在滑动的view中这样操作的话会产生问题,具体的可以参照这篇博客:
View的getDrawingCache为空,解决办法
ScrollView转bitmap
这里我们使用直接绘制的办法:
public Bitmap scrollToBitmap(ScrollView scrollView) {
int h = 0;
Bitmap bitmap;
for (int i = 0; i < scrollView.getChildCount(); i++) {
h += scrollView.getChildAt(i).getHeight();
}
bitmap = Bitmap.createBitmap(scrollView.getWidth(), h, Bitmap.Config.RGB_565);
Canvas canvas = new Canvas(bitmap);
canvas.drawColor(Color.WHITE);
scrollView.draw(canvas);
return bitmap;
}