Android自定義camera相機(jī) 系列(一)

該文章 主要使用 自定義 surfaceview 及 camera 知識點(diǎn)劣挫,來實(shí)現(xiàn)一個自定義的拍照 碍遍、切換閃光燈 和 前后攝像頭的功能场晶。閱讀需要消耗時間 :15分鐘+ 伪冰。內(nèi)容比較簡單算是 開發(fā)相機(jī)的過程記錄把。

本文已獨(dú)家授權(quán)微信公眾號:鴻洋(hongyangAndroid)在微信公眾號平臺原創(chuàng)首發(fā)

GitHub Demo 地址 :yangmingchuan / SunCamera

1. 調(diào)用原生相機(jī)

在記錄自定義camera相機(jī)前哄芜,先簡單提一下調(diào)用系統(tǒng)相機(jī)和獲取圖片返回值的方式貌亭。

Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT,Uri.fromFile(mCurrentPhotoFile));
startActivityForResult(intent, CAMERA_WITH_DATA);

其中 mCurrentPhotoFile 為圖片返回的名稱。

2. 自定義相機(jī)

對于程序在本地真機(jī)的運(yùn)行效果這里先放一個效果圖认臊,如果是讀者需要的效果圃庭,則您可以接著往下看。如果急需的則可以在文章末尾直接查看完整的 代碼美尸。

自定義camera效果圖

看完 效果圖后冤议,我們簡要的說一下 自定義 camera 的過程

1.創(chuàng)建顯示相機(jī)畫面的布局斟薇,Android已經(jīng)為我們選定好SurfaceView
2.創(chuàng)建預(yù)覽界面师坎,創(chuàng)建繼承自SurfaceView并實(shí)現(xiàn)SurfaceHolder接口的拍攝預(yù)覽類。有了拍攝預(yù)覽類堪滨,即可創(chuàng)建一個布局文件胯陋,將預(yù)覽畫面與設(shè)計好的用戶界面控件融合在一起,實(shí)時顯示相機(jī)的預(yù)覽圖像袱箱。
3.設(shè)置拍照監(jiān)聽器遏乔,給用戶界面控件綁定監(jiān)聽器,使其能響應(yīng)用戶操作, 開始拍照過程发笔。
4.拍照并保存文件盟萨,將拍攝獲得的圖像輸出保存成各種常用格式的圖片。
5.當(dāng)相機(jī)使用完畢后了讨,必須正確地將其釋放捻激,以免其它程序訪問使用時發(fā)生沖突制轰。

針對 camera 和 camera2 自定義的不同

1.camera 中使用的顯示的對象為 SurfaceView ,SurfaceView是一個有自己Surface的View胞谭。界面渲染可以放在單獨(dú)線程而不是主線程中垃杖。它更像是一個Window,自身不能做變形和動畫丈屹。

2.camera2 中使用的顯示的載體為 TextureView 调俘,同樣也有自己的Surface。但是它只能在擁有硬件加速層層的Window中繪制旺垒,它更像是一個普通View彩库,可以做變形和動畫。

2.1 添加需要的權(quán)限

目前測試手機(jī):小米6 先蒋,android 版本:27 侧巨。
對于部分危險權(quán)限除了清單文件中的聲明,還需要動態(tài)申請

// 拍照權(quán)限
<uses-permission android:name="android.permission.CAMERA" />
// 網(wǎng)絡(luò)
<uses-permission android:name="android.permission.INTERNET" />
// 讀寫本地存儲
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
// 自動對焦
<uses-feature android:name="android.hardware.camera.autofocus" />

接下來會是該Demo中主要用于 控件聲明和動態(tài)權(quán)限所需要添加的第三方庫

// butterknife
implementation 'com.jakewharton:butterknife:8.8.1'
annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1'

// permission
implementation 'com.yanzhenjie:permission:2.0.0-rc4'
implementation 'com.android.support:exifinterface:28.0.0'

在對應(yīng)的 界面或者首界面添加動態(tài)權(quán)限申請

    /**
     * 動態(tài)申請  (電話/位置/存儲)
     */
    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
    private void requestPermission() {
        AndPermission.with(this)
                .permission(Manifest.permission.CAMERA,
                        Manifest.permission.READ_EXTERNAL_STORAGE)
                .rationale(new Rationale() {
                    @Override
                    public void showRationale(Context context, List<String> permissions, RequestExecutor executor) {
                        executor.execute();
                    }
                })
                .onGranted(new Action() {
                    @Override
                    public void onAction(List<String> permissions) {
                        Log.e(TAG, "用戶給權(quán)限");
                    }
                })
                .onDenied(new Action() {
                    @Override
                    public void onAction(List<String> permissions) {
                        if (AndPermission.hasAlwaysDeniedPermission(MainActivity.this, permissions)) {
                            // 打開權(quán)限設(shè)置頁
                            AndPermission.permissionSetting(MainActivity.this).execute();
                            return;
                        }
                        Log.e(TAG, "用戶拒絕權(quán)限");
                    }
                })
                .start();
    }

