安卓下自定义相机简单实现

关于安卓自定义相机,网上有不少的源码。但是功能实现上一般都还会略有不足比如对焦方式,camera资源的释放等等。还有的自定义相机是基于opnCV实现,应用于AR场景,在这里我们暂时用不到这样高大上的技术。
具体实现为:自定义一个CameraSurfaceView,继承于SurfaceView.实现了在activity中自定义的CameraFocusListener接口用于相机对焦时对焦指示器的显示与隐藏,和OnTouchListener接口用于监听触摸对焦的触摸操作。
具体代码:

public class CameraSurfaceView extends SurfaceView implements CameraFocusListener,OnTouchListener {
    private static final String TAG = "CameraSurfaceView";
    //4:3的比例
    public static double RATIO = 3.0 / 5.0;
    private ImageView mIVIndicator;
    private PeportedCameraActivity mPeportedCameraActivity;

    public void setPeportedCameraActivity(PeportedCameraActivity mPeportedCameraActivity) {
        this.mPeportedCameraActivity = mPeportedCameraActivity;
    }

    public void setIVIndicator(ImageView IVIndicator) {
        this.mIVIndicator = IVIndicator;
        setOnTouchListener(this);
        Log.e("", "had executed  setOnTouchListener");
    }
    /**
     * 对焦的时候把对焦指示器显示出来
     */
    @Override
    public void onFocusBegin(float x,float y) {
        mIVIndicator.setX(x-mIVIndicator.getWidth()/2);
        mIVIndicator.setY(y-mIVIndicator.getHeight()/2);
        mIVIndicator.setAlpha(1.0f);        

    }
/**
 * 对焦结束,隐藏
 */
    @Override
    public void onFocusEnd() {
        mIVIndicator.setAlpha(0.0f);
    }

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            mPeportedCameraActivity.startFocus(event.getX(), event.getY());
            Log.e("onTouch", "检测到触摸操作");
            break;
        default:
            break;
        }
        return false;
    }

    public CameraSurfaceView(Context context) {
        super(context);

    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

        int height = MeasureSpec.getSize(heightMeasureSpec);
        int width = MeasureSpec.getSize(widthMeasureSpec);
        Log.d("Measured", "before width" + width + "height" + height);

        boolean isWidthLonger;
        int longSide;
        int shortSide;
        // 以短边为准确定一下长度?
        if (width < height) {
            height = (int) (width / RATIO);
            isWidthLonger = false;

        } else {
            width = (int) (height / RATIO);
            isWidthLonger = true;
        }

        Log.d("Measured", "after width" + width + "height" + height);
        setMeasuredDimension(width, height);

    }
}

Activity代码如下:

public class PeportedCameraActivity extends Activity  implements OnClickListener,AutoFocusCallback,SurfaceHolder.Callback, Camera.PictureCallback{

    private SurfaceHolder mSurfaceHolder;
    private CameraSurfaceView preview;
    private int mFrontCameraId = -1;
    private int mBackCameraId = -1;

    private CameraFocusListener focusListener;

