序言
由于前段時(shí)間在準(zhǔn)備跳槽琼梆,所以一直沒有更新。不過窿吩,從這個(gè)月開始,我會(huì)繼續(xù)開始記錄自己在android開發(fā)中遇到的一些坑错览,或?qū)懸恍┍容^有意思的文章纫雁。希望大家繼續(xù)關(guān)注。好了倾哺,開始切入正題轧邪。
概述
這段時(shí)間開始接觸到Camera相關(guān)的東西,所以就打算自己寫一個(gè)小demo來熟悉一下流程和要點(diǎn)羞海。當(dāng)然忌愚,本文使用SurfaceView來實(shí)現(xiàn)一個(gè)Camera,同時(shí)適配6.0權(quán)限(開始沒6.0動(dòng)態(tài)權(quán)限却邓,后來因?yàn)樯磉吅芏喽际?.0硕糊,所以簡(jiǎn)單的做了一下6.0權(quán)限),以及sd卡的讀寫,圖片顯示不全等一些相關(guān)的知識(shí)點(diǎn)简十。
相關(guān)知識(shí)的介紹
SurfaceView :使用場(chǎng)景界面迅速更新對(duì)幀率要求較高的情況檬某。SurfaceView繼承 View,SurfaceView和View最本質(zhì)的區(qū)別在于螟蝙,SurfaceView是在一個(gè)新起的單獨(dú)線程中可以重新繪制畫面恢恼,而View必須在UI的主線程中更新畫面。因本文主要講的是怎么使用胰默,所以詳細(xì)介紹可以看SurfaceView或者Google查看场斑。
RxPermissions Github地址:本文使用了原生的6.0權(quán)限請(qǐng)求和RxPermissions。RxPermissions是一個(gè)6.0動(dòng)態(tài)權(quán)限管理的一個(gè)library庫(kù)牵署,它的使用需要結(jié)合Rxjava一起漏隐,因?yàn)镽xPermissions返回的是一個(gè)Observable,所以如果不準(zhǔn)備使用Rxjava碟刺,可以去嘗試一下其他的library锁保。可以參考一下弘洋的6.0權(quán)限管理
還有讀寫文件的基本使用方法以及一些圖片的簡(jiǎn)單處理
實(shí)現(xiàn)
一 :主要邏輯在MainActivity半沽,在onCreate的時(shí)候申請(qǐng)權(quán)限處理爽柒,在onResume的時(shí)候startPreview
開啟預(yù)覽,在onPause的時(shí)候releasePreview關(guān)閉預(yù)覽并釋放Camera(由于一直持有會(huì)出現(xiàn)oom)者填。
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
frameLayout = (FrameLayout) findViewById(R.id.activity_main);
btn_capture = (ImageView) findViewById(R.id.btn_capture);
btn_capture.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
capture();
}
});
/**
* 使用系統(tǒng)API請(qǐng)求相機(jī)權(quán)限
*/
if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
isCamera = false;
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA}, MY_PERMISSIONS_REQUEST_CAMERA);
} else {
isCamera = true;
initCamera();
initDefult();
}
}
/**
* 獲得Camera浩村,開啟預(yù)覽
*
*/
@Override
protected void onResume() {
super.onResume();
if (isCamera == false) return;
if (mCamera == null) {
mCamera = getCamera();
if (sHolder != null) {
setStartPreview(mCamera, sHolder);
}
}
}
/**
* 停止預(yù)覽,銷毀Camera
*/
@Override
protected void onPause() {
super.onPause();
releasePreview();
}
二:initCamera()中主要是初始化Camera和SurfaceView占哟,并且獲得SurfaceHolder心墅,然后SurfaceHolder添加回調(diào),并調(diào)用setStartPreview榨乎,開啟預(yù)覽怎燥。
/**
* 初始化Camera相關(guān)
*/
private void initCamera() {
mCamera = getCamera();
surface_camera = (SurfaceView) findViewById(R.id.surface_camera);
frameLayout.bringChildToFront(surface_camera);
frameLayout.bringChildToFront(btn_capture);
sHolder = surface_camera.getHolder();
sHolder.addCallback(this);
surface_camera.setOnClickListener(this);
setStartPreview(mCamera,sHolder); //由于APP在第一次安裝時(shí),onResume不會(huì)執(zhí)行蜜暑,所以重新獲得cemera權(quán)限以后重新start
}
注:大家會(huì)看到铐姚,在onCeate和onResume都調(diào)用了 mCamera = getCamera(),原因是在于肛捍,當(dāng)app第一次安裝時(shí)隐绵,系統(tǒng)會(huì)依次執(zhí)行Activity的生命周期,如果只在onResume中調(diào)用拙毫,會(huì)發(fā)現(xiàn)并沒有使用相機(jī)依许。原因是在權(quán)限申請(qǐng)時(shí),是另起了一個(gè)線程缀蹄,所以獲得Camera權(quán)限后峭跳,onResume已經(jīng)執(zhí)行完成膘婶。因此添加isCamera字段,來標(biāo)記是否已經(jīng)獲取權(quán)限坦康,同時(shí)在取得權(quán)限后竣付,調(diào)用了 mCamera = getCamera()。
三:當(dāng)添加了SurfaceHolder回調(diào)后滞欠,會(huì)重寫三個(gè)方法:surfaceCreated()古胆,surfaceChanged(),surfaceDestroyed()筛璧。分別是創(chuàng)建逸绎,變化和銷毀。
@Override
public void surfaceCreated(SurfaceHolder holder) {
setStartPreview(mCamera, sHolder);
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
mCamera.stopPreview();
setStartPreview(mCamera, sHolder);
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
releasePreview();
}
四:接下來看最重要的setStartPreview()和 releasePreview()夭谤。這兩個(gè)方法中棺牧,setStartPreview中主要是做一下初始化Preview的分辨率,調(diào)整一下預(yù)覽的成像角度朗儒。releasePreview中主要是給setPreviewCallback置null颊乘,停止預(yù)覽并釋放Camera。
/**
* 開啟Camera預(yù)覽
*/
private void setStartPreview(Camera camera, SurfaceHolder holder) {
try {
Camera.Parameters parameters = camera.getParameters();
List<Camera.Size> size2 = parameters.getSupportedPreviewSizes(); //得到手機(jī)支持的預(yù)覽分辨率
parameters.setPreviewSize(size2.get(0).width,size2.get(0).height);
camera.setPreviewDisplay(holder);//綁定holder
camera.setDisplayOrientation(getPreviewDegree(MainActivity.this));//將系統(tǒng)Camera角度進(jìn)行調(diào)整
camera.startPreview();//開啟預(yù)覽
camera.setParameters(parameters);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 釋放Camera
*/
private void releasePreview() {
if (mCamera == null) return;
mCamera.setPreviewCallback(null);
mCamera.stopPreview();//停止預(yù)覽
mCamera.release();
mCamera = null;
}
五:拍照和點(diǎn)擊屏幕實(shí)現(xiàn)對(duì)焦醉锄。點(diǎn)擊拍照前乏悄,會(huì)設(shè)置一下Picture相關(guān)的參數(shù)。當(dāng)onAutoFocus返回true時(shí)恳不,說明對(duì)焦成功檩小,然后調(diào)用Camera的takePicture實(shí)現(xiàn)拍照。
Camera.Parameters parameters = mCamera.getParameters();
List<Camera.Size> supportedPictureSizes = parameters.getSupportedPictureSizes();
parameters.setPictureFormat(ImageFormat.JPEG);//設(shè)置圖片樣式
parameters.setPictureSize(supportedPictureSizes.get(0).width, supportedPictureSizes.get(0).height);//設(shè)置圖片大小
parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);//自動(dòng)對(duì)焦
mCamera.setParameters(parameters);
mCamera.autoFocus(new Camera.AutoFocusCallback() {
@Override
public void onAutoFocus(boolean success, Camera camera) {
if (success) {
mCamera.takePicture(null, null, pictureCallback);
}
}
});
六:拍照成功后烟勋,使用RxPermissions申請(qǐng)寫入sd權(quán)限规求。然后完成跳轉(zhuǎn)到預(yù)覽界面。其中返回的data是一個(gè)拍照完成后卵惦,沒有壓縮過完整的圖片byte[]阻肿。
//保存圖片
String absolutePath = FileUtil.createIfNotExist(path);
FileUtil.writeBytes(path, data);
Intent intent = new Intent(MainActivity.this,ImageActivity.class);
intent.putExtra("path",absolutePath);
startActivity(intent);
總結(jié)
由于本人原來并沒有涉及到相關(guān)模塊,但是在剛接觸的時(shí)候沮尿,感覺挺簡(jiǎn)單冕茅,就是按部就班的實(shí)現(xiàn)一些方法和生命周期,但是當(dāng)一步步做下來的時(shí)候蛹找,發(fā)現(xiàn)其中涉及到的細(xì)節(jié)還是挺多。比如:
1.在我要設(shè)置setPreviewSize和setPictureSize時(shí)哨坪,我發(fā)現(xiàn)很容易導(dǎo)致程序崩潰庸疾,所以調(diào)用getSupportedPictureSizes,獲取當(dāng)前支持的各種分辨率当编,然后使用最高的分辨來設(shè)置届慈。
2.由于本人沒有6.0以上的測(cè)試機(jī),所以很多問題難以定位。在添加6.0權(quán)限后金顿,發(fā)現(xiàn)原有的邏輯需要重新思考臊泌,所以花費(fèi)了一些時(shí)間和精力。
3.是大家經(jīng)常會(huì)遇到的圖片翻轉(zhuǎn)或者角度問題揍拆。
4.由于安卓機(jī)型實(shí)在太多渠概,所以還要考慮多種屏幕下的顯示和預(yù)覽問題。
源碼
源碼下載地址源碼中注釋寫的很清楚嫂拴,本文只是把關(guān)鍵代碼貼出來播揪,如有需要,歡迎大家下載筒狠。
如果大家在學(xué)習(xí)時(shí)有問題猪狈,歡迎大家隨時(shí)聯(lián)系或者留言,我看見后會(huì)第一時(shí)間回復(fù)并解決辩恼。最后雇庙,愿大家在小長(zhǎng)假中玩得開心,祝愿你我gaygayup灶伊,在編碼的路上堅(jiān)挺下去疆前。