2.2 添加布局

布局中包含的信息主要有 SurfaceView 鞭达、拍照button 司忱、閃光燈按鈕及切換鏡頭按鈕

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@android:color/black"
    tools:context="cn.tongue.tonguecamera.ui.CameraActivity">

    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        
        <SurfaceView
            android:id="@+id/surfaceView"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />

    </FrameLayout>

    <RelativeLayout
        android:id="@+id/homecamera_bottom_relative"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="#00ffffff"
        android:layout_alignParentBottom="true">

        // 返回按鈕
        <ImageView
            android:id="@+id/iv_back"
            android:layout_width="40dp"
            android:layout_height="30dp"
            android:scaleType="centerInside"
            android:layout_marginBottom="20dp"
            android:layout_marginStart="20dp"
            android:layout_centerVertical="true"
            android:background="@drawable/icon_back" />
        
        // 拍照
        <ImageView
            android:id="@+id/img_camera"
            android:layout_width="80dp"
            android:layout_height="80dp"
            android:scaleType="centerInside"
            android:layout_marginBottom="20dp"
            android:layout_centerInParent="true"
            android:background="@drawable/camera" />

    </RelativeLayout>

    <LinearLayout
        android:id="@+id/home_custom_top_relative"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:gravity="center_vertical"
        android:orientation="horizontal"
        android:background="#00ffffff"
        android:layout_alignParentTop="true"
        >
        
        // 切換閃光燈
        <ImageView
            android:id="@+id/camera_flash"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:padding="10dp"
            android:src="@drawable/icon_camera_off" />

        <View
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="5"/>

        // 前/后 鏡頭
        <ImageView
            android:id="@+id/camera_switch"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:padding="10dp"
            android:src="@drawable/btn_camera_turn_n" />

    </LinearLayout>

</RelativeLayout>

2.3 代碼設(shè)置camera

在添加完拍照所需要的權(quán)限和布局后,我們就可以在Activity中書寫對應(yīng)的 邏輯和聲明了畴蹭。

