Android Camera学习总结:如何在android中使用摄像头获取照片

这学期补修了Android这门课,短短的八次课让我对Android有了初步的了解。作为结课项目,老师让我们用Camera/Surfaceview完成相机功能。现将学习的心得记录下来。

整个相机程序实现的思路是,使用Camera实例,设置好参数后,得到摄像头传回的图像数据,将这些数据在Surfaceview实例中进行展示,实现预览功能。在Surfaceview下,设置click button,当单击click button后,调用Camera.takePicture方法,完成照相动作,将图片保存到手机本地。

整个工程的核心是实现了SurfaceHolder.Callback接口,重写接口中的几个方法,而这几个方法,都是Surfaceview实例将要调用的关键方法。他们分别是:surfaceCreated/surfaceDestroyed/surfaceChanged。然后可以通过Surfaceview.getHolder().addCallback(Callback)方法,将这个Callback与前端layout中的surfaceview相关联起来。

下面来看下Callback中方法具体代码:

 1 @Override
 2         public void surfaceChanged(SurfaceHolder holder, int format, int width,
 3                 int height) {
 4             Log.e("tag", " surfaceChanged");
 5             mParameters = camera.getParameters();
 6             mParameters.setPictureFormat(PixelFormat.JPEG);
 7             Log.e("tag",
 8                     "parameters.getPictureSize()"
 9                             + mParameters.getPictureSize().width);
10             setPictureSize(mParameters);
11             Log.i("tag", "holder width:" + width + "  height:" + height);
12             // parameters.setPreviewSize(width, height);//需要判断支持的预览
13 
14             camera.setParameters(mParameters);
15 
16             camera.startPreview();
17         }
18 
19 private void setPictureSize(Parameters parameters) {
20         List<Size> sizes = parameters.getSupportedPictureSizes();
21         if (sizes == null) {
22             return;
23         }
24         int maxSize = 0;
25         int width = 0;
26         int height = 0;
27         for (int i = 0; i < sizes.size(); i++) {
28             Size size = sizes.get(i);
29             int pix = size.width * size.height;
30             if (pix > maxSize) {
31                 maxSize = pix;
32                 width = size.width;
33                 height = size.height;
34             }
35         }
36         Log.i("tag", "图片的大小:" + width + " height:" + height);
37         parameters.setPictureSize(width, height);
38     }

上段代码重写了surfaceChanged方法。该方法中完成了设置相机的参数,包括图片格式,预览尺寸,并开始预览功能,即在surfaceview中实时显示摄像头中捕捉的画面。其中setPicturesize方法使用了相对的图片大小设置,如果将图片大小设置成绝对值,可能会造成不同尺寸屏幕的机器不能正常运行程序的异常发生。

在另外两个方法中分别完成了camera实例的获取和销毁工作,具体代码如下:

@Override
        public void surfaceDestroyed(SurfaceHolder holder) {
            Log.i("tag", " surfaceDestroyed");
            if (camera != null) {
                camera.stopPreview();
                camera.release();
                camera = null;
            }
        }

@Override
        public void surfaceCreated(SurfaceHolder holder) {
            try {
                if (camera == null) {
                    camera = Camera.open();
                }
                camera.setPreviewDisplay(holder);
                Log.i("", "camera created");
                //WindowManager wm = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
                //Display display = wm.getDefaultDisplay();
//                Camera.Parameters parameters = camera.getParameters();
//                //parameters.setPreviewSize(display.getWidth(), display.getHeight());//设置预览照片的大小
//                parameters.setPreviewFrameRate(3);//每秒3帧
//                parameters.setPictureFormat(PixelFormat.JPEG);//设置照片的输出格式
//                parameters.set("jpeg-quality", 100);//照片质量
//                //parameters.setPictureSize(display.getWidth(), display.getHeight());//设置照片的大小
//                camera.setParameters(parameters);
//                camera.setPreviewDisplay(surfaceView.getHolder());//通过SurfaceView显示取景画面
//                camera.startPreview();//开始预览
//                preview = true;
            } catch (IOException e) {
                if (camera != null) {
                    camera.stopPreview();
                    camera.release();
                    camera = null;
                }
                e.printStackTrace();
            }
            
        }

下面将整个项目的代码贡献出来,包括实现自动对焦,手动调焦距,设置闪光灯等功能。

共包含一个mainActivity和CameraCallback两个类

mainActivity的代码如下:

