相機:Camera
相機現在已經不僅僅是手機必備神器了,甚至相機的拍照質量已經是很多人買手機的首選條件了。而對于相機而言主要有兩大功能:拍照片和拍視頻。Android 為此兩種方式:
- 相機 intent
- 相機 API
本節我們就一起來看看相機的具體用法。
1. 打開 Camera 的兩大方式
目前市面上絕大多數的 Android 手機是有前后兩個攝像頭,當然有部分特殊機型會存在其他的情況,本節主要針對雙攝像頭設備做解析。
在前面有提到過,通常使用相機有兩大方式:“Intent”和“API”。最大的差別就是“Intent”是跳轉到系統提供的相機頁面,而使用“API”是封閉在自己的 App 中使用相機。我們可以通過“Intent”直接打開系統提供的相機 Activity,在用戶拍攝完成之后系統 Activity 會通知我們拍攝結果,然后拿到拍攝的圖片或者視頻。而直接通過 API 就需要我們去開發一整套相機的控制頁面,自行完成照片 / 視頻的拍攝進而直接拿到用戶拍攝的結果。
2. Camera 的基本用法
我們分別看看這兩種打開方式的使用方法:
2.1 使用 Intent 打開
通過使用 MediaStore 類提供的兩個 Intnet 常量,可以直接將相機操作托管給 Android 系統而無需創建 Camera 實例:
- ACTION_IMAGE_CAPTURE:
拍攝照片 - ACTION_VIDEO_CAPTURE:
拍攝視頻
2.2 使用 API 打開
采用 API 打開會讓整個相機程序都封閉在自己的 APP 中完成,這里需要明確幾個概念:
-
Camera 類
使用 API 之前需要創建 Camera 實例,然后通過 API 來初始化 Camera 進而拿到實時拍攝的畫面 -
SurfaceView
用來渲染視頻實時畫面的 View
采用 Intent 打開相機的方法非常簡單,接下來我們全程使用 Camera API 來實現一下相機功能。
3. Camera 使用示例
使用 API 來拍照會相對比較麻煩一點,首先需要獲取權限,那么對于 Android 6.0 版本以上的系統除了要在 Manifest 里面加入 Camera 權限之外,還需要動態獲取權限,這個可能大家在用一些比較老的教程示例時會踩坑。
獲取到權限就可以打開 Camera 了,然后拿到 Camera 實例設置一個 SurfaceView 來渲染預覽頁面,在用戶需要拍照的時候調用 Camera 的takePicture()
方法獲取當前幀,最后保存輸出到文件中或者在新的 Activity 中展示,這樣就算完成了一次拍照閉環。
3.1 Camera 拍照邏輯
首先看看整個拍照的代碼如下:
package com.emercy.myapplication;
import android.Manifest;
import android.app.Activity;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.hardware.Camera;
import android.os.Build;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
public class MainActivity extends Activity {
public static final String CAMERA_PATH = "path";
public static final String CAMERA_IMG = "img";
private SurfaceView mSurfaceView;
private Button mTakePhoto;
private Camera mCamera = null;
private SurfaceHolder.Callback mCallback = new SurfaceHolder.Callback() {
@Override
public void surfaceCreated(SurfaceHolder holder) {
startPreview();
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
stopPreview();
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getPermission();
bindViews();
}
/**
* 獲取權限
*/
private void getPermission() {
if (Build.VERSION.SDK_INT > 22) {
if (checkSelfPermission(android.Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
//先判斷有沒有權限 ,沒有就在這里進行權限的申請
requestPermissions(new String[]{Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE}, 100);
} else {
//說明已經獲取到攝像頭權限了
Log.i("Imooc Camera", "已經獲取了權限");
}
} else {
//這個說明系統版本在6.0之下,不需要動態獲取權限。
Log.i("Imooc Camera", "這個說明系統版本在6.0之下,不需要動態獲取權限。");
}
}
private void bindViews() {
mSurfaceView = (SurfaceView) findViewById(R.id.sfv_preview);
mTakePhoto = (Button) findViewById(R.id.btn_take);
mSurfaceView.getHolder().addCallback(mCallback);
mTakePhoto.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mCamera.takePicture(null, null, new Camera.PictureCallback() {
@Override
public void onPictureTaken(byte[] data, Camera camera) {
String path;
if (TextUtils.isEmpty(path = savePhoto(data))) {
Intent it = new Intent(MainActivity.this, PreviewActivity.class);
it.putExtra(CAMERA_PATH, path);
startActivity(it);
} else {
Toast.makeText(MainActivity.this, "拍照失敗", Toast.LENGTH_SHORT).show();
}
}
});
}
});
}
private String savePhoto(byte[] bytes) {
try {
File file = File.createTempFile(CAMERA_IMG, "");
FileOutputStream fos = new FileOutputStream(file);
fos.write(bytes);
fos.flush();
fos.close();
return file.getAbsolutePath();
} catch (IOException e) {
e.printStackTrace();
}
return "";
}
private void startPreview() {
mCamera = Camera.open();
try {
mCamera.setPreviewDisplay(mSurfaceView.getHolder());
mCamera.setDisplayOrientation(90); // 讓相機旋轉90度
mCamera.startPreview();
} catch (IOException e) {
e.printStackTrace();
}
}
private void stopPreview() {
mCamera.stopPreview();
mCamera.release();
mCamera = null;
}
}
里面涉及到的幾個新概念大家需要注意,然后記得在SurfaceHolder.Callback()
的surfaceCreated()
中啟動預覽,在surfaceDestroyed()
方法中要停止預覽,其他的基本上按照步驟來不會有什么問題。
3.2 相機布局
目前市面上有很多相機 App,各種布局千變萬化,大家完全可以按照自己的喜好來進行設計。這里只做一個拍照預覽和拍照按鈕。
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width"match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<SurfaceView
android:id="@+id/sfv_preview"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<Button
android:id="@+id/btn_take"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|center"
android:text="拍照" />
</FrameLayout>
3.3 照片回看頁面
點擊“拍照” Button 獲取到當前畫面之后,我們可以就可以按照自己的邏輯對圖片進行操作了。比如可以通過 Http 上傳、通過 Socket 發送給其他設備、保存到本地、或者傳遞給其他 App 等。本例子中將圖片傳遞給另一個 Activity 專門用于查看圖片,下面是 PhotoActivity 的代碼:
package com.emercy.myapplication;
import android.app.Activity;
import android.net.Uri;
import android.os.Bundle;
import android.widget.ImageView;
import java.io.File;
import static com.emercy.myapplication.MainActivity.CAMERA_PATH;
public class PhotoActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ImageView img = new ImageView(this);
String path = getIntent().getStringExtra(CAMERA_PATH);
if (path != null) {
img.setImageURI(Uri.fromFile(new File(path)));
}
setContentView(img);
}
}
通過 Intent 接收圖片路徑,然后展示在 ImageView 上,需要注意的是PhotoActivity 中是直接將 ImageView 作為參數直接設置給了 SetContentView(),這樣相當于布局文件中只有一個 ImageView 的寫法。
最后別忘了在 Manifest 當中添加權限:
<uses-permission android:name="android.permission.CAMERA"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
編譯運行效果如下:
點擊拍照進入 PhotoActivity,展示的就是我們拍照的畫面。
4. 小結
本節學習了一個手機上必不可少的設備,通過相機我們可以拍攝照片或者視頻,Android 系統提供了兩大打開方式:Intent 調用系統相機 Activitiy或者用 Camera API 自行實現拍照功能。第一種非常簡單,將所有的相機操作都托管給系統,我們只關心最終用戶拍攝的結果;而第二種就需要我們自己初始化 Camera 對象從而實現拍攝,在實際開發中如果你的功能是和拍攝強相關,需要一些定制化的拍攝體驗,那么一定要使用第二種方式來自己實現 Camera,但是如果你只是一個簡單的拍照獲取圖片,那么第一種會讓你事半功倍。