自定义相机预览图片

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 布局文件中,并在ActivityonResume() 回调处对其进行膨胀,并将其附加到您的布局根,例如调用***Layout 对象的addChild 方法并将您的膨胀CameraSurfaceView 传入,并在ActivityonPause() 回调处将其分离,例如从您的***Layout 对象中删除CameraSurfaceView。 总结: 1. 膨胀(每次都重新膨胀)并将您的CameraSurfaceView 添加到onResume() 中的视图层次结构中; 2. 从视图层次结构中删除您的 CameraSurfaceView 并将其无效。

【讨论】:

以上是关于自定义相机预览图片的主要内容,如果未能解决你的问题,请参考以下文章

AVFoundation的自定义相机

Android 三星 Galaxy S4 自定义相机预览失真

自定义相机的画质不佳

自定义相机视图预览层加上大小的手机问题

如何为自定义相机添加 取景框|扫描框 |预览框|矩形框

如何为自定义相机添加 取景框|扫描框 |预览框|矩形框