Camera的使用

简介

  Android的framework支持通过android.hardware.camera2API或者是过时的Camera来拍摄图片和视频。这篇文章讲解的是Camera的使用,后续会写一篇关于camera2使用的文章。当然,如果你等不及的话可以去看看google博客关于camera2的文章,camera2文章地址,请翻墙查看

基础

  1. android.hardware.camera2,这个包是控制设备相机的主要API,它可以被用来拍摄图片或者视频当你想构建一个相机app的时候;
  2. Camera,这个类也是用来控制设备相机的,但是在API21之后过时了;
  3. SurfaceView,这个类用来给用户呈现相机的预览;
  4. MediaRecorder,这个类通过相机来录制视频;
  5. Intent,如果你的目的仅仅只是简单的拍摄一张图片或者一段视频,你完全可以调用系统的相机让它来完成这些事情,而不需要直接操控系统相机。你可以使用MediaStore.ACTION_IMAGE_CAPTURE来调用系统相机来拍摄图片,或者通过MediaStore.ACTION_VIDEO_CAPTURE来调用系统相机来拍摄视频。

Manifest声明

  当你使用Camera API进行开发的时候,你必须确保你的manifest文件中声明了相关的东西。

  1. Camera Permission,当app需要使用设备相机的时候必须声明这个权限

    1
    <uses-permission android:name="android.permission.CAMERA" />
  2. Camera Features,同时得声明使用相机相关的特性,比如。关于camera features的列表,可以查看Features Reference。添加camera features到manifest文件中会导致Google Play防止你的应用安装在那些没有相机或者是不支持相机特性的设备上。如果你的app并不是一定需要camera,你可以在manifest中通过 android:required 来指定。

    1
    <uses-permission android:name="android.hardware.camera" android:required="false" />
  3. Storage Permission,如果应用程序需要将图片或者视频保存到external storage中,还需要声明这个权限;

    1
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
  4. Audio Recording Permission,视频录制的过程中还需要录制音频,所以得添加音频录制的权限,音频录制在之前的文章中已经讲过,不懂的可以去 Android录制系列之音频录制进行查阅;

    1
    <uses-permission android:name="android.permission.RECORD_AUDIO" />
  5. Location Permission,如果app需要给图片打上GPS定位信息的tag,你需要申请 ACCESS_FINE_LOCATION 权限,这里需要注意,Android5.0及以上版本需要声明使用设备的GPS;

    1
    2
    3
    <uses-permission android:name ="android.permission.ACCESS_FINE_LOCATION" />
    <!-- targetSdkVersion >= 21 的需要添加 -->
    <uses-feature android:name="android.hardware.location.gps" />

如何使用

访问相机

  检索并访问相机,如果你的app没有在manifest文件中声明是否只能安装在有相机的设备上,那么在使用之前,请检测设备是否有相机的硬件支持。

1
fun hasFeatureCamera(context: Context) = context.packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA)

  Android2.3 之后你可以直接通过 Camera.getNumberOfCameras() 方法直接获取到设备上可用的相机数量。如果你非常清楚你app运行在有相机支持的设备上运行时,你可以直接通过 Camera.open() 方法去请求并获取一个Camera的实例,它会 访问设备上的第一个后置摄像头。

1
2
3
4
5
6
7
8
9
10
fun getCamera(): Camera? {
var camera: Camera? = null
try {
camera = Camera.open()
} catch (e: Exception) {
//如果不捕获异常,相机在被其他的app使用,或者设备上根本没有相机的时候,就会导致app crash
Log.d("Amoryan", "open camera error!")
}
return camera
}

  Android 2.3 之后你可以通过 Camera.open(int) 访问指定的相机。比如,你可以这么玩。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
fun getCameraInfo() {
try {
for (i in 0 until Camera.getNumberOfCameras()) {
val cameraInfo: Camera.CameraInfo = Camera.CameraInfo()
Camera.getCameraInfo(i, cameraInfo)
if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_BACK) {
Log.d("Amoryan", "$i is back facing,orientation is ${cameraInfo.orientation}")
} else {
Log.d("Amoryan", "$i is front facing,orientation is ${cameraInfo.orientation}")
}
}
} catch (e: Exception) {
Log.d("Amoryan", "open camera error!")
}
}