2.3.1 通過 SurfaceView 獲取需要數(shù)據(jù)

    // 部分對象的聲明
    private Camera mCamera;
    private SurfaceHolder mHolder;

    mHolder = svContent.getHolder();
    mHolder.addCallback(this);

    // SurfaceHolder 的監(jiān)聽事件
    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        // 相機(jī)預(yù)覽
        startPreview(mCamera, holder);
    }

    // 畫布改變 調(diào)用 相機(jī)預(yù)覽
    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        mCamera.stopPreview();
        startPreview(mCamera, holder);
    }

    // 畫布銷毀  回收相機(jī)
    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        releaseCamera();
    }


     /**
     * 預(yù)覽相機(jī)
     */
    private void startPreview(Camera camera, SurfaceHolder holder) {
        try {
            // 確認(rèn)相機(jī)預(yù)覽尺寸
            setupCamera(camera);
            camera.setPreviewDisplay(holder);
            cameraInstance.setCameraDisplayOrientation(this, mCameraId, camera);
            camera.startPreview();
            isView = true;
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 設(shè)置surfaceView的尺寸 因?yàn)閏amera默認(rèn)是橫屏坦仍,所以取得支持尺寸也都是橫屏的尺寸
     * 我們在startPreview方法里面把它矯正了過來,但是這里我們設(shè)置設(shè)置surfaceView的尺寸的時候要注意 previewSize.height<previewSize.width
     * previewSize.width才是surfaceView的高度
     * 一般相機(jī)都是屏幕的寬度 這里設(shè)置為屏幕寬度 高度自適應(yīng) 你也可以設(shè)置自己想要的大小
     */
    private void setupCamera(Camera camera) {
        Camera.Parameters parameters = camera.getParameters();
        if (parameters.getSupportedFocusModes().contains(
                Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE)) {
            parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
        }
        //根據(jù)屏幕尺寸獲取最佳 大小
        Camera.Size previewSize = cameraInstance.getPicPreviewSize(parameters.getSupportedPreviewSizes(),
                screenHeight, screenWidth);
        parameters.setPreviewSize(previewSize.width, previewSize.height);

        Camera.Size pictrueSize = cameraInstance.getPicPreviewSize(parameters.getSupportedPictureSizes(),
                screenHeight,screenWidth);
        parameters.setPictureSize(pictrueSize.width, pictrueSize.height);
        camera.setParameters(parameters);
        // picHeight = (screenWidth * pictrueSize.width) / pictrueSize.height;
        picWidth = pictrueSize.width;
        picHeight = pictrueSize.height;
        FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(screenWidth,
                (screenWidth * pictrueSize.width) / pictrueSize.height);
        svContent.setLayoutParams(params);
    }

2.3.2 打開相機(jī)

為了避免界面切換 相機(jī)界面出現(xiàn)暫停情況叨襟,我們在onResume()方法中調(diào)用相機(jī)的獲取和相機(jī)的預(yù)覽操作繁扎。

    @Override
    protected void onResume() {
        super.onResume();
        if (mCamera == null) {
            mCamera = getCamera(mCameraId);
            if (mHolder != null) {
                startPreview(mCamera, mHolder);
            }
        }
    }

    /**
     * 獲取Camera實(shí)例
     *
     * @return Camera
     */
    private Camera getCamera(int id) {
        Camera camera = null;
        try {
            camera = Camera.open(id);
        } catch (Exception e) {
            Log.e(TAG, "getCamera: " + e);
        }
        return camera;
    }

2.3.3 釋放相機(jī)

     /**
     * 釋放相機(jī)資源
     */
    private void releaseCamera() {
        if (mCamera != null) {
            mCamera.setPreviewCallback(null);
            mCamera.stopPreview();
            mCamera.release();
            mCamera = null;
        }
    }
2.3.4 切換閃光燈模式

Camera.Parameters.FLASH_MODE_AUTO 自動模式,當(dāng)光線較暗時自動打開閃光燈糊闽;

Camera.Parameters.FLASH_MODE_OFF 關(guān)閉閃光燈梳玫;

Camera.Parameters.FLASH_MODE_ON 拍照時閃光燈;

Camera.Parameters.FLASH_MODE_RED_EYE 閃光燈參數(shù)右犹,防紅眼模式提澎。

    /**
     * 自動模式閃光燈
     *
     * @param mCamera mCamera
     */
    Camera.Parameters parameters = mCamera.getParameters();
    parameters.setFlashMode(Camera.Parameters.FLASH_MODE_AUTO);//開啟
                        mCamera.setParameters(parameters);

    /**
     * 關(guān)閉閃光燈
     *
     * @param mCamera mCamera
     */
    Camera.Parameters parameters = mCamera.getParameters();
    parameters.setFlashMode(Camera.Parameters.FLASH_MODE_OFF);
                        mCamera.setParameters(parameters);
                        
    /**
     * 打開閃關(guān)燈
     *
     * @param mCamera mCamera
     */
    Camera.Parameters parameters = mCamera.getParameters();
    parameters.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH);//開啟
    
2.3.5 切換閃光燈模式
    /**
     * 切換前后攝像頭
     */
    public void switchCamera() {
        releaseCamera();
        mCameraId = (mCameraId + 1) % Camera.getNumberOfCameras();
        mCamera = getCamera(mCameraId);
        if (mHolder != null) {
            startPreview(mCamera, mHolder);
        }
    }

2.3.6 部分配置參數(shù)

對焦模式配置參數(shù),可以通過Parameters.getFocusMode()接口獲取念链。

Camera.Parameters.FOCUS_MODE_AUTO 自動對焦模式盼忌,攝影小白專用模式;

Camera.Parameters.FOCUS_MODE_FIXED 固定焦距模式掂墓,拍攝老司機(jī)模式谦纱;

Camera.Parameters.FOCUS_MODE_EDOF 景深模式,文藝女青年最喜歡的模式君编;

Camera.Parameters.FOCUS_MODE_INFINITY 遠(yuǎn)景模式跨嘉,拍風(fēng)景大場面的模式;

Camera.Parameters.FOCUS_MODE_MACRO 微焦模式吃嘿,拍攝小花小草小螞蟻專用模式祠乃;

場景模式配置參數(shù)窘游,可以通過Parameters.getSceneMode()接口獲取。

Camera.Parameters.SCENE_MODE_BARCODE 掃描條碼場景跳纳,NextQRCode項(xiàng)目會判斷并設(shè)置為這個場景忍饰;

Camera.Parameters.SCENE_MODE_ACTION 動作場景,就是抓拍跑得飛快的運(yùn)動員寺庄、汽車等場景用的艾蓝;

Camera.Parameters.SCENE_MODE_AUTO 自動選擇場景;

Camera.Parameters.SCENE_MODE_HDR 高動態(tài)對比度場景斗塘,通常用于拍攝晚霞等明暗分明的照片赢织;

Camera.Parameters.SCENE_MODE_NIGHT 夜間場景;

2.3.7 整體的Activity代碼

接下來 我會將整體的Activity代碼都放上來馍盟。

CameraActivity

package cn.tongue.tonguecamera.ui;

import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.hardware.Camera;
import android.os.Environment;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.Toast;

