該文章 主要使用 自定義 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 的過程
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)行即可