Android Camera - litonghui/TechBlog GitHub Wiki
在社交软件中,文字传送已近不能满足用户需求,以图片、语音、视频为主的流媒体传送开始主导社交,以Android 图片上传为例,实现从相册选照片和拍照传照片的dome。
===================
申请权限,在AndroidManifest 申明权限,包括获取照相机权限,文件(图片)存储读写权限。
<!-- 照相机权限 -->
<uses-permission android:name="android.permission.CAMERA" />
<!-- 用于读取手机当前的状态-->
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<!-- 写入扩展存储,向扩展卡写入数据,用于写入离线定位数据-->
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
<!-- 往SDCard写入数据权限 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
AlertDialog 弹窗选择:照相和相册
final AlertDialog dlg = new AlertDialog.Builder(this).create();
dlg.show();
Window window = dlg.getWindow();
// 设置窗口的内容页面,shrew_exit_dialog.xml文件中定义view内容
window.setContentView(R.layout.alertdialog);
TextView tv_paizhao = (TextView) window.findViewById(R.id.tv_content1);
tv_paizhao.setText("拍照");
TextView tv_xiangce = (TextView) window.findViewById(R.id.tv_content2);
tv_xiangce.setText("相册");
拍照功能
1. 判断照相机:
/**
* 判断系统是否可以启动相机
* @return true 表示有照相机,false 表示没有照相机
*/
public static boolean hasCamera(Activity activity) {
if (null != activity && !activity.isFinishing()) {
PackageManager packageManager = activity.getPackageManager();
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
List<ResolveInfo> infos = packageManager.queryIntentActivities(
intent, PackageManager.MATCH_DEFAULT_ONLY);
return infos != null ? infos.size() > 0 : false;
} else
return false;
}
2. 创建存储文件 和 以时间命名文件名
/**
* 创建照片存储绝对路径
* @param filePath 文件路径
* @param fileName 照片名字
* @return 照片绝对路径
*/
public static File createOrOpen(String filePath,String fileName) {
File dir = new File(filePath);
if (!dir.exists()) {
dir.mkdirs();
}
File destFile = new File(dir, fileName);
return destFile;
}
/**
* 获取当前时间
* @return
*/
@SuppressLint("SimpleDateFormat")
public static String getNowTime() {
Date date = new Date(System.currentTimeMillis());
SimpleDateFormat dateFormat = new SimpleDateFormat("MMddHHmmssSS");
return dateFormat.format(date);
}
3. 调起照相机
tv_paizhao.setOnClickListener(new View.OnClickListener() {
@SuppressLint("SdCardPath")
public void onClick(View v) {
if (Utils.hasCamera(mActivity)) {
mImageName = Utils.getNowTime() + ".png";
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
// 指定调用相机拍照后照片的储存路径
intent.putExtra(MediaStore.EXTRA_OUTPUT,
Uri.fromFile(Utils.createOrOpen(ImageAvatarPath, mImageName)));
startActivityForResult(intent, PHOTO_REQUEST_TAKEPHOTO);
} else {
Toast.makeText(mActivity, "No Find Camera !", Toast.LENGTH_SHORT).show();
}
dlg.cancel();
}
});
4. 回调处理数据
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode == RESULT_OK) {
switch (requestCode) {
case PHOTO_REQUEST_TAKEPHOTO:
startPhotoSave(Uri.fromFile(Utils.createOrOpen(ImageAvatarPath, mImageName)));
break;
default:
break;
}
}
super.onActivityResult(requestCode, resultCode, data);
}
/**
* 照片存储并显示
* @return
*/
private void startPhotoSave(Uri uri){
ContentResolver resolver = getContentResolver();
//照片的原始资源地址
try {
//使用ContentProvider通过URI获取原始图片
Bitmap photo = MediaStore.Images.Media.getBitmap(resolver, uri);
if (photo != null) {
Bitmap smallBitmap = Utils.zoomBitmap(photo, photo.getWidth() / SCALE, photo.getHeight() / SCALE);
photo.recycle(); //释放原始图片占用的内存,防止out of memory异常发生
Utils.save(smallBitmap, Bitmap.CompressFormat.PNG, 1, ImageAvatarPath,mImageName);
mImageView.setImageBitmap(smallBitmap);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
在图片存储时候用到两个方法:zoomBitmap(),save(),分析原因,目前流行照相机拍照产生原始照片相对较大,如果不做相应压缩,很容易造成OOM,所以需要对照片压缩。
/**
* 压缩图片
* @param bitmap 原始图片
* @param width 宽度
* @param height 高度
* @return 压缩结果
*/
public static Bitmap zoomBitmap(Bitmap bitmap, int width, int height) {
int w = bitmap.getWidth();
int h = bitmap.getHeight();
Matrix matrix = new Matrix();
float scaleWidth = ((float) width / w);
float scaleHeight = ((float) height / h);
matrix.postScale(scaleWidth, scaleHeight);
Bitmap newbmp = Bitmap.createBitmap(bitmap, 0, 0, w, h, matrix, true);
if (null != bitmap && !bitmap.isRecycled()) {
bitmap.recycle();
}
return newbmp;
}
public static String save(
Bitmap bitmap,
Bitmap.CompressFormat format,
int quality, File destFile) {
try {
FileOutputStream out = new FileOutputStream(destFile);
if (bitmap.compress(format, quality, out)) {
out.flush();
out.close();
}
return destFile.getAbsolutePath();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
相册选择
1 ,创建存储文件 和 以时间命名文件名 同上
2 ,调起相册选择图片
tv_xiangce.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
mImageName = Utils.getNowTime() + ".png";
Intent intent = new Intent(Intent.ACTION_PICK, null);
intent.setDataAndType(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*");
startActivityForResult(intent, PHOTO_REQUEST_GALLERY);
dlg.cancel();
}
});
3,接收图片选择回调
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode == RESULT_OK) {
switch (requestCode) {
case PHOTO_REQUEST_GALLERY:
if(data != null) {
startPhotoSave(data.getData());// 方法同上
}
break;
default:
break;
}
}
super.onActivityResult(requestCode, resultCode, data);
}
通过上面方法可以完成简单的图片选择功能,但是细节优化还值得注意,比如,通过照相和相册选择图片之后,可以对图片做裁剪;因为调用照相机相当于启动新的Activity,个别手会出现销毁栈顶Activity,也就意味着照片选择完成之后无法预览显示,需要借助onSaveInstanceState、onRestoreInstanceState 方法做保存。
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
if (mImageName != null) {
outState.putSerializable(EXTRA_RESTORE_PHOTO, mImageName);
}
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
mImageName = (String) savedInstanceState.getSerializable(EXTRA_RESTORE_PHOTO);
}
如果想测试不用上门两个方法是否可以造成影响,可以在如图一,选择不保留当前活动,测试是否显示正常。