import android.app.Activity;
import android.hardware.Camera;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnTouchListener;
import android.widget.Button;
import android.widget.SeekBar;
import android.widget.Toast;
import android.widget.SeekBar.OnSeekBarChangeListener;

public class MainActivity extends Activity implements OnClickListener {
    private Camera mCamera;
    private SurfaceView mSurfaceView;
    private SurfaceHolder mHolder;
    private CameraCallback mCallback;
    private Button mTakePicButton;
    private Button mSwitchButton;
    private Button mflashButton;

    private boolean saved = true;
    private boolean isFrontCamera;

    public static final int MESSAGE_SVAE_SUCCESS = 0;
    public static final int MESSAGE_SVAE_FAILURE = 1;

    private final int FLASH_MODE_AUTO = 0;
    private final int FLASH_MODE_ON = 1;
    private final int FLASH_MODE_OFF = 2;
    private int mFlashMode = 0;

    private Handler mHandler = new Handler() {
        public void handleMessage(Message msg) {
            saved = true;
            switch (msg.what) {
            case MESSAGE_SVAE_SUCCESS:
                Toast.makeText(MainActivity.this, "保存成功", Toast.LENGTH_SHORT)
                        .show();
                break;
            case MESSAGE_SVAE_FAILURE:
                Toast.makeText(MainActivity.this, "保存失败", Toast.LENGTH_SHORT)
                        .show();
                break;
            }
        };
    };
    private SeekBar mZoomBar;
    private View mZoomLayout;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        initView();

    }

    private void initView() {

        mTakePicButton = (Button) findViewById(R.id.camera_take_btn);
        mTakePicButton.setOnClickListener(this);
        mSwitchButton = (Button) findViewById(R.id.camera_switch_btn);
        mSwitchButton.setOnClickListener(this);
        mflashButton = (Button) findViewById(R.id.flashMode);
        mflashButton.setOnClickListener(this);

        mZoomLayout = findViewById(R.id.zoomLayout);
        mZoomBar = (SeekBar) findViewById(R.id.seekBar1);
        mZoomBar.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {

            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {
            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {
            }

            @Override
            public void onProgressChanged(SeekBar seekBar, int progress,
                    boolean fromUser) {
                mCallback.setZoom(progress);
            }
        });

        initSurfaceView();
    }

    private void initSurfaceView() {
        mSurfaceView = (SurfaceView) findViewById(R.id.surfaceView1);
        mHolder = mSurfaceView.getHolder();
        mCallback = new CameraCallback(this);
        mHolder.addCallback(mCallback);
        mSurfaceView.setOnTouchListener(new OnTouchListener() {

            @Override
            public boolean onTouch(View v, MotionEvent event) {
                if (event.getAction() == MotionEvent.ACTION_UP
                        && !isFrontCamera) {// 前置摄像头取消触摸自动聚焦功能
                    View view = findViewById(R.id.RelativeLayout1);
                    mCallback.autoFocus(view, event);
                }

                return true;
            }
        });

        // 判断是否支持前置摄像头
        int cameras = mCallback.getNumberOfCameras();
        if (cameras <= 1) {
            mSwitchButton.setVisibility(View.GONE);
        }

        // 是否支持闪关灯
        if (!mCallback.isSupportedFlashMode()) {
            mflashButton.setVisibility(View.GONE);
        }

        if (mCallback.isSupportedZoom()) {
            mZoomBar.setMax(mCallback.getMaxZoom());
        } else {
            mZoomBar.setVisibility(View.GONE);
        }
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
        case R.id.camera_take_btn:
            if (saved) {
                saved = false;
                mCallback.takePicture(mHandler);
            }
            break;
        case R.id.camera_switch_btn:
            isFrontCamera = !isFrontCamera;
            if (isFrontCamera) {
                mSwitchButton.setText("打开后摄像头");
                mflashButton.setVisibility(View.GONE);
                mZoomLayout.setVisibility(View.GONE);
            } else {
                mSwitchButton.setText("打开前摄像头");
                mflashButton.setVisibility(View.VISIBLE);
                mZoomLayout.setVisibility(View.VISIBLE);
            }
            mCallback.switchCamera(mSurfaceView, isFrontCamera);
            break;
        case R.id.flashMode:
            mFlashMode = (mFlashMode + 1) % 3;
            switch (mFlashMode) {
            case FLASH_MODE_AUTO:
                mflashButton.setText("flash_auto");
                break;
            case FLASH_MODE_ON:
                mflashButton.setText("flash_on");
                break;
            case FLASH_MODE_OFF:
                mflashButton.setText("flash_off");
                break;

            default:
                break;
            }
            mCallback.SetFlashMode(mFlashMode);
            break;

        default:
            break;
        }

    }

}

