Android:激活相机后的nullponterException

时间:2018-05-16 21:13:58

标签: android drawing photo android-camera-intent

我正在为android开发简单的绘图应用程序。我的目标是:

  1. 从我的应用程序打开相机
  2. 拍张照片
  3. 将此照片绘制到画布
  4. 绘制这张照片
  5. 激活相机后:

    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对象,但它太大了,里面有一个Bitmap。 我有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());
        }
    
    }
    

    DrawingView类:

    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;
        }
    
    }
    

1 个答案:

答案 0 :(得分:0)

你是对的;您的NPE的根本原因是您的活动已关闭。最有可能的是,Camera应用程序强制横向显示,并且重新启动(可能不止一次)您的不支持配置更改的活动以解释此“轮换”问题。另一种常见情况是系统决定关闭您的应用程序,因为相机应用程序需要更多内存。

无论如何,您无法在 onSaveInstanceState()的捆绑中保存/恢复Canvas或View。活动恢复后,您必须连接到新的 DrawingView 及其 drawingCanvas

可以在Activity恢复之前调用 onActivityResult(),这意味着在收到位图时可能无法绘制位图。在 DrawingView.drawBitmap()中,如果是drawingCanvas == null,请将位图保存到局部变量,并在画布可用后立即绘制此位图。

PS 请注意,对于CAMERA_INTENT_CALLED,您的应用会绘制相机拍摄的照片的低分辨率缩略图位图。如果您想要完整质量的图像,则必须从Camera应用程序创建的文件中检索它。

以下是 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进行序列化。