相机已成为android手机不可或缺的一部分,我们在开发可能也会涉及到关于相机的使用,以及在使用中会遇到一些问题(重点),下面把我个人的一点经验贴出来,仅供大家参考学习。
最简单的方式直接调用系统相机进行拍照,虽然能满足拍照的要求,但拍照的功能和界面却局限系统相机的样子,于是我们这时根据Camera API实现自己的拍照和摄像程序,当然这并不是我想说重点,今天的重点在于开发中不同型号手机调用系统相机拍完照出现闪退的问题
一、调用安卓系统相机拍照
首先应该添加权限:
1
2
3
|
<uses-permission android:name=
"android.permission.CAMERA"
/>
<uses-feature android:name=
"android.hardware.camera"
/>
<uses-feature android:name=
"android.hardware.camera.autofocus"
/>
|
通过Intent 打开系统相机
1
2
3
4
5
6
7
8
9
10
|
openbButton.setOnClickListener(
new
OnClickListener() {
@Override
public
void
onClick(View v) {
// 使用意图直接调用安装在手机上的照相机
Intent intent =
new
Intent(
android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
// 打开照相机,设置请求码
startActivityForResult(intent, OPEN_RESULT);
}
});
|
在onActivityResult中,通过相机返回意图intent.get("data") 取得拍照的图片
1
2
3
4
5
6
7
8
9
|
@Override
protected
void
onActivityResult(
int
requestCode,
int
resultCode, Intent data) {
if
(requestCode == OPEN_RESULT) {
if
(resultCode == RESULT_OK) {
Bundle bundle = data.getExtras();
Bitmap bitmap = (Bitmap) bundle.get(
"data"
);
imageView.setImageBitmap(bitmap);
}
}
|
二.系统相机拍照方式---解决系统相机拍照闪退
上面的代码,实现了调用系统相机,但是经过我在测试数10台手机之后发现,红米和三星出现拍完照后返回时,黑屏闪退。测试结果说明,该方法只试用于部分手机。
观察错误信息,
提示
onActivityResult 中空指针异常,相机拍照返回的 Intent data这个参数为空无法取出刚刚拍照的Bitmap,研究了一下,应该是不同手机厂商在系统定制时,隐藏了拍照返回意图。厂商系统定制我们不能修改,只能采取另外的方式来保存照片。针对返回意图为空的问题,解决办法如下:
通过把照片保存在指定路径,完成后通过路径取出
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
takePhoto2.setOnClickListener(
new
OnClickListener() {
@Override
public
void
onClick(View v) {
// 调用系统相机
Intent intent =
new
Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intent.addCategory(Intent.CATEGORY_DEFAULT);
// 取当前时间为照片名
mPictureFile = DateFormat.format(
"yyyyMMdd_hhmmss"
,
Calendar.getInstance(Locale.CHINA))
+
".jpg"
;
Log.d(
"onactivity"
,
"mPictureFile:"
+ mPictureFile);
filePath = getPhotoPath() + mPictureFile;
// 通过文件创建一个uri中
Uri imageUri = Uri.fromFile(
new
File(filePath));
// 保存uri对应的照片于指定路径
intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
startActivityForResult(intent, OPEN_RESULT2);
}
});
|
拍照完成返回时,在onActivityResult中,通过指定文件的绝对路径,获得拍照的图片
1
2
3
4
5
6
7
8
|
if
(requestCode == OPEN_RESULT2) {
if
(resultCode == RESULT_OK) {
Log.e(
"takePhoto"
, filePath);
Bitmap bitmap = BitmapFactory.decodeFile(filePath);
// imageView.setImageURI(Uri.fromFile(new File(filePath)));
imageView.setImageBitmap(bitmap);
}
}
|
三、使用自定义路径拍照闪退问题
如果使用方法二拍照依然部分手机出现闪退的情况,观察打印的错误信息,依然NullPointerException,这个问题相对来说也比较简单,但第一次遇到也把我困住了一会,异常信息我就不贴出来了,错误的原因是这样的:
当我们 通过 startActivityForResult(intent, OPEN_RESULT);
打开系统相机时,部门手机由于系统优化的原因,会把当前页面的成员变量回收,当拍完照片返回时,在onActivityResult方法中是要通过照片路径来获得Bitmap,而这种情况下保存照片路径的mPictureFile指向的字符串已经被回收,mPictureFile值就为空,导致产生空指针异常而闪退。解决办法是在Activity中加上onSaveInstanceState生命周期方法,保证对象在被系统回收之前能够先以hashMap的形式保存下来,当返回时再从onRestoreInstanceState方法中恢复出来
下面把整个应用程序代码写出来,结合Activity的生命周期方法,可以更好的观察理解这个问题:
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
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
|
package
cn.keno.takephoto;
import
java.io.File;
import
java.util.Calendar;
import
java.util.Locale;
import
android.app.Activity;
import
android.content.Intent;
import
android.graphics.Bitmap;
import
android.graphics.BitmapFactory;
import
android.net.Uri;
import
android.os.Bundle;
import
android.os.Environment;
import
android.provider.MediaStore;
import
android.text.TextUtils;
import
android.text.format.DateFormat;
import
android.util.Log;
import
android.view.View;
import
android.view.View.OnClickListener;
import
android.widget.Button;
import
android.widget.ImageView;
public
class
MainActivity
extends
Activity {
private
final
String TAG =
"TakePhoto"
;
private
Button openbButton;
private
Button takePhoto2;
// 拍照2
private
Button pickButton;
private
ImageView imageView;
private
String mPictureFile;
private
String filePath;
private
final
int
OPEN_RESULT =
1
;
// 打开相机
private
final
int
PICK_RESULT =
2
;
// 查看相册
private
final
int
OPEN_RESULT2 =
3
;
// 打开相机2
@Override
protected
void
onCreate(Bundle savedInstanceState) {
super
.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.i(TAG,
"onCreate"
);
openbButton = (Button) findViewById(R.id.btnTakePhoto);
takePhoto2 = (Button) findViewById(R.id.btnTakePhoto2);
pickButton = (Button) findViewById(R.id.btnPick);
imageView = (ImageView) findViewById(R.id.imgPotho);
openbButton.setOnClickListener(
new
OnClickListener() {
@Override
public
void
onClick(View v) {
// 使用意图直接调用安装在手机上的照相机
Intent intent =
new
Intent(
android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
// 打开照相机,设置请求码
startActivityForResult(intent, OPEN_RESULT);
}
});
takePhoto2.setOnClickListener(
new
OnClickListener() {
@Override
public
void
onClick(View v) {
// 调用系统相机
Intent intent =
new
Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intent.addCategory(Intent.CATEGORY_DEFAULT);
// 取当前时间为照片名
mPictureFile = DateFormat.format(
"yyyyMMdd_hhmmss"
,
Calendar.getInstance(Locale.CHINA))
+
".jpg"
;
Log.d(
"onactivity"
,
"mPictureFile:"
+ mPictureFile);
filePath = getPhotoPath() + mPictureFile;
// 通过文件创建一个uri中
Uri imageUri = Uri.fromFile(
new
File(filePath));
// 保存uri对应的照片于指定路径
intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
startActivityForResult(intent, OPEN_RESULT2);
}
});
pickButton.setOnClickListener(
new
OnClickListener() {
@Override
public
void
onClick(View v) {
// 使用意图直接调用手机相册
Intent intent =
new
Intent(
Intent.ACTION_PICK,
android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
// 打开手机相册,设置请求码
startActivityForResult(intent, PICK_RESULT);
}
});
}
@Override
protected
void
onSaveInstanceState(Bundle outState) {
super
.onSaveInstanceState(outState);
outState.putString(
"filePath"
, filePath);
Log.d(TAG,
"onSaveInstanceState"
);
}
@Override
protected
void
onRestoreInstanceState(Bundle savedInstanceState) {
super
.onRestoreInstanceState(savedInstanceState);
if
(TextUtils.isEmpty(filePath)) {
filePath = savedInstanceState.getString(
"filePath"
);
}
Log.d(TAG,
"onRestoreInstanceState"
);
}
@Override
protected
void
onStart() {
super
.onStart();
Log.i(TAG,
"onStart"
);
}
@Override
protected
void
onRestart() {
super
.onRestart();
Log.i(TAG,
"onRestart"
);
}
@Override
protected
void
onResume() {
super
.onResume();
Log.i(TAG,
"onResume"
);
}
@Override
protected
void
onPause() {
super
.onPause();
Log.i(TAG,
"onPause"
);
}
@Override
protected
void
onStop() {
super
.onStop();
Log.i(TAG,
"onStop"
);
}
@Override
protected
void
onDestroy() {
super
.onDestroy();
Log.i(TAG,
"onDestroy"
);
}
@Override
protected
void
onActivityResult(
int
requestCode,
int
resultCode, Intent data) {
if
(requestCode == OPEN_RESULT) {
if
(resultCode == RESULT_OK) {
Bundle bundle = data.getExtras();
Bitmap bitmap = (Bitmap) bundle.get(
"data"
);
imageView.setImageBitmap(bitmap);
}
}
else
if
(requestCode == PICK_RESULT) {
// 表示选择图片库的图片结果
if
(resultCode == RESULT_OK) {
Uri uri = data.getData();
imageView.setImageURI(uri);
}
}
else
if
(requestCode == OPEN_RESULT2) {
if
(resultCode == RESULT_OK) {
Log.e(
"takePhoto"
, filePath);
Bitmap bitmap = BitmapFactory.decodeFile(filePath);
// imageView.setImageURI(Uri.fromFile(new File(filePath)));
imageView.setImageBitmap(bitmap);
}
}
}
/**
* 获得照片路径
*
* @return
*/
private
String getPhotoPath() {
return
Environment.getExternalStorageDirectory() +
"/DCIM/"
;
}
}
|