import java.io.File;
import java.io.IOException;

import butterknife.BindView;
import butterknife.OnClick;
import cn.tongue.tonguecamera.R;
import cn.tongue.tonguecamera.base.BaseActivity;
import cn.tongue.tonguecamera.util.AppConstant;
import cn.tongue.tonguecamera.util.BitmapUtils;
import cn.tongue.tonguecamera.util.CameraUtil;

/**
 * 拍照界面
 * 5.0 版本以前的拍照
 *
 * @author ymc
 */

public class CameraActivity extends BaseActivity implements SurfaceHolder.Callback {
    private static final String TAG = "CameraActivity";
    @BindView(R.id.surfaceView)
    SurfaceView svContent;
    @BindView(R.id.img_camera)
    ImageView ivCamera;
    @BindView(R.id.camera_flash)
    ImageView ivFlash;
    @BindView(R.id.camera_switch)
    ImageView ivSwitch;
    @BindView(R.id.iv_back)
    ImageView ivBack;

    private Camera mCamera;
    private SurfaceHolder mHolder;
    private CameraUtil cameraInstance;
    /**
     * 屏幕寬高
     */
    private int screenWidth;
    private int screenHeight;
    /**
     * 圖片寬高
     */
    private int picWidth;

    /**
     * 是否有界面
     */
    private boolean isView = true;
    /**
     * 拍照id  1: 前攝像頭  0:后攝像頭
     */
    private int mCameraId = 0;
    /**
     * 閃光燈類型 0 :關(guān)閉 1: 打開 2:自動
     */
    private int light_type = 0;

    /**
     * 圖片高度
     */
    private int picHeight;

    @Override
    protected int getLayoutId() {
        return R.layout.activity_camera;
    }

    @Override
    protected void initView() {
        mHolder = svContent.getHolder();
        mHolder.addCallback(this);
    }

    @Override
    protected void initData() {
        cameraInstance = CameraUtil.getInstance();
        DisplayMetrics dm = getResources().getDisplayMetrics();
        screenWidth = dm.widthPixels;
        screenHeight = dm.heightPixels;
    }

    @Override
    protected void onResume() {
        super.onResume();
        if (mCamera == null) {
            mCamera = getCamera(mCameraId);
            if (mHolder != null) {
                startPreview(mCamera, mHolder);
            }
        }
    }

