onCreate中加载图片至自定义Canvas视图无效的问题与解决
我自定义了一个包含Canvas的View,在Activity的onCreate方法中从内部存储加载Bitmap并设置到该Canvas,但图片无法显示;不过在OnClickListener中执行相同的设置Bitmap操作时,图片却能正常显示。请问问题出在哪里?如何在Activity启动时自动将图片加载到Canvas?
我的代码如下:
protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_sign); signatureView = findViewById(R.id.signView); clearBtn = findViewById(R.id.clearBtn); acceptSignBtn = findViewById(R.id.acceptSignBtn); String fileName = getIntent().getStringExtra("photo"); try { FileInputStream fis = openFileInput(fileName); byte[] byteArray = new byte[getIntent().getIntExtra("size", fis.available())]; fis.read(byteArray); fis.close(); photo = BitmapFactory.decodeByteArray(byteArray, 0, byteArray.length); photo = photo.copy(Bitmap.Config.ARGB_8888, true); Log.i("TAG", "load done" +photo.getHeight()); signatureView.setBitmap(photo); //There is no result, canvas stay clear } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } clearBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { signatureView.clearCanvas(); signatureView.setBitmap(photo); //after click photo appears } }); }
问题出在哪?
核心原因是View的绘制时序问题:
在Activity的onCreate方法执行阶段,你的自定义signatureView还没完成「测量(measure)」和「布局(layout)」流程——此时View的宽高还没确定,甚至View的第一次onDraw可能已经跑完了。你这时候调用setBitmap,虽然Bitmap已经传给了View,但没有触发View的重新绘制,所以图片自然不会显示。
而点击按钮的时候,View早就完成了所有初始化和绘制流程,这时候调用setBitmap,要么是按钮点击事件间接触发了View的重绘,要么是你的clearCanvas方法里已经调用了invalidate()(触发重绘),所以图片能正常显示出来。
怎么解决?
这里有几个简单可靠的方案,任选其一即可:
方案1:用View的post方法延迟执行
post会把任务放到View的消息队列里,等View彻底完成布局和初始化后再执行,这样就能保证View已经准备好接收Bitmap并绘制:
// 把onCreate里的signatureView.setBitmap(photo);替换成下面这段 signatureView.post(new Runnable() { @Override public void run() { signatureView.setBitmap(photo); } });
方案2:在自定义View的setBitmap里主动触发重绘
检查一下你的SignatureView类里的setBitmap方法,如果没有调用invalidate(),一定要加上——这个方法会告诉系统:「这个View内容变了,赶紧重新画一遍!」
// 自定义SignatureView中的setBitmap方法示例 public void setBitmap(Bitmap bitmap) { this.mBitmap = bitmap; // 关键:触发View重绘 invalidate(); }
这样哪怕你在onCreate里调用setBitmap,后续View完成布局后,invalidate()会触发onDraw,图片就能正常显示了。
方案3:在Activity的onWindowFocusChanged中加载
当Activity获取到窗口焦点时,所有View都已经完成布局了,这时候再设置Bitmap肯定没问题:
@Override public void onWindowFocusChanged(boolean hasFocus) { super.onWindowFocusChanged(hasFocus); // 确保只有第一次获取焦点且photo不为空时执行 if (hasFocus && photo != null) { signatureView.setBitmap(photo); } }
内容的提问来源于stack exchange,提问作者Fleczer

