自定义相机预览图片
Posted
技术标签:
【中文标题】自定义相机预览图片【英文标题】:Custom Camera preview picture 【发布时间】:2015-11-26 20:47:38 【问题描述】:我正在制作一个自定义相机,它可以简单地拍照并预览图像。我无法预览图像。即使我在拍照后调用 stopPreview 方法,也会显示照片,但是当我按下 home 并再次恢复应用程序时,相机会重置。
public class CameraActivity extends ActionBarActivity
Camera camera;
private static final String TAG = CameraActivity.class.getName();
private Button captureImageButton;
private Button retakeImageButton;
private Button nextImageButton;
private Button doneButton;
static final String IS_NEW_IMAGE = "isNewImage";
private CameraSurfaceView cameraPreview;
@Override
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_camera);
captureImageButton = (Button) findViewById(R.id.button_capture);
retakeImageButton = (Button) findViewById(R.id.retakeImage);
nextImageButton = (Button) findViewById(R.id.nextImage);
doneButton = (Button) findViewById(R.id.done);
@Override
protected void onResume()
super.onResume();
camera = getCameraInstance();
if (camera != null)
cameraPreview = new CameraSurfaceView(this, camera);
FrameLayout preview = (FrameLayout) findViewById(R.id.camera_preview);
preview.addView(cameraPreview);
captureImageButton.setOnClickListener(new View.OnClickListener()
@Override
public void onClick(View v)
if (camera != null)
camera.takePicture(null, null, mPicture);
);
@Override
public boolean onCreateOptionsMenu(Menu menu)
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_camera, menu);
return true;
@Override
public boolean onOptionsItemSelected(MenuItem item)
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in androidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings)
return true;
return super.onOptionsItemSelected(item);
private Camera getCameraInstance()
Camera c = null;
try
int backCameraId = findBackFacingCamera();
if (backCameraId >= 0)
c = Camera.open(backCameraId); // attempt to get a Camera instance
catch (Exception e)
final Snackbar snackBar = Snackbar.make(findViewById(android.R.id.content), "Please shut down all the background applications and try again", Snackbar.LENGTH_LONG);
ViewGroup snackBarView = (ViewGroup) snackBar.getView();
snackBarView.setBackgroundColor(getResources().getColor(R.color.PrimaryOrange));
snackBar.setAction("Go Back", new View.OnClickListener()
@Override
public void onClick(View v)
if (getApplicationContext() != null)
snackBar.dismiss();
finish();
);
snackBar.show();
return c; // returns null if camera is unavailable
private Camera.PictureCallback mPicture = new Camera.PictureCallback()
@Override
public void onPictureTaken(byte[] data, Camera camera)
enablePreviewMode();
camera.stopPreview();
File pictureFile = getOutputMediaFile(MEDIA_TYPE_IMAGE);
if (pictureFile == null)
Log.d(TAG, "Error creating media file, check storage permissions: ");
return;
try
FileOutputStream fos = new FileOutputStream(pictureFile);
fos.write(data);
fos.close();
catch (FileNotFoundException e)
Log.d(TAG, "File not found: " + e.getMessage());
catch (IOException e)
Log.d(TAG, "Error accessing file: " + e.getMessage());
;
public static final int MEDIA_TYPE_IMAGE = 1;
/**
* Create a file Uri for saving an image or video
*/
private static Uri getOutputMediaFileUri(int type)
return Uri.fromFile(getOutputMediaFile(type));
/**
* Create a File for saving an image or video
*/
private static File getOutputMediaFile(int type)
// To be safe, you should check that the SDCard is mounted
// using Environment.getExternalStorageState() before doing this.
File mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES), "MyCameraApp");
// This location works best if you want the created images to be shared
// between applications and persist after your app has been uninstalled.
// Create the storage directory if it does not exist
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", Locale.US).format(new Date());
File mediaFile;
mediaFile = new File(mediaStorageDir.getPath() + File.separator +
"IMG_" + timeStamp + ".png");
return mediaFile;
private void enablePreviewMode()
if (captureImageButton != null && retakeImageButton != null && doneButton != null & nextImageButton != null)
captureImageButton.setVisibility(View.GONE);
retakeImageButton.setVisibility(View.VISIBLE);
retakeImageButton.setOnClickListener(new View.OnClickListener()
@Override
public void onClick(View v)
Intent retakeIntent = getIntent();
if (retakeIntent != null)
finish();
retakeIntent.putExtra(IS_NEW_IMAGE, true);
startActivity(retakeIntent);
);
doneButton.setVisibility(View.VISIBLE);
doneButton.setOnClickListener(new View.OnClickListener()
@Override
public void onClick(View v)
finish();
);
nextImageButton.setVisibility(View.VISIBLE);
nextImageButton.setOnClickListener(new View.OnClickListener()
@Override
public void onClick(View v)
Intent retakeIntent = getIntent();
if (retakeIntent != null)
finish();
retakeIntent.putExtra(IS_NEW_IMAGE, false);
startActivity(retakeIntent);
);
private void releaseCamera()
if (camera != null)
camera.release();
if (cameraPreview != null)
cameraPreview.getHolder().removeCallback(cameraPreview);
private int findBackFacingCamera()
int cameraId = -1;
//Search for the back facing camera
//get the number of cameras
int numberOfCameras = Camera.getNumberOfCameras();
//for every camera check
for (int i = 0; i < numberOfCameras; i++)
Camera.CameraInfo info = new Camera.CameraInfo();
Camera.getCameraInfo(i, info);
if (info.facing == Camera.CameraInfo.CAMERA_FACING_BACK)
cameraId = i;
break;
return cameraId;
@Override
protected void onPause()
super.onPause();
releaseCamera();
@Override
protected void onStop()
super.onStop();
releaseCamera();
表面视图
public class CameraSurfaceView extends SurfaceView implements SurfaceHolder.Callback
private SurfaceHolder mHolder;
private Camera mCamera;
private static final String TAG = CameraSurfaceView.class.getName();
public CameraSurfaceView(Context context, Camera camera)
super(context);
mCamera = camera;
// Install a SurfaceHolder.Callback so we get notified when the
// underlying surface is created and destroyed.
mHolder = getHolder();
mHolder.addCallback(this);
// deprecated setting, but required on Android versions prior to 3.0
mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
public void surfaceCreated(SurfaceHolder holder)
// The Surface has been created, now tell the camera where to draw the preview.
try
mCamera.setDisplayOrientation(90);
mCamera.setPreviewDisplay(holder);
Camera.Parameters params = mCamera.getParameters();
params.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
mCamera.setParameters(params);
mCamera.startPreview();
catch (IOException e)
Log.d(TAG, "Error setting camera preview: " + e.getMessage());
public void surfaceDestroyed(SurfaceHolder holder)
// empty. Take care of releasing the Camera preview in your activity.
mCamera.release();
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h)
// If your preview can change or rotate, take care of those events here.
// Make sure to stop the preview before resizing or reformatting it.
if (mHolder.getSurface() == null)
// preview surface does not exist
return;
// stop preview before making changes
try
mCamera.stopPreview();
catch (Exception e)
// ignore: tried to stop a non-existent preview
// set preview size and make any resize, rotate or
// reformatting changes here
// start preview with new settings
try
mCamera.setPreviewDisplay(mHolder);
mCamera.startPreview();
catch (Exception e)
Log.d(TAG, "Error starting camera preview: " + e.getMessage());
即使是普通的相机也不能这样工作。另一种方法是使用 2 个片段并传递图像,但如果 android 对此有一些规定,我会喜欢它。
【问题讨论】:
【参考方案1】:您是否将您的CameraSurfaceView
放入您的activity_camera.xml
中?
如果答案是真的,我想我知道如何解决您的问题。
您只需要将您的CameraSurfaceView
xml 定义提取到一个独立的 xml 布局文件中,并在Activity
的onResume()
回调处对其进行膨胀,并将其附加到您的布局根,例如调用***Layout
对象的addChild
方法并将您的膨胀CameraSurfaceView
传入,并在Activity
的onPause()
回调处将其分离,例如从您的***Layout
对象中删除CameraSurfaceView
。
总结:
1. 膨胀(每次都重新膨胀)并将您的CameraSurfaceView
添加到onResume()
中的视图层次结构中;
2. 从视图层次结构中删除您的 CameraSurfaceView
并将其无效。
【讨论】:
以上是关于自定义相机预览图片的主要内容,如果未能解决你的问题,请参考以下文章