CameraCallback类代码如下:

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.List;

import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.PixelFormat;
import android.graphics.Bitmap.CompressFormat;
import android.hardware.Camera;
import android.hardware.Camera.AutoFocusCallback; //import android.hardware.Camera.CameraInfo;
import android.hardware.Camera.Parameters;
import android.hardware.Camera.PictureCallback;
import android.hardware.Camera.Size;
import android.os.Environment;
import android.os.Handler;
import android.util.Log;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.ScaleAnimation;
import android.view.animation.Animation.AnimationListener;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.RelativeLayout.LayoutParams;

public class CameraCallback implements SurfaceHolder.Callback {
    private Context mContext;
    private Camera mCamera;
    private boolean isShowFrame;
    private SurfaceHolder mHolder;
    // 在2.3的Camera.CameraInfo类中
    // CAMERA_FACING_BACK常量的值为0,CAMERA_FACING_FRONT为1
    private static final int CAMERA_FACING_BACK = 0;
    private static final int CAMERA_FACING_FRONT = 1;

    private final int FLASH_MODE_AUTO = 0;
    private final int FLASH_MODE_ON = 1;
    private final int FLASH_MODE_OFF = 2;

    private Parameters mParameters;

    public CameraCallback(Context context) {
        this.mContext = context;
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width,
            int height) {
        Log.e("tag", " surfaceChanged");
        mParameters = mCamera.getParameters();
        if (isSupportedFlashMode()) {// 需要判断是否支持闪光灯
            mParameters.setFlashMode(Parameters.FLASH_MODE_AUTO);
        }
        mParameters.setPictureFormat(PixelFormat.JPEG);
        Log.e("tag",
                "parameters.getPictureSize()"
                        + mParameters.getPictureSize().width);
        setPictureSize(mParameters);
        // parameters.setPreviewFormat(PixelFormat.JPEG);//
        Log.i("tag", "holder width:" + width + "  height:" + height);
        // parameters.setPreviewSize(width, height);//需要判断支持的预览

        mCamera.setParameters(mParameters);

        mCamera.startPreview();
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        mHolder = holder;
        Log.e("tag", " surfaceCreated");
        try {
            if (mCamera == null) {
                mCamera = Camera.open();
            }
            setDisplayOrientation(mCamera);
            mCamera.setPreviewDisplay(holder);
            Log.i("", "mCamera 2");

        } catch (IOException e) {
            if (mCamera != null) {
                mCamera.stopPreview();
                mCamera.release();
                mCamera = null;
            }
            e.printStackTrace();
        }

    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        Log.e("tag", " surfaceDestroyed");
        if (mCamera != null) {
            mCamera.stopPreview();
            mCamera.release();
            mCamera = null;
        }

    }