    TextView camera_cancle;
    TextView local_image;
    Camera camera;
    TextView fotoButton;
    String savatar;
    private File favatar;
    public static int REQUEST_CODE_PICK_IMAGE = 0;
    private LinearLayout rl_content;
    private ImageView iv_show;
    private TextView top_back;
    private TextView cancle;
    private LinearLayout fl;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.reported_camera);
        initview();
    }

    private void initview() {
        //拍照按钮
        fotoButton = (TextView) findViewById(R.id.imageView_foto);
        //查看相册
        local_image = (TextView) findViewById(R.id.imageView_localpic);
        //取消按钮
        camera_cancle = (TextView) findViewById(R.id.imageView_cancle);
        rl_content = (LinearLayout) findViewById(R.id.rl_upload);
        iv_show = (ImageView) findViewById(R.id.iv_show);
        top_back = (TextView) findViewById(R.id.top_back);
        cancle = (TextView) findViewById(R.id.imageView_cancle);

        fl = (LinearLayout) findViewById(R.id.KutCameraFragment);
        preview = new CameraSurfaceView(this);
        preview.getHolder().addCallback(this);
        focusListener=preview;

        ImageView imageView=new ImageView(this);
        Bitmap bitmap=BitmapFactory.decodeResource(getResources(), R.drawable.focus_on_touch);
        imageView.setImageBitmap(bitmap);
        imageView.setAlpha(0.0f);
        preview.setIVIndicator(imageView);
        preview.setPeportedCameraActivity(this);

        RelativeLayout layout = new RelativeLayout(this);
        layout.addView(preview);
        layout.addView(imageView);
        fl.addView(layout);

        preview.setKeepScreenOn(true);
        fotoButton.setOnClickListener(this);
        local_image.setOnClickListener(this);
        top_back.setOnClickListener(this);
        cancle.setOnClickListener(this);
    }

    @Override
    protected void onResume() {
        super.onResume();
        // TODO Auto-generated method stub
        if(camera==null){
            camera = Camera.open();
            camera.startPreview();
            //preview.setCamera(camera);
            findAvailableCameras();
        }
    }


    /**
     * 获得可用的相机,并设置前后摄像机的ID
     */
    private void findAvailableCameras() {

        Camera.CameraInfo info = new CameraInfo();
        int numCamera = Camera.getNumberOfCameras();
        for (int i = 0; i < numCamera; i++) {
            Camera.getCameraInfo(i, info);
            // 找到了前置摄像头
            if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
                mFrontCameraId = info.facing;
            }
            // 招到了后置摄像头
            if (info.facing == Camera.CameraInfo.CAMERA_FACING_BACK) {
                mBackCameraId = info.facing;
            }

        }
        getCorrectOrientation();
    }

    public void takePicture() {
        camera.takePicture(null, null, this);
    }

    private void startPreView() {
        try {
            camera.setPreviewDisplay(mSurfaceHolder);
            setPreviewSize();
            setDisplayOrientation();
            camera.startPreview();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }

    Camera.ShutterCallback mShutterCallback = new ShutterCallback() {

        @Override
        public void onShutter() {

        }
    };

    PictureCallback rawCallback = new PictureCallback() {
        public void onPictureTaken(byte[] data, Camera camera) {
            // Log.d(TAG, "onPictureTaken - raw");
        }
    };



    public static Bitmap rotate(Bitmap source, float angle) {
        Matrix matrix = new Matrix();
        matrix.postRotate(angle);
        return Bitmap.createBitmap(source, 0, 0, source.getWidth(),
                source.getHeight(), matrix, false);
    }


    @Override
    public void onClick(View v) {
        switch (v.getId()) {
        case R.id.imageView_cancle:
            finish();
            break;

        case R.id.top_back:
            finish();
            break;

        case R.id.imageView_foto:
            takePicture();
            break;

        case R.id.imageView_localpic:
            Intent localIntent = new Intent(Intent.ACTION_GET_CONTENT);
            localIntent.setType("image/*");
            startActivityForResult(localIntent, REQUEST_CODE_PICK_IMAGE);
            //camera = null;
            break;
        }
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == REQUEST_CODE_PICK_IMAGE && resultCode == Activity.RESULT_OK) {
            FileOutputStream out = null;
            savatar = Environment.getExternalStorageDirectory().toString()+ "/guixue/tmp/";
            favatar = new File(savatar);
            if (!favatar.exists()) {
                favatar.mkdirs();
            }
            new DateFormat();
            String name = DateFormat.format("yyyyMMdd_hhmmss",Calendar.getInstance(Locale.CHINA))+ ".jpg";
            savatar = savatar +"avatar.jpg";
            favatar = new File(savatar);
            Uri uri = data.getData();
            ContentResolver contentResolver = getContentResolver();
            try {
                Bitmap bitmap = BitmapFactory.decodeStream(contentResolver.openInputStream(uri));
                out = new FileOutputStream(favatar);
                bitmap.compress(Bitmap.CompressFormat.JPEG, 100, out);
                iv_show.setImageBitmap(bitmap);
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            }finally{
                try {
                    out.flush();
                    out.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            rl_content.setVisibility(View.VISIBLE);
        }
    }

    public void startFocus(float x,float y){
        focusListener.onFocusBegin(x,y);
        camera.autoFocus(this);
        Log.e("startFocus", "startFocus(float x,float y) is executed !");
    }

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

    @Override
    public void onPictureTaken(byte[] data, Camera camera) {
        savatar = Environment.getExternalStorageDirectory().toString()+"/guixue/tmp/";
        FileOutputStream outStream = null;
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        Calendar c = Calendar.getInstance();
        favatar = new File(savatar);

        if (!favatar.exists()) {
            favatar.mkdirs();
        }
        savatar = savatar +"avatar.jpg";
        favatar = new File(savatar);
        try {
            // Write to SD Card
            outStream = new FileOutputStream(favatar);
            outStream.write(data);
            outStream.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {

        }

        Bitmap realImage;
        final BitmapFactory.Options options = new BitmapFactory.Options();
        options.inSampleSize = 5;
        options.inPurgeable=true;                   //Tell to gc that whether it needs free memory, the Bitmap can be cleared
        options.inInputShareable=true;              //Which kind of reference will be used to recover the Bitmap data after being clear, when it will be used in the future
        realImage = BitmapFactory.decodeByteArray(data,0,data.length,options);
        ExifInterface exif = null;
        try {
            exif = new ExifInterface(savatar);
        } catch (IOException e) {
            e.printStackTrace();
        }

        try {
            Log.d("EXIF value",
                    exif.getAttribute(ExifInterface.TAG_ORIENTATION));
            if (exif.getAttribute(ExifInterface.TAG_ORIENTATION)
                    .equalsIgnoreCase("1")) {
                realImage = rotate(realImage, 90);
            } else if (exif.getAttribute(ExifInterface.TAG_ORIENTATION)
                    .equalsIgnoreCase("8")) {
                realImage = rotate(realImage, 90);
            } else if (exif.getAttribute(ExifInterface.TAG_ORIENTATION)
                    .equalsIgnoreCase("3")) {
                realImage = rotate(realImage, 90);
            } else if (exif.getAttribute(ExifInterface.TAG_ORIENTATION)
                    .equalsIgnoreCase("0")) {
                realImage = rotate(realImage, 90);
            }
        } catch (Exception e) {

        }

        fotoButton.setClickable(true);
        rl_content.setVisibility(View.VISIBLE);
        iv_show.setImageBitmap(realImage);
    }


    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width,
            int height) {

    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        mSurfaceHolder = holder;

        startPreView();

    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        if (camera != null) {
            camera.setPreviewCallback(null);
            camera.stopPreview();
            camera.release();
            camera=null;
        }
    }

    /**
     * 我们用4比3的比例设置预览图片
     */
    private void setPreviewSize() {
        Camera.Parameters params = camera.getParameters();
        List sizes = params.getSupportedPreviewSizes();
        for (Size size : sizes) {
            Log.d("previewSize", "width:" + size.width + " height " + size.height);
        }
        for (Size size : sizes) {
            if (size.width / 5 == size.height / 3) {
                params.setPreviewSize(size.width, size.height);
                Log.d("previewSize", "SET width:" + size.width + " height " + size.height);
                break;
            }
        }
        // params一定要记得写回Camera
        camera.setParameters(params);
    }

    private void setDisplayOrientation() {
        int displayOrientation = getCorrectOrientation();

        camera.setDisplayOrientation(displayOrientation);
    }

    /**
     * 让预览跟照片符合正确的方向。
* 因为预览默认是横向的。如果是一个竖向的应用,就需要把预览转90度
* 比如横着时1280*960的尺寸时,1280是宽.
* 竖着的时候1280就是高了
* 这段代码来自官方API。意思就是让拍出照片的方向和预览方向正确的符合设备当前的方向(有可能是竖向的也可能使横向的) * */
private int getCorrectOrientation() { android.hardware.Camera.CameraInfo info = new android.hardware.Camera.CameraInfo(); android.hardware.Camera.getCameraInfo(mBackCameraId, info); int rotation = this.getWindowManager().getDefaultDisplay().getRotation(); int degrees = 0; switch (rotation) { case Surface.ROTATION_0: degrees = 0; break; case Surface.ROTATION_90: degrees = 90; break; case Surface.ROTATION_180: degrees = 180; break; case Surface.ROTATION_270: degrees = 270; break; } int result; if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) { result = (info.orientation + degrees) % 360; result = (360 - result) % 360; // compensate the mirror } else { // back-facing result = (info.orientation - degrees + 360) % 360; } Log.d("orientationResult", result + ""); return result; } public interface CameraFocusListener { public void onFocusBegin(float x,float y); public void onFocusEnd(); } }

activity布局如下:

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    tools:context=".MainActivity" >

    <FrameLayout
        android:id="@+id/preview"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:layout_weight="1" >

        <LinearLayout
            android:id="@+id/KutCameraFragment"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:orientation="vertical" />

        <RelativeLayout
            android:id="@+id/rel_layout"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" >

            <RelativeLayout
                android:id="@+id/RelativeLayout1"
                android:layout_width="match_parent"
                android:layout_height="90dp"
                android:layout_alignParentBottom="true"
                android:background="@android:color/black"
                android:padding="10dp" >

                <TextView
                    android:id="@+id/imageView_foto"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_centerHorizontal="true"
                    android:layout_centerVertical="true"
                    android:background="@drawable/camera_down" />

                <TextView
                    android:id="@+id/imageView_localpic"
                    android:layout_width="80dp"
                    android:layout_height="100dp"
                    android:gravity="center_vertical"
                    android:padding="5dp"
                    android:paddingLeft="30dp"
                    android:text="相册"
                    android:textColor="#ffffff"
                    android:textSize="20sp" />

                <TextView
                    android:id="@+id/imageView_cancle"
                    android:layout_width="80dp"
                    android:layout_height="100dp"
                    android:layout_alignParentRight="true"
                    android:gravity="center"
                    android:padding="5dp"
                    android:paddingLeft="30dp"
                    android:text="取消"
                    android:textColor="#ffffff"
                    android:textSize="20sp" />
            RelativeLayout>
        RelativeLayout>
    FrameLayout>

    <LinearLayout
        android:id="@+id/rl_upload"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="#ffffff"
        android:orientation="vertical"
        android:visibility="gone" >

        <RelativeLayout
            android:layout_width="fill_parent"
            android:layout_height="55dp"
            android:background="#00aeff" >

            <include layout="@layout/main_tab_top" />
        RelativeLayout>

        <ImageView
            android:id="@+id/iv_show"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_marginBottom="5dp"
            android:layout_weight="1"
            android:scaleType="fitXY" />

        <Button
            android:id="@+id/upload_btn"
            android:layout_width="match_parent"
            android:layout_height="50dp"
            android:layout_alignParentBottom="true"
            android:layout_marginBottom="5dp"
            android:layout_marginLeft="40dp"
            android:layout_marginRight="40dp"
            android:background="@drawable/login_btn_bg"
            android:text="上传"
            android:textSize="20sp" />
    LinearLayout>

FrameLayout>

代码是项目中先前已经写完了的,但是后来要加入触摸对焦,我就在他的基础上做了修改,部分地方来未来得及改进。

注意几点:
1、在自定义的surfaceView中,我将预览比例调至5:3,具体情况可根据自己的实际情况而定。(最一开始写的4:3,但是实际效果由于比例问题,在预览的相机画面下方会多一条灰边,调至5:3即可)
2、从相机至相册,surfaceview 停止,释放相机资源并置空,在onResume中以相机是否为空进行判断,务必将资源的释放与链接控制好。

你可能感兴趣的:(安卓多媒体)