Android Studio Chipmunk开发绘图App导出截图变暗问题
Android自定义绘图View导出图片发暗问题解决方案
问题现象
基于canvas.drawLines实现简易绘图应用时,App内onDraw渲染的画面显示完全正常,但将内容导出为PNG图片存入相册后,相册列表的缩略图预览显示正常,单独点开大图查看时会出现整体画面变暗、亮度低于屏幕实际显示效果的问题,先后尝试DrawingCache、手动创建Bitmap调用view.draw()等三种截图方案均未解决问题。
现有问题代码
1. onDraw绘制逻辑
public void onDraw(Canvas canvas) { setBackgroundColor(cfgBackground); //background //canvas.drawColor(ConfigActivity.getCfgBackground(ConfigActivity.cfgBackground)); //to keep background when export to PNG/JPG //title paint.setColor(0xbb020a0d); paint.setTextSize(40); canvas.drawText(Title, 0, 40, paint); paint.setColor(color); paint.setStrokeWidth(thickness); canvas.drawLines(lines, paint); }
2. 图片导出存储逻辑
File sdCard = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES); File file = new File(sdCard, imageName); FileOutputStream fos = null; try { // Make sure the Pictures directory exists. sdCard.mkdirs(); fos = new FileOutputStream(file); } catch (FileNotFoundException e) { Toast.makeText(getContext(), file + " open failed: " + e, Toast.LENGTH_SHORT).show(); e.printStackTrace(); return; } /* //method A: will get dark image setDrawingCacheEnabled(true); Bitmap b = getDrawingCache(); //b.setHasAlpha(true); //to set transparency //Canvas canvas = new Canvas(b); //canvas.drawColor(cfgBackground)); //trying to solve darken image b.compress(Bitmap.CompressFormat.PNG, 100, fos); setDrawingCacheEnabled(false); //for next screen update */ Bitmap b = screenShot(this); //Method B b.compress(Bitmap.CompressFormat.PNG, 100, fos); try { fos.flush(); fos.close(); } catch (IOException e) { Toast.makeText(getContext(), file + " save failed: " + e, Toast.LENGTH_SHORT).show(); e.printStackTrace(); } Toast.makeText(getContext(), file + " saved in Albums", Toast.LENGTH_SHORT).show(); //add to gallary getContext().sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.parse("file://" + file.toString())));
3. 已尝试的截图实现
//Method C public static Bitmap getBitmapFromView(View view) { //Define a bitmap with the same size as the view Bitmap returnedBitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(),Bitmap.Config.ARGB_8888); //Bind a canvas to it Canvas canvas = new Canvas(returnedBitmap); //Get the view's background Drawable bgDrawable =view.getBackground(); if (bgDrawable!=null) //has background drawable, then draw it on the canvas bgDrawable.draw(canvas); else //does not have background drawable, then draw white background on the canvas canvas.drawColor(cfgBackground); // draw the view on the canvas view.draw(canvas); //return the bitmap return returnedBitmap; } //Method B public Bitmap screenShot(View view) { Bitmap bitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(), Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(bitmap); view.draw(canvas); return bitmap; }
问题根因
这个问题是Android自定义View截图的常见问题,核心诱因有两个:
- 背景绘制逻辑不规范:把
setBackgroundColor(cfgBackground)写在onDraw方法里是错误用法——这个方法是修改View的属性,不是直接在当前Canvas上执行绘制。屏幕显示时,系统渲染管线会单独把View背景绘制到窗口Surface上,但离屏导出时,自己创建的软件Canvas不会完全复用硬件渲染的背景绘制逻辑,最终生成的Bitmap会带有透明Alpha通道。相册生成缩略图时会自动给透明图片垫白色底,所以预览正常;但单独打开大图时,不少相册App对带Alpha通道的PNG会按黑色底做预乘合成,直接导致整体画面发暗。 - 软硬件渲染路径差异:自定义View默认开启硬件加速,屏幕显示走硬件渲染管线,而离屏导出绑定的是软件Canvas,两种路径下抗锯齿、Alpha合成、色彩计算的逻辑存在细微差异,也会导致导出的图片亮度和屏幕显示不一致。
修复方案
按以下步骤修改即可解决问题:
- 修正onDraw绘制逻辑:把
setBackgroundColor调用移出onDraw(只需要在View初始化时调用一次即可,反复调用会触发不必要的重绘),在onDraw的最开头直接调用canvas.drawColor()绘制完全不透明的背景,保证不管是屏幕绘制还是离屏导出,背景都会直接画在当前Canvas上,彻底消除透明通道的影响。
修正后的onDraw代码:public void onDraw(Canvas canvas) { // 直接在当前画布绘制不透明背景,覆盖整个绘制区域 canvas.drawColor(cfgBackground); // 标题绘制 paint.setColor(0xbb020a0d); paint.setTextSize(40); canvas.drawText(Title, 0, 40, paint); paint.setColor(color); paint.setStrokeWidth(thickness); canvas.drawLines(lines, paint); } - 截图时临时切换到软件渲染:在调用截图方法前,临时把View的图层类型改为软件渲染,保证离屏绘制的逻辑和色彩计算和预期一致,截图完成后再切回硬件加速保证日常绘制的流畅度:
// 截图前临时关闭硬件加速 setLayerType(View.LAYER_TYPE_SOFTWARE, null); Bitmap b = screenShot(this); // 截图完成后恢复硬件加速 setLayerType(View.LAYER_TYPE_HARDWARE, null); b.compress(Bitmap.CompressFormat.PNG, 100, fos); - 可选优化:如果绘图应用不需要导出带透明背景的图片,可以把创建Bitmap时的配置从
Bitmap.Config.ARGB_8888改成Bitmap.Config.RGB_565,完全去掉Alpha通道,从格式上避免所有图片查看器对透明PNG的合成逻辑差异。 - 额外校验:检查
cfgBackground颜色值是否为完全不透明的色值(即色值最高两位为0xff),如果背景色本身带透明度,也会导致最终图片半透明发暗。
内容的提问来源于stack exchange,提问作者runningdog
相关产品推荐
相关产品推荐

