无法在surfaceview上绘制,因为Canvas为null

时间:2015-02-22 15:54:31

标签: android android-camera android-canvas surfaceview

我正在创建像Snapchat这样的应用,您可以在其中拍摄照片,进行预览,以及预览时使用它。为了实现这一点,我使用的是表面视图。

以下是拍摄照片和绘图的代码

CameraActivity.java:

public class CameraActivity extends Activity {
    public static final String TAG = CameraActivity.class.getSimpleName();
    private android.hardware.Camera mCamera;
    private CameraPreview mCameraPreview;

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_camera);

        mCamera = getCameraInstance();
        mCameraPreview = new CameraPreview(this, mCamera);
        FrameLayout preview = (FrameLayout) findViewById(R.id.camera_preview);
        preview.addView(mCameraPreview);

        Button captureButton = (Button) findViewById(R.id.button_capture);
        captureButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mCamera.takePicture(null, null, mPicture);
            }
        });
    }

    /**
     * Helper method to access the camera returns null if it cannot get the
     * camera or does not exist
     *
     * @return
     */
    private Camera getCameraInstance() {
        Camera camera = null;
        try {
            camera = Camera.open();
        } catch (Exception e) {
            // cannot get camera or does not exist
        }
        return camera;
    }


    PictureCallback mPicture = new PictureCallback() {
        @Override
        public void onPictureTaken(byte[] data, Camera camera) {
            File pictureFile = getOutputMediaFile();
            if (pictureFile == null) {
                return;
            }
            try {
                FileOutputStream fos = new FileOutputStream(pictureFile);
                fos.write(data);
                fos.close();
            } catch (FileNotFoundException e) {

            } catch (IOException e) {
            }
        }
    };

    private static File getOutputMediaFile() {
        File mediaStorageDir = new File(
                Environment
                        .getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES),
                "MyCameraApp");
        if (!mediaStorageDir.exists()) {
            if (!mediaStorageDir.mkdirs()) {
                Log.d("MyCameraApp", "failed to create directory");
                return null;
            }
        }
        // Create a media file name
        String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss")
                .format(new Date());
        File mediaFile;
        mediaFile = new File(mediaStorageDir.getPath() + File.separator
                + "IMG_" + timeStamp + ".jpg");

        return mediaFile;
    }
}

CameraPreview.java:

public class CameraPreview extends SurfaceView implements
        SurfaceHolder.Callback {
    public static final String TAG = CameraPreview.class.getSimpleName();
    private SurfaceHolder mSurfaceHolder;
    private android.hardware.Camera mCamera;

    // Constructor that obtains context and camera
    @SuppressWarnings("deprecation")
    public CameraPreview(Context context, Camera camera) {
        super(context);
        this.mCamera = camera;
        this.mSurfaceHolder = this.getHolder();
        this.mSurfaceHolder.addCallback(this);
        this.mSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
    }

    @Override
    public void surfaceCreated(SurfaceHolder surfaceHolder) {
        try {
            mCamera.setPreviewDisplay(surfaceHolder);
            mCamera.startPreview();
            tryDrawing(surfaceHolder);

        } catch (IOException e) {
            // left blank for now
        }
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
        mCamera.stopPreview();
        mCamera.release();
    }

    @Override
    public void surfaceChanged(SurfaceHolder surfaceHolder, int format,
                               int width, int height) {
        // start preview with new settings
        try {
            mCamera.setPreviewDisplay(surfaceHolder);
            mCamera.startPreview();
            tryDrawing(surfaceHolder);
        } catch (Exception e) {
            // intentionally left blank for a test
        }
    }

    private void tryDrawing(SurfaceHolder surfaceHolder) {
        Log.i(TAG, "Trying to draw...");

        Canvas canvas = surfaceHolder.lockCanvas();
        if (canvas == null) {
            Log.e(TAG, "Cannot draw onto the canvas as it's null");
        } else {
            drawMyStuff(canvas);
            surfaceHolder.unlockCanvasAndPost(canvas);
        }
    }

    private void drawMyStuff(final Canvas canvas) {
        Random random = new Random();
        Log.i(TAG, "Drawing...");
        canvas.drawRGB(255, 128, 128);
    }
}

这非常适合拍照和预览,但是,当我尝试在画布上实际绘制时(也就是说,运行CameraPreview.trydrawing()方法),我收到此错误:

02-22 15:36:59.861    4261-4261/com.johncorser.selfiesnap I/CameraPreview﹕ Trying to draw...
02-22 15:36:59.861    4261-4261/com.johncorser.selfiesnap E/SurfaceHolder﹕ Exception locking surface
    java.lang.IllegalArgumentException
            at android.view.Surface.nativeLockCanvas(Native Method)
            at android.view.Surface.lockCanvas(Surface.java:243)
            at android.view.SurfaceView$4.internalLockCanvas(SurfaceView.java:814)
            at android.view.SurfaceView$4.lockCanvas(SurfaceView.java:782)
            at com.johncorser.selfiesnap.CameraPreview.tryDrawing(CameraPreview.java:63)
            at com.johncorser.selfiesnap.CameraPreview.surfaceChanged(CameraPreview.java:54)
            at android.view.SurfaceView.updateWindow(SurfaceView.java:583)
            at android.view.SurfaceView.access$000(SurfaceView.java:86)
            at android.view.SurfaceView$3.onPreDraw(SurfaceView.java:175)
            at android.view.ViewTreeObserver.dispatchOnPreDraw(ViewTreeObserver.java:847)
            at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1867)
            at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:996)
            at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:5600)
            at android.view.Choreographer$CallbackRecord.run(Choreographer.java:761)
            at android.view.Choreographer.doCallbacks(Choreographer.java:574)
            at android.view.Choreographer.doFrame(Choreographer.java:544)
            at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:747)
            at android.os.Handler.handleCallback(Handler.java:733)
            at android.os.Handler.dispatchMessage(Handler.java:95)
            at android.os.Looper.loop(Looper.java:136)
            at android.app.ActivityThread.main(ActivityThread.java:5001)
            at java.lang.reflect.Method.invokeNative(Native Method)
            at java.lang.reflect.Method.invoke(Method.java:515)
            at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:785)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:601)
            at dalvik.system.NativeStart.main(Native Method)
02-22 15:36:59.961    4261-4261/com.johncorser.selfiesnap E/CameraPreview﹕ Cannot draw onto the canvas as it's null

我已经在堆栈溢出中看了这个,基本上发现问题与相机锁定表面视图的事实有关,所以我无法锁定画布(因此它是空值)。所以我想知道的是,有什么方法可以克服这个问题,并能够在图像上绘制?

理想情况下,在图像上绘制的任何内容也会保存在文件中,但我也可以添加一个" save"按钮最有可能处理这个逻辑?

1 个答案:

答案 0 :(得分:1)

将另一个CustomView置于顶部并在其上绘制,然后您可以通过此方法合并这两个图像

public Bitmap mergeBitmaps(Bitmapclass bitmap1,RenderView Bitmapextends bitmap2)View {
Bitmap mergedBitmap =private Bitmap.createBitmap(bitmap1.getWidth(), bitmap1.getHeight(), bitmap1.getConfig());
Canvas canvas = newprivate Canvas(mergedBitmap);
canvas.drawBitmap(bitmap1, 0, 0, null);
canvas.drawBitmap(bitmap2, 0, 0, null);
return mergedBitmap;

}

这是自定义视图的实现

public class RenderView extends View {
private Bitmap cachedBitmap;
private Canvas cachedCanvas;
private Paint linePaint;

private boolean isClicked;
private float lastX;
private float lastY;

public RenderView(Context context) {
    super(context);
}

public RenderView(Context context, AttributeSet attrs) {
    super(context, attrs);
}

public RenderView(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
}

@Override
protected void onDraw(Canvas canvas) {
    int width = getWidth();
    int height = getHeight();
    if(cachedBitmap == null && width > 0 && height > 0) {
        cachedBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
        cachedCanvas = new Canvas(cachedBitmap);
        linePaint = new Paint();
        linePaint.setStyle(Paint.Style.STROKE);
        linePaint.setStrokeWidth(2);
        linePaint.setColor(Color.parseColor("#000000"));
    }
    canvas.drawBitmap(cachedBitmap, 0, 0, null);
}

@Override
public boolean onTouchEvent(MotionEvent event) {
    switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            isClicked = true;
            break;

        case MotionEvent.ACTION_MOVE:
            if(isClicked && cachedCanvas != null) {
                cachedCanvas.drawLine(lastX, lastY, event.getX(), event.getY(), linePaint);
                invalidate();
            }
            break;

        case MotionEvent.ACTION_UP:
            isClicked = false;
            break;
    }
    lastX = event.getX();
    lastY = event.getY();
    return true;
}

}

以这种方式通过XML添加此视图

    <com.myapp.RenderView
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@+id/renderView"
    android:clickable="true"/>

RenderView

从相机捕获位图后,您可以从此类获取cachedBitmap,并将这两个位图与mergeBitmaps方法合并。