    @OnClick({R.id.img_camera, R.id.camera_flash, R.id.camera_switch, R.id.iv_back})
    public void OnClick(View view) {
        switch (view.getId()) {
            // 點(diǎn)擊拍照
            case R.id.img_camera:
                switch (light_type) {
                    case 0:
                        //關(guān)閉
                        cameraInstance.turnLightOff(mCamera);
                        break;
                    case 1:
                        cameraInstance.turnLightOn(mCamera);
                        break;
                    case 2:
                        //自動
                        cameraInstance.turnLightAuto(mCamera);
                        break;
                    default:
                        break;
                }
                takePhoto();
                break;
            // 切換閃光燈
            case R.id.camera_flash:
                if (mCameraId == 1) {
                    Toast.makeText(this, "請切換到后置攝像頭", Toast.LENGTH_LONG).show();
                    return;
                }
                Camera.Parameters parameters = mCamera.getParameters();
                switch (light_type) {
                    case 0:
                        //打開
                        light_type = 1;
                        ivFlash.setImageResource(R.drawable.icon_camera_on);
                        parameters.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH);//開啟
                        mCamera.setParameters(parameters);
                        break;
                    case 1:
                        //自動
                        light_type = 2;
                        parameters.setFlashMode(Camera.Parameters.FLASH_MODE_AUTO);
                        mCamera.setParameters(parameters);
                        ivFlash.setImageResource(R.drawable.icon_camera_a);
                        break;
                    case 2:
                        //關(guān)閉
                        light_type = 0;
                        //關(guān)閉
                        parameters.setFlashMode(Camera.Parameters.FLASH_MODE_OFF);
                        mCamera.setParameters(parameters);
                        ivFlash.setImageResource(R.drawable.icon_camera_off);
                        break;
                    default:
                        break;
                }
                break;
            //切換前后攝像頭
            case R.id.camera_switch:
                switchCamera();
                break;
            // 返回按鈕
            case R.id.iv_back:
                finish();
                break;
            default:
                break;
        }
    }

    /**
     * 切換前后攝像頭
     */
    public void switchCamera() {
        releaseCamera();
        mCameraId = (mCameraId + 1) % Camera.getNumberOfCameras();
        mCamera = getCamera(mCameraId);
        if (mHolder != null) {
            startPreview(mCamera, mHolder);
        }
    }

    /**
     * 拍照
     */
    private void takePhoto() {
        mCamera.takePicture(null, null, new Camera.PictureCallback() {
            @Override
            public void onPictureTaken(byte[] data, Camera camera) {
                isView = false;
                //將data 轉(zhuǎn)換為位圖 或者你也可以直接保存為文件使用 FileOutputStream
                //這里我相信大部分都有其他用處把 比如加個水印 后續(xù)再講解
                Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
                Bitmap saveBitmap = cameraInstance.setTakePicktrueOrientation(mCameraId, bitmap);
                saveBitmap = Bitmap.createScaledBitmap(saveBitmap, screenWidth, screenHeight, true);
                String imgpath = getExternalFilesDir(Environment.DIRECTORY_DCIM).getPath() +
                        File.separator + System.currentTimeMillis() + ".jpeg";
                Log.e(TAG, "imgpath: ---  " + imgpath);
                BitmapUtils.saveJPGE_After(getApplicationContext(), saveBitmap, imgpath, 100);
                if (!bitmap.isRecycled()) {
                    bitmap.recycle();
                }
                if (!saveBitmap.isRecycled()) {
                    saveBitmap.recycle();
                }
                Intent intent = new Intent();
                intent.putExtra(AppConstant.KEY.IMG_PATH, imgpath);
                intent.putExtra(AppConstant.KEY.PIC_WIDTH, picWidth);
                intent.putExtra(AppConstant.KEY.PIC_HEIGHT, picHeight);
                setResult(AppConstant.RESULT_CODE.RESULT_OK, intent);
                finish();
            }
        });

    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        startPreview(mCamera, holder);
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        mCamera.stopPreview();
        startPreview(mCamera, holder);
    }


    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        releaseCamera();
    }

    /**
     * 釋放相機(jī)資源
     */
    private void releaseCamera() {
        if (mCamera != null) {
            mCamera.setPreviewCallback(null);
            mCamera.stopPreview();
            mCamera.release();
            mCamera = null;
        }
    }

    /**
     * 預(yù)覽相機(jī)
     */
    private void startPreview(Camera camera, SurfaceHolder holder) {
        try {
            setupCamera(camera);
            camera.setPreviewDisplay(holder);
            cameraInstance.setCameraDisplayOrientation(this, mCameraId, camera);
            camera.startPreview();
            isView = true;
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 設(shè)置surfaceView的尺寸 因?yàn)閏amera默認(rèn)是橫屏于置,所以取得支持尺寸也都是橫屏的尺寸
     * 我們在startPreview方法里面把它矯正了過來,但是這里我們設(shè)置設(shè)置surfaceView的尺寸的時候要注意 previewSize.height<previewSize.width
     * previewSize.width才是surfaceView的高度
     * 一般相機(jī)都是屏幕的寬度 這里設(shè)置為屏幕寬度 高度自適應(yīng) 你也可以設(shè)置自己想要的大小
     */
    private void setupCamera(Camera camera) {
        Camera.Parameters parameters = camera.getParameters();
        if (parameters.getSupportedFocusModes().contains(
                Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE)) {
            parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
        }
        //根據(jù)屏幕尺寸獲取最佳 大小
        Camera.Size previewSize = cameraInstance.getPicPreviewSize(parameters.getSupportedPreviewSizes(),
                screenHeight, screenWidth);
        parameters.setPreviewSize(previewSize.width, previewSize.height);

        Camera.Size pictrueSize = cameraInstance.getPicPreviewSize(parameters.getSupportedPictureSizes(),
                screenHeight,screenWidth);
        parameters.setPictureSize(pictrueSize.width, pictrueSize.height);
        camera.setParameters(parameters);
//        picHeight = (screenWidth * pictrueSize.width) / pictrueSize.height;
        picWidth = pictrueSize.width;
        picHeight = pictrueSize.height;
        FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(screenWidth,
                (screenWidth * pictrueSize.width) / pictrueSize.height);
        svContent.setLayoutParams(params);
    }

    /**
     * 獲取Camera實(shí)例
     *
     * @return Camera
     */
    private Camera getCamera(int id) {
        Camera camera = null;
        try {
            camera = Camera.open(id);
        } catch (Exception e) {
            Log.e(TAG, "getCamera: " + e);
        }
        return camera;
    }

}

CameraUtil

package cn.tongue.tonguecamera.util;

import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.Matrix;
import android.hardware.Camera;
import android.hardware.Camera.Size;
import android.util.Log;
import android.view.Surface;

import java.util.Collections;
import java.util.Comparator;
import java.util.List;


/**
 * 拍照工具類
 */
public class CameraUtil {
    private static final String TAG = "CameraUtil";
    /**
     * 降序
     */
    private CameraDropSizeComparator dropSizeComparator = new CameraDropSizeComparator();
    /**
     * 升序
     */
    private CameraAscendSizeComparator ascendSizeComparator = new CameraAscendSizeComparator();
    private static CameraUtil instance = null;


