实现设置头像功能的具体步骤
- 创建实现功能的视图界面
- 设置权限(android 6.0以上需要动态申请)
- 选择拍照或者相册选择图片
- 选择图片后的进行裁剪(看需求)
- 裁剪完后对结果进行处理(设置到界面、上传到服务器等)
最后会贴上整个测试类的代码
1、创建实现功能的视图界面
类似如下所示,具体内容根据项目需求去实现
2、设置权限(6.0以上需要动态申请)
首先在AndroidManifest.xml中添加以下权限
6.0以上系统需要动态权限适配
这里用的是RxPermissions来进行权限申请
//检查有无相机使用权限,没有的话动态申请
if (ActivityCompat.checkSelfPermission(TestActivity.this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
new RxPermissions(TestActivity.this).requestEach(Manifest.permission.CAMERA).subscribe(new Consumer() {
@Override
public void accept(Permission permission) throws Exception {
if (permission.granted) {
// 用户已经同意该权限
LogUtil.e(TAG, permission.name + " is granted.");
takePhoto();
} else if (permission.shouldShowRequestPermissionRationale) {
// 用户拒绝了该权限,没有选中『不再询问』(Never ask again),那么下次再次启动时,还会提示请求权限的对话框
LogUtil.e(TAG, permission.name + " is denied. More info should be provided.");
Toast.makeText(TestActivity.this, "未能授予权限,部分功能可能不能正常使用", Toast.LENGTH_SHORT).show();
} else {
// 用户拒绝了该权限,并且选中『不再询问』
LogUtil.e(TAG, permission.name + " is denied.");
Toast.makeText(TestActivity.this, "请打开相机权限", Toast.LENGTH_SHORT).show();
//引导用户至设置页手动授权
Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
Uri uri = Uri.fromParts("package", getApplicationContext().getPackageName(), null);
intent.setData(uri);
startActivity(intent);
}
}
});
} else {
takePhoto();
}
3、选择拍照或者相册选择图片
这里需要注意Android 7.0 以后对于共享文件路径的处理
具体请参考FileProvider(应用间共享文件)
/**
* 拍照
*/
private void takePhoto() {
//创建保存拍照的文件
outFile = new File(getExternalFilesDir(""), "head.jpg");
if (outFile.exists()) {
outFile.delete();
}
try {
//创建文件
outFile.createNewFile();
} catch (IOException e) {
e.printStackTrace();
Log.e("createXMLFileException", e.getMessage());
}
// 启动系统相机
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
// 判断7.0 android系统
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
//临时添加一个拍照权限
intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);
//通过FileProvider获取uri
outUri = FileProvider.getUriForFile(TestActivity.this,
"com.lin.fileProvider", outFile);
//将拍取的照片保存到指定URI
intent.putExtra(MediaStore.EXTRA_OUTPUT, outUri);
} else {
outUri = Uri.fromFile(outFile);
intent.putExtra(MediaStore.EXTRA_OUTPUT, outUri);
}
startActivityForResult(intent, PHOTO_PZ);
}
/**
* 从相册选择
*/
private void selectPhoto() {
//调用系统图库,选择图片
Intent intent = new Intent(Intent.ACTION_PICK, null);
intent.setDataAndType(
MediaStore.Images.Media.INTERNAL_CONTENT_URI, "image/*");
//返回结果和标识
startActivityForResult(intent, PHOTO_TK);
}
4、选择图片后的进行裁剪(看需求)
一般用作头像的图片不宜太大,最好作适当的裁剪
这里也涉及到FileProvider(应用间共享文件),还有对MIUI系统的适配,具体看注释
/**
* 裁剪照片
*
* @param uri
*/
private void cutPhoto(Uri uri) {
//裁剪完成后保存的文件
cutOutFile = new File(getExternalFilesDir(""), "cutHead.jpg");
if (cutOutFile.exists()) {
cutOutFile.delete();
}
try {
//创建文件
cutOutFile.createNewFile();
} catch (IOException e) {
e.printStackTrace();
Log.e("createXMLFileException", e.getMessage());
}
Log.e("Uri====", uri + "");
//裁剪图片意图
Intent intent = new Intent("com.android.camera.action.CROP");
intent.setDataAndType(uri, "image/*");
intent.putExtra("crop", "true");
// 裁剪框的比例,1:1
intent.putExtra("aspectX", 1);
intent.putExtra("aspectY", 1);
// 裁剪后输出图片的尺寸大小
intent.putExtra("outputX", 320);
intent.putExtra("outputY", 320);
//设置裁剪完保存的URI
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
//开启临时权限
intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);
//重点:针对7.0以上的操作
cutOutUri = FileProvider.getUriForFile(TestActivity.this,
"com.lin.fileProvider", cutOutFile);
intent.setClipData(ClipData.newRawUri(MediaStore.EXTRA_OUTPUT, cutOutUri));
} else {
cutOutUri = Uri.parse("file://" + "/" + cutOutFile.getPath());
}
intent.putExtra(MediaStore.EXTRA_OUTPUT, cutOutUri);
intent.putExtra("return-data", false);
/**
* return-data
* 这个属性决定我们在 onActivityResult 中接收到的是什么数据,
* 如果设置为true 那么data将会返回一个bitmap
* 如果设置为false,则会将图片保存到本地并将对应的uri返回,当然这个uri得有我们自己设定。
* 系统裁剪完成后将会将裁剪完成的图片保存在我们所这设定这个uri地址上。我们只需要在裁剪完成后直接调用该uri来设置图片,就可以了。
*/
//intent.putExtra("return-data", true); // 重点:小米的系统使用这个会使返回数据为null,所以设置为false用uri来获取数据
//图片输出格式
intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());
//头像识别 会启动系统的拍照时人脸识别
intent.putExtra("noFaceDetection", true);
startActivityForResult(intent, PHOTO_CLIP);
}
5、裁剪完后对结果进行处理(设置到界面、上传到服务器等)
在onActivityResult方法中根据请求码对拍照、相册选择、裁剪完后的结果进行处理,大致流程如下图
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == Activity.RESULT_OK) {
switch (requestCode) {
case PHOTO_PZ:
cutPhoto(outUri);
break;
case PHOTO_TK:
//获取图库结果,执行裁剪
cutPhoto(data.getData());
break;
case PHOTO_CLIP:
//1.裁剪时,这样设置 cropIntent.putExtra("return-data", true); 处理方案如下
// if (data != null) {
// Bundle bundle = data.getExtras();
// if (bundle != null) {
// Bitmap bitmap = bundle.getParcelable("data");
// ivHead.setImageBitmap(bitmap);
// // 把裁剪后的图片保存至本地
// FileUtils.writeImageToFile("cutHead.jpg", bitmap);
// }
// }
//2.cropIntent.putExtra("return-data", false);处理方案如下
//裁剪完成后的操作,上传至服务器或者本地设置
//将Uri图片转换为Bitmap
try {
Bitmap bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(cutOutUri));
ivHead.setImageBitmap(bitmap);
} catch (FileNotFoundException e) {
LogUtil.e(e.toString());
}
break;
default:
break;
}
}
}
这里贴上整个测试类的代码
/**
* @author Lin
* @date 2019/5/10
* @description 选择头像功能
*/
public class TestActivity extends BaseActivity {
private ImageView iconBack;
private RelativeLayout rlHead;
private CircleImageView ivHead;
private SelectPictureDialog dialog;
private Uri outUri;
private Uri cutOutUri;
//拍照保存的文件
private File outFile;
//裁剪完保存的图片
private File cutOutFile;
//图库
private static final int PHOTO_TK = 0;
//拍照
private static final int PHOTO_PZ = 1;
//裁剪
private static final int PHOTO_CLIP = 2;
@Override
protected int getLayoutId() {
return R.layout.activity_test;
}
@Override
protected void initView() {
iconBack = findViewById(R.id.icon_back);
rlHead = findViewById(R.id.rl_head);
ivHead = findViewById(R.id.iv_head);
}
@Override
protected void initData(Bundle savedInstanceState) {
}
@Override
protected void setEvent() {
rlHead.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.rl_head:
dialog = new SelectPictureDialog(this, R.style.MyAppDialog);
dialog.show();
dialog.setListener(new SelectPictureDialog.OnSelectListener() {
@Override
public void onClickTakePhoto() {
dialog.dismiss();
if (ActivityCompat.checkSelfPermission(TestActivity.this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
new RxPermissions(TestActivity.this).requestEach(Manifest.permission.CAMERA).subscribe(new Consumer() {
@Override
public void accept(Permission permission) throws Exception {
if (permission.granted) {
// 用户已经同意该权限
LogUtil.e(TAG, permission.name + " is granted.");
takePhoto();
} else if (permission.shouldShowRequestPermissionRationale) {
// 用户拒绝了该权限,没有选中『不再询问』(Never ask again),那么下次再次启动时,还会提示请求权限的对话框
LogUtil.e(TAG, permission.name + " is denied. More info should be provided.");
Toast.makeText(TestActivity.this, "未能授予权限,部分功能可能不能正常使用", Toast.LENGTH_SHORT).show();
} else {
// 用户拒绝了该权限,并且选中『不再询问』
LogUtil.e(TAG, permission.name + " is denied.");
Toast.makeText(TestActivity.this, "请打开相机权限", Toast.LENGTH_SHORT).show();
//引导用户至设置页手动授权
Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
Uri uri = Uri.fromParts("package", getApplicationContext().getPackageName(), null);
intent.setData(uri);
startActivity(intent);
}
}
});
} else {
takePhoto();
}
}
@Override
public void onClickAlbum() {
dialog.dismiss();
selectPhoto();
}
@Override
public void onClickCancel() {
//不用操作
}
});
break;
default:
break;
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == Activity.RESULT_OK) {
switch (requestCode) {
case PHOTO_PZ:
cutPhoto(outUri);
break;
case PHOTO_TK:
//获取图库结果,执行裁剪
cutPhoto(data.getData());
break;
case PHOTO_CLIP:
//1.裁剪时,这样设置 cropIntent.putExtra("return-data", true); 处理方案如下
// if (data != null) {
// Bundle bundle = data.getExtras();
// if (bundle != null) {
// Bitmap bitmap = bundle.getParcelable("data");
// ivHead.setImageBitmap(bitmap);
// // 把裁剪后的图片保存至本地
// FileUtils.writeImageToFile("cutHead.jpg", bitmap);
// }
// }
//2.cropIntent.putExtra("return-data", false);处理方案如下
//裁剪完成后的操作,上传至服务器或者本地设置
//将Uri图片转换为Bitmap
try {
Bitmap bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(cutOutUri));
ivHead.setImageBitmap(bitmap);
} catch (FileNotFoundException e) {
LogUtil.e(e.toString());
}
break;
default:
break;
}
}
}
/**
* 从相册选择
*/
private void selectPhoto() {
//调用系统图库,选择图片
Intent intent = new Intent(Intent.ACTION_PICK, null);
intent.setDataAndType(
MediaStore.Images.Media.INTERNAL_CONTENT_URI, "image/*");
//返回结果和标识
startActivityForResult(intent, PHOTO_TK);
}
/**
* 拍照
*/
private void takePhoto() {
//创建保存拍照的文件
outFile = new File(getExternalFilesDir(""), "head.jpg");
if (outFile.exists()) {
outFile.delete();
}
try {
//创建文件
outFile.createNewFile();
} catch (IOException e) {
e.printStackTrace();
Log.e("createXMLFileException", e.getMessage());
}
// 启动系统相机
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
// 判断7.0 android系统
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
//临时添加一个拍照权限
intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);
//通过FileProvider获取uri
outUri = FileProvider.getUriForFile(TestActivity.this,
"com.lin.fileProvider", outFile);
//将拍取的照片保存到指定URI
intent.putExtra(MediaStore.EXTRA_OUTPUT, outUri);
} else {
outUri = Uri.fromFile(outFile);
intent.putExtra(MediaStore.EXTRA_OUTPUT, outUri);
}
startActivityForResult(intent, PHOTO_PZ);
}
/**
* 裁剪照片
*
* @param uri
*/
private void cutPhoto(Uri uri) {
//裁剪完成后保存的文件
cutOutFile = new File(getExternalFilesDir(""), "cutHead.jpg");
if (cutOutFile.exists()) {
cutOutFile.delete();
}
try {
//创建文件
cutOutFile.createNewFile();
} catch (IOException e) {
e.printStackTrace();
Log.e("createXMLFileException", e.getMessage());
}
Log.e("Uri====", uri + "");
//裁剪图片意图
Intent intent = new Intent("com.android.camera.action.CROP");
intent.setDataAndType(uri, "image/*");
intent.putExtra("crop", "true");
// 裁剪框的比例,1:1
intent.putExtra("aspectX", 1);
intent.putExtra("aspectY", 1);
// 裁剪后输出图片的尺寸大小
intent.putExtra("outputX", 320);
intent.putExtra("outputY", 320);
//设置裁剪完保存的URI
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
//开启临时权限
intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);
//重点:针对7.0以上的操作
cutOutUri = FileProvider.getUriForFile(TestActivity.this,
"com.lin.fileProvider", cutOutFile);
intent.setClipData(ClipData.newRawUri(MediaStore.EXTRA_OUTPUT, cutOutUri));
} else {
cutOutUri = Uri.parse("file://" + "/" + cutOutFile.getPath());
}
intent.putExtra(MediaStore.EXTRA_OUTPUT, cutOutUri);
intent.putExtra("return-data", false);
/**
* return-data
* 这个属性决定我们在 onActivityResult 中接收到的是什么数据,
* 如果设置为true 那么data将会返回一个bitmap
* 如果设置为false,则会将图片保存到本地并将对应的uri返回,当然这个uri得有我们自己设定。
* 系统裁剪完成后将会将裁剪完成的图片保存在我们所这设定这个uri地址上。我们只需要在裁剪完成后直接调用该uri来设置图片,就可以了。
*/
//intent.putExtra("return-data", true); // 重点:小米的系统使用这个会使返回数据为null,所以设置为false用uri来获取数据
//图片输出格式
intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());
//头像识别 会启动系统的拍照时人脸识别
intent.putExtra("noFaceDetection", true);
startActivityForResult(intent, PHOTO_CLIP);
}
}