    public int getNumberOfCameras() {
        try {
            Method method = Camera.class.getMethod("getNumberOfCameras", null);
            if (method != null) {
                Object object = method.invoke(mCamera, null);
                if (object != null) {
                    return (Integer) object;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return 0;
    }

    private void setDisplayOrientation(Camera camera) {
        try {
            Method method = Camera.class.getMethod("setDisplayOrientation",
                    int.class);
            if (method != null) {
                method.invoke(camera, 90);
            }
            Log.i("tag", "方法名:" + method.getName());
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    public Camera open(int i) {
        try {
            Method method = Camera.class.getMethod("open", int.class);
            if (method != null) {
                Object object = method.invoke(mCamera, i);
                if (object != null) {
                    return (Camera) object;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    // 设置图片大小
    private void setPictureSize(Parameters parameters) {
        List<Size> sizes = parameters.getSupportedPictureSizes();
        if (sizes == null) {
            return;
        }
        int maxSize = 0;
        int width = 0;
        int height = 0;
        for (int i = 0; i < sizes.size(); i++) {
            Size size = sizes.get(i);
            int pix = size.width * size.height;
            if (pix > maxSize) {
                maxSize = pix;
                width = size.width;
                height = size.height;
            }
        }
        Log.i("tag", "图片的大小:" + width + " height:" + height);
        parameters.setPictureSize(width, height);
    }

    public Camera getCamera() {
        return mCamera;
    }

    // 自动对焦
    public void autoFocus(View v, MotionEvent event) {
        if (isShowFrame) {
            return;
        }
        mCamera.autoFocus(new AutoFocusCallback() {

            @Override
            public void onAutoFocus(boolean success, Camera camera) {
            }
        });

        RelativeLayout layout = (RelativeLayout) v;

        final ImageView imageView = new ImageView(mContext);
        Bitmap bitmap = BitmapFactory.decodeResource(mContext.getResources(),
                R.drawable.lcamera_focus_frame1);
        imageView.setImageBitmap(bitmap);
        LayoutParams params = new RelativeLayout.LayoutParams(
                bitmap.getWidth(), bitmap.getHeight());
        // imageView.setLayoutParams(params);
        Log.e("tag", "bitmap.getWidth:" + bitmap.getWidth());
        params.leftMargin = (int) (event.getX() - bitmap.getWidth() / 2);
        params.topMargin = (int) (event.getY() - bitmap.getHeight() / 2);
        layout.addView(imageView, params);
        imageView.setVisibility(View.VISIBLE);
        ScaleAnimation animation = new ScaleAnimation(1, 0.5f, 1, 0.5f,
                Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF,
                0.5f);
        animation.setDuration(300);
        animation.setFillAfter(true);
        animation.setAnimationListener(new AnimationListener() {

            @Override
            public void onAnimationStart(Animation animation) {
            }

            @Override
            public void onAnimationRepeat(Animation animation) {
            }

            @Override
            public void onAnimationEnd(final Animation animation) {
                imageView.setImageResource(R.drawable.lcamera_focus_frame2);

                new Thread() {
                    public void run() {
                        try {
                            Thread.sleep(400);
                            ((Activity) (mContext))
                                    .runOnUiThread(new Runnable() {

                                        @Override
                                        public void run() {
                                            imageView
                                                    .setImageResource(R.drawable.lcamera_focus_frame3);
                                        }
                                    });
                            Thread.sleep(200);
                            ((Activity) (mContext))
                                    .runOnUiThread(new Runnable() {

                                        @Override
                                        public void run() {
                                            imageView.clearAnimation();
                                            imageView.setVisibility(View.GONE);
                                            isShowFrame = false;
                                        }
                                    });
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }

                    };
                }.start();
            }
        });
        imageView.startAnimation(animation);
        isShowFrame = true;
    }

    // 拍照
    public void takePicture(final Handler handler) {
        mCamera.takePicture(null, null, new PictureCallback() {

            @Override
            public void onPictureTaken(byte[] data, Camera camera) {
                FileOutputStream fos = null;
                try {
                    File directory;
                    if (Environment.getExternalStorageState().equals(
                            Environment.MEDIA_MOUNTED)) {
                        directory = new File(Environment
                                .getExternalStorageDirectory(), "camera");
                    } else {
                        directory = new File(mContext.getCacheDir(), "camera");
                    }
                    if (!directory.exists()) {
                        directory.mkdir();
                    }
                    File file = new File(directory, System.currentTimeMillis()
                            + ".jpg");
                    Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0,
                            data.length);
                    fos = new FileOutputStream(file);
                    boolean compress = bitmap.compress(CompressFormat.JPEG,
                            100, fos);
                    if (compress) {
                        handler.sendEmptyMessage(MainActivity.MESSAGE_SVAE_SUCCESS);
                    } else {
                        handler.sendEmptyMessage(MainActivity.MESSAGE_SVAE_FAILURE);
                    }
                    mCamera.startPreview();
                    Log.i("tag", " 保存是否成功:" + compress + "  file.exists:"
                            + file.exists());
                } catch (FileNotFoundException e) {
                    e.printStackTrace();
                } finally {
                    if (fos != null) {
                        try {
                            fos.close();
                        } catch (IOException e) {
                        }
                    }
                }

            }
        });

    }

    // 多镜头切换
    public void switchCamera(SurfaceView surfaceView, boolean isFrontCamera) {
        if (mCamera != null) {
            mCamera.stopPreview();
            mCamera.release();
            mCamera = null;
        }

        int cameraId = isFrontCamera ? CAMERA_FACING_FRONT : CAMERA_FACING_BACK;// CAMERA_FACING_FRONT为前置摄像头

        mCamera = open(cameraId);
        Parameters parameters = mCamera.getParameters();
        Log.e("tag",
                "parameters.getPictureSize()"
                        + parameters.getPictureSize().width);
        setPictureSize(parameters);

        parameters.setPictureFormat(PixelFormat.JPEG);
        mCamera.setParameters(parameters);
        Log.e("tag",
                "2 parameters.getPictureSize()"
                        + parameters.getPictureSize().width);
        setDisplayOrientation(mCamera);
        try {
            mCamera.setPreviewDisplay(mHolder);
            mCamera.startPreview();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    public boolean isSupportedZoom() {
        if (mCamera != null) {
            Parameters parameters = mCamera.getParameters();
            return parameters.isZoomSupported();
        }
        return false;
    }

    public int getMaxZoom() {
        if (mCamera == null) {
            mCamera = Camera.open();
        }
        mParameters = mCamera.getParameters();
        return mParameters.getMaxZoom();
    }

    // 设置Zoom
    public void setZoom(int value) {
        Log.i("tag", "value:" + value);
        mParameters.setZoom(value);
        mCamera.setParameters(mParameters);
        mCamera.startPreview();
    }

    public boolean isSupportedFlashMode() {
        if (mCamera == null) {
            mCamera = Camera.open();
        }
        Parameters parameters = mCamera.getParameters();
        List<String> modes = parameters.getSupportedFlashModes();
        if (modes != null && modes.size() != 0) {
            boolean autoSupported = modes.contains(Parameters.FLASH_MODE_AUTO);
            boolean onSupported = modes.contains(Parameters.FLASH_MODE_ON);
            boolean offSupported = modes.contains(Parameters.FLASH_MODE_OFF);
            return autoSupported && onSupported && offSupported;
        }
        return false;
    }

    // 设置闪光灯模式
    public void SetFlashMode(int flashMode) {
        switch (flashMode) {
        case FLASH_MODE_AUTO:
            mParameters.setFlashMode(Parameters.FLASH_MODE_AUTO);
            break;
        case FLASH_MODE_ON:
            mParameters.setFlashMode(Parameters.FLASH_MODE_ON);
            break;
        case FLASH_MODE_OFF:
            mParameters.setFlashMode(Parameters.FLASH_MODE_OFF);
            break;
        }
        mCamera.setParameters(mParameters);
        mCamera.startPreview();
    }
}

layout中main.xml声明相应的surfaceview和相应的button/seekbar

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/RelativeLayout1"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

    <SurfaceView
        android:id="@+id/surfaceView1"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:layout_marginBottom="55dp" />

    <RelativeLayout
        android:id="@+id/camera_bottom"
        android:layout_width="fill_parent"
        android:layout_height="55.0dip"
        android:layout_alignParentBottom="true"
        android:layout_alignParentRight="true"
        android:background="#88ffffff"
        android:orientation="vertical" >

        <Button
            android:id="@+id/camera_take_btn"
            android:layout_width="60dp"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:layout_gravity="center"
            android:layout_marginLeft="3.0dip"
            android:text="拍照" />
    </RelativeLayout>

    <Button
        android:id="@+id/camera_switch_btn"
        android:layout_width="120dp"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="5dp"
        android:gravity="center"
        android:text="打开前置摄像头" >
    </Button>

    <Button
        android:id="@+id/flashMode"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        android:layout_marginTop="5dp"
        android:text="flash_auto" />

    <LinearLayout
        android:id="@+id/zoomLayout"
        android:layout_width="wrap_content"
        android:layout_height="20dp"
        android:layout_above="@+id/camera_bottom"
        android:layout_centerHorizontal="true"
        android:layout_marginBottom="5dp"
        android:orientation="horizontal" >

        <TextView
            android:id="@+id/textView1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text=" - "
            android:textColor="#ffffff" />

        <SeekBar
            android:id="@+id/seekBar1"
            android:layout_width="250dp"
            android:layout_height="13dp"
            android:layout_gravity="center_vertical"
            android:maxHeight="6dp"
            android:progressDrawable="@drawable/seekbar_progress"
            android:thumb="@drawable/camera_seekbar_progress_ball"
            android:thumbOffset="0dp" />

        <TextView
            android:id="@+id/textView2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="top"
            android:gravity="top"
            android:text=" + "
            android:textColor="#ffffff" />
    </LinearLayout>

</RelativeLayout>

在AndroidManifest中做如下权限声明

    <uses-sdk android:minSdkVersion="8" />

    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.CAMERA" />

    <uses-feature android:name="android.hardware.camera" />
    <uses-feature android:name="android.hardware.camera.autofocus" />

 

 

 

posted on 2013-05-12 16:47  chlde2500  阅读(6080)  评论(2编辑  收藏  举报

导航