android - Android:激活相机后出现 nullponterException
问题描述
我正在为 android 开发简单的绘图应用程序。我的目标是:
- 从我的应用程序中打开相机
- 拍张照片
- 把这张照片画到画布上
- 画在这张照片上
激活相机后:
Intent takePhoto = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (takePhoto.resolveActivity(getPackageManager()) != null) {
startActivityForResult(takePhoto, CAMERA_INTENT_CALLED);
}
当我尝试将照片绘制到画布中时:
else if (requestCode == CAMERA_INTENT_CALLED) {
if (resultCode == RESULT_OK) {
Bundle extras = returnedIntent.getExtras();
Bitmap imageBitmap = (Bitmap) extras.get("data");
drawView.drawImage(imageBitmap);
}
}
我得到 NullPointerException:尝试调用虚拟方法说我的画布为空。将照片从画廊加载到画布上效果很好。可能原因是激活相机后我的活动被破坏了,所以我制作了一个包装器以使我的画布可序列化:
public class SerializableCanvas extends Canvas implements Serializable {
public SerializableCanvas(Bitmap canvasBitmap) {
super(canvasBitmap);
}
}
并试图保存我的画布:
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putSerializable("drawCanvas", drawView.getDrawCanvas());
}
并恢复它:
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
if (savedInstanceState != null) {
if (savedInstanceState.containsKey("drawCanvas")) {
drawView.setDrawCanvas((SerializableCanvas)savedInstanceState.getSerializable("drawCanvas"));
}
}
}
但现在我收到一条错误消息,指出此片段中的宽度和高度必须 >0:
public void drawImage(Bitmap image) {
drawCanvas.drawBitmap(resize(image, drawCanvas.getWidth(), drawCanvas.getHeight()), 0, 0, canvasPaint);
invalidate();
}
我也尝试保存整个 DrawingView 对象,但它太大并且里面有一个位图。我有 2 个类:MainActivity 和 DrawingView。如果相同的人知道如何克服这个问题,我将不胜感激。完整代码如下。
MainActivity 类:
public class MainActivity extends Activity implements View.OnClickListener {
private DrawingView drawView;
private ImageButton currPaint;
private ImageButton drawBtn;
private ImageButton eraseBtn;
private ImageButton newBtn;
private ImageButton saveBtn;
private ImageButton openBtn;
private ImageButton cameraBtn;
private float smallBrush;
private float mediumBrush;
private float largeBrush;
private Drawable image;
private final int GALLERY_INTENT_CALLED = 1;
private final int CAMERA_INTENT_CALLED = 2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_main);
drawView = (DrawingView)findViewById(R.id.drawing);
LinearLayout paintLayout = (LinearLayout)findViewById(R.id.paint_colors);
currPaint = (ImageButton)paintLayout.getChildAt(0);
currPaint.setImageDrawable(getResources().getDrawable(R.drawable.paint_pressed));
smallBrush = getResources().getInteger(R.integer.small_size);
mediumBrush = getResources().getInteger(R.integer.medium_size);
largeBrush = getResources().getInteger(R.integer.large_size);
drawBtn = (ImageButton)findViewById(R.id.draw_btn);
drawBtn.setOnClickListener(this);
eraseBtn = (ImageButton)findViewById(R.id.erase_btn);
eraseBtn.setOnClickListener(this);
newBtn = (ImageButton)findViewById(R.id.new_btn);
newBtn.setOnClickListener(this);
saveBtn = (ImageButton)findViewById(R.id.save_btn);
saveBtn.setOnClickListener(this);
openBtn = (ImageButton)findViewById(R.id.open_btn);
openBtn.setOnClickListener(this);
cameraBtn = (ImageButton)findViewById(R.id.camera_btn);
cameraBtn.setOnClickListener(this);
drawView.setBrushSize(mediumBrush);
}
public void paintClicked(View view){
drawView.setErase(false);
drawView.setBrushSize(drawView.getLastBrushSize());
if(view!=currPaint){
ImageButton imgView = (ImageButton)view;
String color = view.getTag().toString();
drawView.setColor(color);
imgView.setImageDrawable(getResources().getDrawable(R.drawable.paint_pressed));
currPaint.setImageDrawable(getResources().getDrawable(R.drawable.paint));
currPaint=(ImageButton)view;
}
}
@Override
public void onClick(View view) {
if(view.getId()==R.id.draw_btn){
proceedDrawButtonAction();
}else if(view.getId()==R.id.erase_btn){
proceedEraseButtonAction();
}else if(view.getId()==R.id.new_btn){
proceedNewButtonAction();
}else if(view.getId()==R.id.save_btn){
proceedSaveButtonAction();
}else if(view.getId()==R.id.open_btn){
proceedOpenButtonAction();
}else if(view.getId()==R.id.camera_btn){
proceedCameraButtonAction();
}
}
public void proceedDrawButtonAction(){
final Dialog brushDialog = new Dialog(this);
brushDialog.setTitle("Brush size:");
brushDialog.setContentView(R.layout.brush_chooser);
brushDialog.show();
ImageButton smallBtn = (ImageButton)brushDialog.findViewById(R.id.small_brush);
smallBtn.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View v) {
drawView.setErase(false);
drawView.setBrushSize(smallBrush);
drawView.setLastBrushSize(smallBrush);
brushDialog.dismiss();
}
});
ImageButton mediumBtn = (ImageButton)brushDialog.findViewById(R.id.medium_brush);
mediumBtn.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View v) {
drawView.setErase(false);
drawView.setBrushSize(mediumBrush);
drawView.setLastBrushSize(mediumBrush);
brushDialog.dismiss();
}
});
ImageButton largeBtn = (ImageButton)brushDialog.findViewById(R.id.large_brush);
largeBtn.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View v) {
drawView.setErase(false);
drawView.setBrushSize(largeBrush);
drawView.setLastBrushSize(largeBrush);
brushDialog.dismiss();
}
});
}
public void proceedEraseButtonAction(){
final Dialog brushDialog = new Dialog(this);
brushDialog.setTitle("Eraser size:");
brushDialog.setContentView(R.layout.brush_chooser);
brushDialog.show();
ImageButton smallBtn = (ImageButton)brushDialog.findViewById(R.id.small_brush);
smallBtn.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View v) {
drawView.setErase(true);
drawView.setBrushSize(smallBrush);
brushDialog.dismiss();
}
});
ImageButton mediumBtn = (ImageButton)brushDialog.findViewById(R.id.medium_brush);
mediumBtn.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View v) {
drawView.setErase(true);
drawView.setBrushSize(mediumBrush);
brushDialog.dismiss();
}
});
ImageButton largeBtn = (ImageButton)brushDialog.findViewById(R.id.large_brush);
largeBtn.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View v) {
drawView.setErase(true);
drawView.setBrushSize(largeBrush);
brushDialog.dismiss();
}
});
}
public void proceedNewButtonAction(){
AlertDialog.Builder yesNoDialog = new AlertDialog.Builder(this);
yesNoDialog.setTitle("New drawing");
yesNoDialog.setMessage("Start new drawing (you will lose the current drawing)?");
yesNoDialog.setPositiveButton("Yes", new DialogInterface.OnClickListener(){
public void onClick(DialogInterface dialog, int which){
drawView.startNew();
dialog.dismiss();
}
});
yesNoDialog.setNegativeButton("Cancel", new DialogInterface.OnClickListener(){
public void onClick(DialogInterface dialog, int which){
dialog.cancel();
}
});
yesNoDialog.show();
}
public void proceedSaveButtonAction(){
AlertDialog.Builder saveDialog = new AlertDialog.Builder(this);
saveDialog.setTitle("Save drawing");
saveDialog.setMessage("Save drawing to device Gallery?");
saveDialog.setPositiveButton("Yes", new DialogInterface.OnClickListener(){
public void onClick(DialogInterface dialog, int which){
drawView.setDrawingCacheEnabled(true);
String imgSaved = MediaStore.Images.Media.insertImage(
getContentResolver(), drawView.getDrawingCache(),
UUID.randomUUID().toString()+".png", "drawing");
if(imgSaved!=null){
Toast savedToast = Toast.makeText(getApplicationContext(),
"Drawing saved to Gallery!", Toast.LENGTH_SHORT);
savedToast.show();
}
else{
Toast unsavedToast = Toast.makeText(getApplicationContext(),
"Image could not be saved.", Toast.LENGTH_SHORT);
unsavedToast.show();
}
drawView.destroyDrawingCache();
}
});
saveDialog.setNegativeButton("Cancel", new DialogInterface.OnClickListener(){
public void onClick(DialogInterface dialog, int which){
dialog.cancel();
}
});
saveDialog.show();
}
public void proceedOpenButtonAction(){
AlertDialog.Builder yesNoDialog = new AlertDialog.Builder(this);
yesNoDialog.setTitle("Load image");
yesNoDialog.setMessage("Load image from gallery (you will lose the current drawing)?");
yesNoDialog.setPositiveButton("Yes", new DialogInterface.OnClickListener(){
public void onClick(DialogInterface dialog, int which){
Intent pickPhoto = new Intent(
Intent.ACTION_PICK,
android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
startActivityForResult(pickPhoto, GALLERY_INTENT_CALLED);
}
});
yesNoDialog.setNegativeButton("Cancel", new DialogInterface.OnClickListener(){
public void onClick(DialogInterface dialog, int which){
dialog.cancel();
}
});
yesNoDialog.show();
}
public void proceedCameraButtonAction(){
AlertDialog.Builder yesNoDialog = new AlertDialog.Builder(this);
yesNoDialog.setTitle("Take a photo");
yesNoDialog.setMessage("Take a photo and start drawing (you will lose the current drawing)?");
yesNoDialog.setPositiveButton("Yes", new DialogInterface.OnClickListener(){
public void onClick(DialogInterface dialog, int which){
Intent takePhoto = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (takePhoto.resolveActivity(getPackageManager()) != null) {
startActivityForResult(takePhoto, CAMERA_INTENT_CALLED);
}
}
});
yesNoDialog.setNegativeButton("Cancel", new DialogInterface.OnClickListener(){
public void onClick(DialogInterface dialog, int which){
dialog.cancel();
}
});
yesNoDialog.show();
}
protected void onActivityResult(int requestCode, int resultCode, Intent returnedIntent) {
super.onActivityResult(requestCode, resultCode, returnedIntent);
if (requestCode == GALLERY_INTENT_CALLED) {
if (resultCode == RESULT_OK) {
try {
Uri selectedImage = returnedIntent.getData();
InputStream inputStream = getContentResolver().openInputStream(selectedImage);
image = Drawable.createFromStream(inputStream, selectedImage.toString());
} catch (FileNotFoundException e) {}
drawView.drawImage(drawableToBitmap(image));
}
}else if (requestCode == CAMERA_INTENT_CALLED) {
if (resultCode == RESULT_OK) {
Bundle extras = returnedIntent.getExtras();
Bitmap imageBitmap = (Bitmap) extras.get("data");
drawView.drawImage(imageBitmap);
}
}
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
if (savedInstanceState != null) {
if (savedInstanceState.containsKey("drawCanvas")) {
drawView.setDrawCanvas((SerializableCanvas)savedInstanceState.getSerializable("drawCanvas"));
}
}
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putSerializable("drawCanvas", drawView.getDrawCanvas());
}
}
绘图视图类:
public class DrawingView extends View implements Serializable{
private Path drawPath;
private Paint drawPaint;
private Paint canvasPaint;
private int paintColor = 0xFFFF0000;
private SerializableCanvas drawCanvas;
private Bitmap canvasBitmap;
private float brushSize;
private float lastBrushSize;
private boolean erase = false;
public DrawingView(Context context, AttributeSet attrs){
super(context, attrs);
setupDrawing();
}
private void setupDrawing(){
brushSize = getResources().getInteger(R.integer.medium_size);
lastBrushSize = brushSize;
drawPath = new Path();
drawPaint = new Paint();
drawPaint.setColor(paintColor);
drawPaint.setAntiAlias(true);
drawPaint.setStrokeWidth(brushSize);
drawPaint.setStyle(Paint.Style.STROKE);
drawPaint.setStrokeJoin(Paint.Join.ROUND);
drawPaint.setStrokeCap(Paint.Cap.ROUND);
canvasPaint = new Paint(Paint.DITHER_FLAG);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
float touchX = event.getX();
float touchY = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
drawPath.moveTo(touchX, touchY);
break;
case MotionEvent.ACTION_MOVE:
drawPath.lineTo(touchX, touchY);
if(erase){
drawCanvas.drawPath(drawPath, drawPaint);
}
break;
case MotionEvent.ACTION_UP:
drawCanvas.drawPath(drawPath, drawPaint);
drawPath.reset();
break;
default:
return false;
}
invalidate();
return true;
}
@Override
protected void onDraw(Canvas canvas) {
canvas.drawBitmap(canvasBitmap, 0, 0, canvasPaint);
if(erase){ return;}
canvas.drawPath(drawPath, drawPaint);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
canvasBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
drawCanvas = new SerializableCanvas(canvasBitmap);
}
public void setColor(String newColor){
invalidate();
paintColor = Color.parseColor(newColor);
drawPaint.setColor(paintColor);
}
public void setBrushSize(float newSize){
float pixelAmount = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
newSize, getResources().getDisplayMetrics());
brushSize = pixelAmount;
drawPaint.setStrokeWidth(brushSize);
}
public void setLastBrushSize(float lastSize){
lastBrushSize=lastSize;
}
public float getLastBrushSize(){
return lastBrushSize;
}
public void setErase(boolean erase){
this.erase = erase;
if(erase) {
drawPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
} else {
drawPaint.setXfermode(null);
}
}
public void startNew(){
drawCanvas.drawColor(0, PorterDuff.Mode.CLEAR);
invalidate();
}
public void drawImage(Bitmap image) {
drawCanvas.drawBitmap(resize(image, drawCanvas.getWidth(), drawCanvas.getHeight()), 0, 0, canvasPaint);
invalidate();
}
public static Bitmap drawableToBitmap (Drawable drawable) {
if (drawable instanceof BitmapDrawable) {
return ((BitmapDrawable)drawable).getBitmap();
}
Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
drawable.draw(canvas);
return bitmap;
}
private static Bitmap resize(Bitmap image, int maxWidth, int maxHeight) {
image = rotateImage(image, 90);
image = Bitmap.createScaledBitmap(image, maxWidth, maxHeight, true);
return image;
}
public static Bitmap rotateImage(Bitmap source, float angle) {
Matrix matrix = new Matrix();
matrix.postRotate(angle);
return Bitmap.createBitmap(source, 0, 0, source.getWidth(), source.getHeight(),
matrix, true);
}
public SerializableCanvas getDrawCanvas(){
return drawCanvas;
}
public void setDrawCanvas(SerializableCanvas drawCanvas){
this.drawCanvas = drawCanvas;
}
public Bitmap getCanvasBitmap(){
return canvasBitmap;
}
public void setCanvasBitmap(Bitmap canvasBitmap){
this.canvasBitmap = canvasBitmap;
}
public Paint getCanvasPaint(){
return canvasPaint;
}
public void setCanvasPaint(Paint canvasPaint){
this.canvasPaint = canvasPaint;
}
}
解决方案
你是绝对正确的; NPE 的根本原因是您的活动已关闭。最有可能的是,Camera 应用程序会强制横向放置,并且您的不支持配置更改的 Activity 会重新启动(可能不止一次)以解决这种“旋转”问题。另一个常见的情况是系统决定关闭您的应用程序,因为相机应用程序需要更多 RAM。
无论如何,您无法在onSaveInstanceState()包中保存/恢复 Canvas 或 View 。活动恢复后,您必须连接到新的DrawingView及其drawingCanvas。
问题是onActivityResult()可能在 Activity 恢复之前被调用,这意味着当你收到它时可能无法绘制位图。在DrawingView.drawBitmap()中,如果drawingCanvas == null
,将位图保存到局部变量,并在画布可用时立即绘制此位图。
PS请注意,对于 CAMERA_INTENT_CALLED,您的应用程序将绘制相机拍摄的照片的低分辨率缩略图位图。如果您想要完整质量的图像,您必须从相机应用程序创建的文件中检索它。
下面是一段DrawingView实现延迟绘制的代码:
private Bitmap savedBitmap;
public void drawImage(Bitmap image) {
if (drawCanvas == null) {
savedBitmap = image;
}
else {
drawCanvas.drawBitmap(resize(image, drawCanvas.getWidth(), drawCanvas.getHeight()), 0, 0, canvasPaint);
invalidate();
}
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
canvasBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
drawCanvas = new SerializableCanvas(canvasBitmap);
if (savedBitmap != null) {
drawBitmap(savedBitmap);
savedBitmap = null;
}
}
请注意,您不需要对 Canvas 或 DrawingView 进行序列化。
推荐阅读
- angular - 服务方法如何在 ngOnInit() 内部工作,即使 ngOnInit() 钩子执行一次?
- python - 如何使用 Python 提取 .gz 压缩文件?
- android - 如何将相同的 SHA1 用于具有不同包名称的应用程序,作为另一个应用程序?
- influxdb - 如何在电报中舍入输出值?
- java-8 - 如何从jdk 8中的另一个列表添加到列表
- flutter - 未处理的异常:在构造函数中调用 setState():....(生命周期状态:已创建,无小部件,未安装)在 Flutter 中
- c++ - Visual Studio C++ 本机单元测试链接器错误 LNK2001 链接器找不到测试类成员变量
- android - addTextChangedListener 立即触发
- dialogflow-cx - 在哪里可以找到 DialogFlow CX 代理 ID?
- reactjs - 在 React 中获取 Azure Devops 构建 ID