我正在创建像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"按钮最有可能处理这个逻辑?
答案 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"/>
从相机捕获位图后,您可以从此类获取cachedBitmap,并将这两个位图与mergeBitmaps方法合并。