设置预览

  设置预览界面,你可以直接使用SurfaceView,或写一个SurfaceView的子类,然后实现SurfaceHolder.Callback的相关回调,这里我为了方便起见,直接在布局文件中使用SurfaceView了

1
2
3
4
5
6
7
8
<SurfaceView
android:id="@+id/surfaceView"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"/>

  然后实现相对应的回调接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class MainActivity : AppCompatActivity(), SurfaceHolder.Callback {

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

surfaceView.holder.addCallback(this)
}

override fun surfaceChanged(holder: SurfaceHolder?, format: Int, width: Int, height: Int) {
}

override fun surfaceDestroyed(holder: SurfaceHolder?) {
}

override fun surfaceCreated(holder: SurfaceHolder?) {
}

}

拍摄图片

  通过 takePicture 方法拍摄图片,这里只是简单的将拍摄的图片显示在ImageView上了。

1
2
3
4
5
6
7
8
9
10
11
captureTv.setOnClickListener { camera?.safeTakePicture(null, null, pictureCallback) }

private val pictureCallback = Camera.PictureCallback { data, camera ->
data?.apply {
releaseBitmap()
bitmap = BitmapFactory.decodeByteArray(this, 0, this.size)
previewImage.setImageBitmap(bitmap)
camera?.safeStopPreview()
setupCamera()
}
}

释放Camera

  Camera是设备上的共享资源,当app获取到Camera的实例后就可以使用它,但是在app不需要Camera或者当界面变得不可见的时候应该正确的释放。举个栗子,当界面变得不可见的时候,如果你没有正确的释放掉资源,就会导致接下来想要访问Camera的app获取失败,而没办法正常工作。

1
2
3
4
5
override fun onPause() {
super.onPause()
camera?.safeRelease()
camera = null
}

完整代码

  github项目地址

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
package com.yanfangxiong.camerademo

import android.Manifest
import android.content.pm.PackageManager
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.hardware.Camera
import android.os.Bundle
import android.support.v4.app.ActivityCompat
import android.support.v7.app.AppCompatActivity
import android.view.SurfaceHolder
import kotlinx.android.synthetic.main.activity_main.*

class MainActivity : AppCompatActivity(), SurfaceHolder.Callback {

companion object {

val REQUEST_CAMERA_PERMISSION = 1

}

private var camera: Camera? = null
private var bitmap: Bitmap? = null
private val pictureCallback = Camera.PictureCallback { data, camera ->
data?.apply {
releaseBitmap()
bitmap = BitmapFactory.decodeByteArray(this, 0, this.size)
previewImage.setImageBitmap(bitmap)
camera?.safeStopPreview()
setupCamera()
}
}

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

surfaceView.holder.addCallback(this)
captureTv.setOnClickListener { camera?.safeTakePicture(null, null, pictureCallback) }
}

private fun checkCameraPermission() {
val cameraPermissionStatus = ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA)
if (cameraPermissionStatus == PackageManager.PERMISSION_GRANTED) {
openCamera()
} else {
ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.CAMERA), REQUEST_CAMERA_PERMISSION)
}
}

override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (requestCode == REQUEST_CAMERA_PERMISSION && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
openCamera()
}
}

private fun openCamera() {
camera = safeOpenCamera()
setupCamera()
}

private fun setupCamera() {
camera?.apply {
setDisplayOrientation(90)
setPreviewDisplay(surfaceView.holder)
safeStartPreview()
}
}

override fun surfaceCreated(holder: SurfaceHolder?) {
if (camera == null) {
checkCameraPermission()
} else {
setupCamera()
}
}

override fun surfaceChanged(holder: SurfaceHolder?, format: Int, width: Int, height: Int) {
camera?.safeStopPreview()
setupCamera()
}

override fun surfaceDestroyed(holder: SurfaceHolder?) {
}

override fun onPause() {
super.onPause()
camera?.safeRelease()
camera = null
}

private fun releaseBitmap() {
bitmap?.recycle()
bitmap = null
}

}

毒鸡汤

  人生不如意的时候是上帝给的长假。

本文标题:Camera的使用

文章作者:严方雄

发布时间:2018-01-10

最后更新:2018-09-13

原始链接:http://yanfangxiong.com/2018/01/10/Camera的使用/

0%