    private CameraUtil() {

    }

    public static CameraUtil getInstance() {
        if (instance == null) {
            instance = new CameraUtil();
            return instance;
        } else {
            return instance;
        }
    }

    private int getRecorderRotation(int cameraId) {
        android.hardware.Camera.CameraInfo info =
                new android.hardware.Camera.CameraInfo();
        android.hardware.Camera.getCameraInfo(cameraId, info);
        return info.orientation;
    }

    /**
     * 獲取所有支持的返回視頻尺寸
     *
     * @param list      list
     * @param minHeight minHeight
     * @return Size
     */
    private Size getPropVideoSize(List<Size> list, int minHeight) {
        Collections.sort(list, ascendSizeComparator);

        int i = 0;
        for (Size s : list) {
            if ((s.height >= minHeight)) {
                break;
            }
            i++;
        }
        if (i == list.size()) {
            i = 0;
        }
        return list.get(i);
    }

    /**
     * 保證預(yù)覽方向正確
     *
     * @param activity activity
     * @param cameraId cameraId
     * @param camera   camera
     */
    public void setCameraDisplayOrientation(Activity activity,
                                             int cameraId, Camera camera) {
        android.hardware.Camera.CameraInfo info =
                new android.hardware.Camera.CameraInfo();
        android.hardware.Camera.getCameraInfo(cameraId, info);
        int rotation = activity.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;
            default:
                break;
        }
        int result;
        if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
            result = (info.orientation + degrees) % 360;
            result = (360 - result) % 360;
        } else {
            result = (info.orientation - degrees + 360) % 360;
        }
        //設(shè)置角度
        camera.setDisplayOrientation(result);
    }


    public Bitmap setTakePicktrueOrientation(int id, Bitmap bitmap) {
        Camera.CameraInfo info = new Camera.CameraInfo();
        Camera.getCameraInfo(id, info);
        bitmap = rotaingImageView(id, info.orientation, bitmap);
        return bitmap;
    }

    /**
     * 把相機(jī)拍照返回照片轉(zhuǎn)正
     *
     * @param angle 旋轉(zhuǎn)角度
     * @return bitmap 圖片
     */
    private Bitmap rotaingImageView(int id, int angle, Bitmap bitmap) {
        //矩陣
        Matrix matrix = new Matrix();
        matrix.postRotate(angle);
        //加入翻轉(zhuǎn) 把相機(jī)拍照返回照片轉(zhuǎn)正
        if (id == 1) {
            matrix.postScale(-1, 1);
        }
        // 創(chuàng)建新的圖片
        Bitmap resizedBitmap = Bitmap.createBitmap(bitmap, 0, 0,
                bitmap.getWidth(), bitmap.getHeight(), matrix, true);
        return resizedBitmap;
    }

    /**
     * 獲取所有支持的預(yù)覽尺寸
     *
     * @param list     list
     * @param minWidth minWidth
     * @return Size
     */
    private Size getPropPreviewSize(List<Size> list, int minWidth) {
        Collections.sort(list, ascendSizeComparator);

        int i = 0;
        for (Size s : list) {
            if ((s.width >= minWidth)) {
                break;
            }
            i++;
        }
        if (i == list.size()) {
            i = 0;
        }
        return list.get(i);
    }

    /**
     * 獲取所有支持的返回圖片尺寸
     *
     * @param list     list
     * @param minWidth minWidth
     * @return Size
     */
    private Size getPropPictureSize(List<Size> list, int minWidth) {
        Collections.sort(list, ascendSizeComparator);
        int i = 0;
        for (Size s : list) {
            if ((s.width >= minWidth)) {
                break;
            }
            i++;
        }
        if (i == list.size()) {
            i = 0;
        }
        return list.get(i);
    }

    /**
     * 獲取所有支持的返回視頻尺寸
     *
     * @param list      list
     * @param minHeight minHeight
     * @return Size
     */
    public Size getPropSizeForHeight(List<Size> list, int minHeight) {
        Collections.sort(list, ascendSizeComparator);
        int i = 0;
        for (Size s : list) {
            if ((s.height >= minHeight)) {
                Log.e(TAG, "getPropSizeForHeight: s.height=" + s.height);
                break;
            }
            i++;
        }
        if (i == list.size()) {
            i = list.size();
        }
        return list.get(i);
    }

    /**
     * 根據(jù) 寬度和高度找到是否有相等的 尺寸  如果沒有 就獲取最小的 值
     * @param list list
     * @param th 高度
     * @param minWidth 寬度
     * @return size
     */
    public  Size getPicPreviewSize(List<Camera.Size> list, int th, int minWidth){
        Collections.sort(list, ascendSizeComparator);

        int i = 0;
        for(int x=0;x<list.size();x++){
            Size s = list.get(x);
            // camera 中的寬度和高度 相反 因?yàn)闇y試板子原因 這里暫時 替換 && 為 ||
            if((s.width == th) && (s.height == minWidth)){
                i = x;
                break;
            }
        }
        //如果沒找到贞岭,就選最小的size 0
        return list.get(i);
    }

    public Size getPropPictureSize(List<Camera.Size> list, float th, int minWidth){
        Collections.sort(list, ascendSizeComparator);

        int i = 0;
        for(Size s:list){
            if((s.width >= minWidth) && equalRate(s, th)){
                Log.i(TAG, "PictureSize : w = " + s.width + "h = " + s.height);
                break;
            }
            i++;
        }
        if(i == list.size()){
            i = 0;//如果沒找到八毯,就選最小的size
        }
        return list.get(i);
    }

    /**
     * 升序 按照高度
     */
    private class CameraAscendSizeComparatorForHeight implements Comparator<Size> {
        @Override
        public int compare(Size lhs, Size rhs) {
            if (lhs.height == rhs.height) {
                return 0;
            } else if (lhs.height > rhs.height) {
                return 1;
            } else {
                return -1;
            }
        }
    }

    private boolean equalRate(Size s, float rate) {
        float r = (float) (s.width) / (float) (s.height);
        return Math.abs(r - rate) <= 0.03;
    }

    /**
     * 降序
     */
    private class CameraDropSizeComparator implements Comparator<Size> {
        @Override
        public int compare(Size lhs, Size rhs) {
            if (lhs.width == rhs.width) {
                return 0;
            } else if (lhs.width < rhs.width) {
                return 1;
            } else {
                return -1;
            }
        }
    }

    /**
     * 升序
     */
    private class CameraAscendSizeComparator implements Comparator<Size> {
        @Override
        public int compare(Size lhs, Size rhs) {
            if (lhs.width == rhs.width) {
                return 0;
            } else if (lhs.width > rhs.width) {
                return 1;
            } else {
                return -1;
            }
        }
    }

    /**
     * 打印支持的previewSizes
     *
     * @param params
     */
    private void printSupportPreviewSize(Camera.Parameters params) {
        List<Size> previewSizes = params.getSupportedPreviewSizes();
        for (int i = 0; i < previewSizes.size(); i++) {
            Size size = previewSizes.get(i);
        }

    }

    /**
     * 打印支持的pictureSizes
     *
     * @param params
     */
    private void printSupportPictureSize(Camera.Parameters params) {
        List<Size> pictureSizes = params.getSupportedPictureSizes();
        for (int i = 0; i < pictureSizes.size(); i++) {
            Size size = pictureSizes.get(i);
        }
    }

    /**
     * 打印支持的聚焦模式
     *
     * @param params params
     */
    private void printSupportFocusMode(Camera.Parameters params) {
        List<String> focusModes = params.getSupportedFocusModes();
        for (String mode : focusModes) {
            Log.e(TAG, "printSupportFocusMode: " + mode);
        }
    }

    /**
     * 打開閃關(guān)燈
     *
     * @param mCamera mCamera
     */
    public void turnLightOn(Camera mCamera) {
        if (mCamera == null) {
            return;
        }
        Camera.Parameters parameters = mCamera.getParameters();
        if (parameters == null) {
            return;
        }
        List<String> flashModes = parameters.getSupportedFlashModes();
        if (flashModes == null) {
            return;
        }
        String flashMode = parameters.getFlashMode();
        if (!Camera.Parameters.FLASH_MODE_ON.equals(flashMode)) {
            // Turn on the flash
            if (flashModes.contains(Camera.Parameters.FLASH_MODE_TORCH)) {
                parameters.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH);
                mCamera.setParameters(parameters);
            }
        }
    }

    /**
     * 自動模式閃光燈
     *
     * @param mCamera mCamera
     */
    public void turnLightAuto(Camera mCamera) {
        if (mCamera == null) {
            return;
        }
        Camera.Parameters parameters = mCamera.getParameters();
        if (parameters == null) {
            return;
        }
        List<String> flashModes = parameters.getSupportedFlashModes();
        if (flashModes == null) {
            return;
        }
        String flashMode = parameters.getFlashMode();
        if (!Camera.Parameters.FLASH_MODE_AUTO.equals(flashMode)) {
            // Turn on the flash
            if (flashModes.contains(Camera.Parameters.FLASH_MODE_TORCH)) {
                parameters.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH);
                mCamera.setParameters(parameters);
            }
        }
    }

    /**
     * 關(guān)閉閃光燈
     *
     * @param mCamera mCamera
     */
    public void turnLightOff(Camera mCamera) {
        if (mCamera == null) {
            return;
        }
        Camera.Parameters parameters = mCamera.getParameters();
        if (parameters == null) {
            return;
        }
        List<String> flashModes = parameters.getSupportedFlashModes();
        String flashMode = parameters.getFlashMode();
        if (flashModes == null) {
            return;
        }
        if (!Camera.Parameters.FLASH_MODE_OFF.equals(flashMode)) {
            if (flashModes.contains(Camera.Parameters.FLASH_MODE_TORCH)) {
                parameters.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH);
                mCamera.setParameters(parameters);
            }
        }
    }
}

如果想要更加詳細(xì)的了解 camera + surfaceview 請移步博客頂部GitHub地址內(nèi) clone project 運(yùn)行即可

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市瞄桨,隨后出現(xiàn)的幾起案子话速,更是在濱河造成了極大的恐慌,老刑警劉巖芯侥,帶你破解...
    沈念sama閱讀 221,576評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件泊交,死亡現(xiàn)場離奇詭異,居然都是意外死亡柱查,警方通過查閱死者的電腦和手機(jī)廓俭,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,515評論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來唉工,“玉大人研乒,你說我怎么就攤上這事〗妥希” “怎么了告嘲?”我有些...
    開封第一講書人閱讀 168,017評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長奖地。 經(jīng)常有香客問我,道長赋焕,這世上最難降的妖魔是什么参歹? 我笑而不...
    開封第一講書人閱讀 59,626評論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮隆判,結(jié)果婚禮上犬庇,老公的妹妹穿的比我還像新娘僧界。我一直安慰自己,他們只是感情好臭挽,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,625評論 6 397
  • 文/花漫 我一把揭開白布捂襟。 她就那樣靜靜地躺著,像睡著了一般欢峰。 火紅的嫁衣襯著肌膚如雪葬荷。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,255評論 1 308
  • 那天纽帖,我揣著相機(jī)與錄音宠漩,去河邊找鬼。 笑死懊直,一個胖子當(dāng)著我的面吹牛扒吁,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播室囊,決...
    沈念sama閱讀 40,825評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼雕崩,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了融撞?” 一聲冷哼從身側(cè)響起晨逝,我...
    開封第一講書人閱讀 39,729評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎懦铺,沒想到半個月后捉貌,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,271評論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡冬念,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,363評論 3 340
  • 正文 我和宋清朗相戀三年趁窃,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片急前。...
    茶點(diǎn)故事閱讀 40,498評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡醒陆,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出裆针,到底是詐尸還是另有隱情刨摩,我是刑警寧澤,帶...
    沈念sama閱讀 36,183評論 5 350
  • 正文 年R本政府宣布世吨,位于F島的核電站澡刹,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏耘婚。R本人自食惡果不足惜罢浇,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,867評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧嚷闭,春花似錦攒岛、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,338評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至嗅榕,卻和暖如春顺饮,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背誊册。 一陣腳步聲響...
    開封第一講書人閱讀 33,458評論 1 272
  • 我被黑心中介騙來泰國打工领突, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人案怯。 一個月前我還...
    沈念sama閱讀 48,906評論 3 376
  • 正文 我出身青樓君旦,卻偏偏與公主長得像,于是被迫代替她去往敵國和親嘲碱。 傳聞我的和親對象是個殘疾皇子金砍,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,507評論 2 359

推薦閱讀更多精彩內(nèi)容

  • Android中開發(fā)相機(jī)的兩種方式Android系統(tǒng)提供了兩種使用手機(jī)相機(jī)資源實(shí)現(xiàn)拍攝功能的方法,一種是直接通過I...
    TensorFlow開發(fā)者閱讀 3,034評論 0 14
  • 情景 開發(fā)過程中麦锯,經(jīng)常會遇到自定義開發(fā)相機(jī)恕稠。下面詳細(xì)介紹: Android中開發(fā)相機(jī)的兩種方式 Android系統(tǒng)...
    TensorFlow開發(fā)者閱讀 1,562評論 0 3
  • 上一篇介紹了如何使用系統(tǒng)相機(jī)簡單、快速的進(jìn)行拍照扶欣,本篇將介紹如何使用框架提供的API直接控制攝像機(jī)硬件鹅巍。 你還在為...
    Xiao_Mai閱讀 7,190評論 4 18
  • 2018.02.10 四川·成都·寬窄巷子 成都之行第1天第3站 寬巷子 窄巷子 井巷子 寬窄巷子,成都的一個地標(biāo)...
    舒涵vivian閱讀 1,443評論 0 1
  • 中午吃飯時 腦子里 突然蹦出一個問題 那是我想要的生活嗎料祠? 頓時 眼神迷離著看著窗外卻沒有聚焦點(diǎn) 上海定居 有個...
    小小太陽000閱讀 116評論 0 0