最近用到从相机/相册选择图片的功能,这个功能虽然不复杂,网上的代码也一大堆,但是考虑到可能以后别的地方也会用到,所以就抽个空封装了一个类,来实现此功能。
先列出来需要解决问题:
- 这个类需要具备哪些功能
至少要支持:相机、相册、裁剪、裁剪比例设定、缩放功能,因为使用系统的自带的裁剪功能,所以如裁剪成圆形、裁剪时展示网格、图片进行二次压缩等功能暂且不论。
- 如何能够在下次使用的时候快速集成进去
如果要快速集成,最好是几行代码就可以得到选择之后的图片,那么就需要将弹出相机/相册选择框、拍照/选择相册之后的裁剪逻辑、图片保存逻辑、权限检查逻辑、7.0以上Uri逻辑、版本兼容问题都在一个类中完成,使用者只需要设置开关参数即可。
- 6.0以上权限问题
6.0以上动态检查权限问题、设置了targetSdkVersion设置为23以下时正常的权限检查无效问题,没有权限时自动获取权限。
- 7.0以上Uri问题
在7.0版本以上系统设定不能直接使用本地真是路径的Uri,必须使用FileProvider封装之后才可以获取,但是这个也是分情况的,具体下面会提到。
- 重复选择拍照时产生的垃圾文件问题
选定一张图片后,觉得不好重新由选择了一张,之前的那个图片文件怎么处理,如果加入裁剪功能,那么裁剪后拍照的原图怎么处理。如果像发朋友圈一样需要连续多张图片时,产生的文件会不会冲突?
展示效果
先上效果图,然后说代码:

注意事项
根据前面提到的几个问题,有以下几点需要说明:
-
封装一个类,传入Activity上下文(没有使用content是因为需要打开相机、相册等intent需要startactiity,虽然fragment也可以,但是这样在纯activity中就无法使用了),提供是否裁剪、裁剪比例、裁剪输出尺寸、是否缩放的开关方法,并提供默认值,可以在使用过程中自行改动。使用
PopupWindow
展示选择框(当然也可以使用dialog),这样可以自由控制选择框的。 -
弹出选择框时检查权限,如果没有权限则进行权限申请,如果用户点击了已拒绝授权,弹出设置界面,引导用户授权,授权成功后弹出选择框。
-
提供回调接口,在获取图片成功之后通过回调返回图片的路径。
-
相机拍照、相册选择之后、裁剪之后、权限回调需要通过
onActivityResult
和onRequestPermissionsResult
来实现,所以使用的地方需要将这两个方法传递给封装的类。 -
前面提到过的7.0以上Uri问题,使用
FileProvider
来实现。但是如果项目的targetSdkVersion
设置成23以下的话,即使在7.0以上手机上,也可以不用FileProvider
的形式来获取Uri,不过targetSdkVersion
在23以下,在小米市场无法上传安装包(目前我就知道这个市场) -
在4.4开始,从相册选择照片返回的Uri就不能直接使用,所以需要经过转化才能得到真实的图片路径。
-
7.0以上,
targetSdkVersion
设置为23以上时,相机拍照输出的uri需要通过FileProvider
获取的,否则会抛出异常。裁剪输入的uri为普通形式获取的uri,裁剪输出的uri需要时通过FileProvider
转换的uri,要不会提示无法载入图片/无法保存裁剪的图片。 -
图片裁剪时,可以选择直接
return-data
true或者false,true则直接返回对应的bitmap,false需要制定一个输出uri,为了内存考虑,在封装时,选择false,让其返回对应的uri(需要考虑targetSdkVersion
和7.0以上的uri问题)。 -
在
targetSdkVersion
设置为23以下时,从相机拍照时会生成一个图片,可以得到一个对应的uri,如果需要裁剪,可以继续使用该uri,系统会自动替代之前的拍照生成的文件,这样减少了无用文件。但是设置targetSdkVersion
大于23,且在高版本(这个没有具体研究是哪一个版本,我测试的是华为8.1)使用同一个uri裁剪时,会抛出一个已存在的错误。所以封装类中相机拍照后裁剪输入和输出的uri处理成不同的,裁剪完成后再删除拍照时生成的图片。 -
文件的命名使用当前时间戳,保存在sd卡下面以项目包名为名的文件夹下。
-
为了方便代码集成,一些处理图片、处理uri的公共方法,也封装到一个类中,如果觉得不合适可以自行单独出来一个工具类(或者自己的项目中已经有对应的方法就可以删掉对应的代码)。
-
在fragment中使用时,因为传递的是activity,所以
onActivityResult
等方法是fragment对应的activity先接收到,需要activity传递给fragment在传递到这个类中。 -
代码中有详细的注释
-
以上代码在三星GT-I9300(Android 4.3),华为Mate10Pro(Android8.1)中测试过。
代码
- 主要的类(SelectPictureManager.java)
import android.Manifest;
import android.app.Activity;
import android.content.ContentUris;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.drawable.ColorDrawable;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.provider.DocumentsContract;
import android.provider.MediaStore;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.FileProvider;
import android.support.v4.content.PermissionChecker;
import android.text.TextUtils;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.PopupWindow;
import android.widget.Toast;
import java.io.File;
import java.io.IOException;
/**
* 管理选择相册的类
* Application
* Created by anonyper on 2018/11/19.
*/
public class SelectPictureManager {
/*
定义返回的code值
*/
public static final int TAKE_PHOTO_CODE = 1000;//拍照
public static final int CHOOSE_PHOTO_CODE = 2000;//选择相册
public static final int PICTURE_CROP_CODE = 3000;//剪切图片
public static final int REQUEST_PERMISSIONS = 4000;//授权code
private boolean isNeedCrop = false;//是否需要裁剪 默认不需要
private boolean isScale = true;//是否需要支持缩放 在可裁剪情况下有效
private boolean isContinuous = false;//是否是连拍模式 比如连续需要两张以上照片
private String fileName;//文件名字
private String oldFileName;//拍照裁剪时的原图片 需要删掉
private Uri outImageUri;//相机拍照图片保存地址
private int aspectX = 1000;//裁剪的宽高比例
private int aspectY = 1001;//裁剪的宽高比例 两个比例略微不一样是为了解决部分手机1:1时显示的时圆形裁剪框
private int outputX = 400;//裁剪后输出图片的尺寸大小
private int outputY = 400;//裁剪后输出图片的尺寸大小
Activity activity;//全局上下文 需要startactiity
PictureSelectListner pictureSelectListner;//选择之后照片的回调监听
/**
* 构造SelectPictureManager对象
*
* @param activity 上下文 这样就可以自主的实现
*/
public SelectPictureManager(Activity activity) {
this(activity, null);
}
/**
* 构造SelectPictureManager对象
*
* @param activity 上下文
* @param pictureSelectListner 回调监听
*/
public SelectPictureManager(Activity activity, PictureSelectListner pictureSelectListner) {
this.activity = activity;
this.pictureSelectListner = pictureSelectListner;
fileName = System.currentTimeMillis() + ".jpg";//默认使用时间戳作为图片名字
}
/**
* 设置图片回调监听
*
* @param pictureSelectListner
*/
public void setPictureSelectListner(PictureSelectListner pictureSelectListner) {
this.pictureSelectListner = pictureSelectListner;
}
/**
* 设置连拍 这样可以连续排多张不重复的图片
*
* @param isContinuous
* @return
*/
public SelectPictureManager setContinuous(boolean isContinuous) {
this.isContinuous = isContinuous;
return this;
}
/**
* 设置是否需要裁剪
*
* @param isNeedCut
* @return
*/
public SelectPictureManager setNeedCrop(boolean isNeedCrop) {
this.isNeedCrop = isNeedCrop;
return this;
}
/**
* 设置裁剪是否需要缩放
*
* @param isScale
* @return
*/
public SelectPictureManager setScaleAble(boolean isScale) {
this.isScale = isScale;
return this;
}
/**
* 设置裁剪的宽高比例
*
* @param x 裁剪比例宽
* @param y 裁剪比例高
* @return
*/
public SelectPictureManager setAspect(int x, int y) {
this.aspectX = x;
this.aspectY = y;
return this;
}
/**
* 设置裁剪后输出的尺寸大小
*
* @param x 裁剪输入的宽
* @param y 裁剪输出的高
* @return
*/
public SelectPictureManager setOutPutSize(int x, int y) {
this.outputX = x;
this.outputY = y;
return this;
}
View showView;//使用PopupWindow 显示时需要一个View,如果觉得不便可以使用dialog
/**
* 展示选择拍照的pop框 一个是从相机选择 一个是拍照选择
*
* @param showView 需要展示view
*/
public void showSelectPicturePopupWindow(View showView) {
if (showView == null) {
return;
}
this.showView = showView;
if (this.activity == null) {
if (this.pictureSelectListner != null) {
this.pictureSelectListner.throwError(new NullPointerException("上下文activity不可为空"));
} else {
throw new NullPointerException("上下文activity不可为空");
}
}
boolean hasPermission = checkPermission();
if (hasPermission) {
//拥有权限 可以直接打开
final PopupWindow popupWindow = new PopupWindow(this.activity);
LayoutInflater inflater = (LayoutInflater) this.activity
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
assert inflater != null;
final View mView = inflater.inflate(R.layout.pop_window_view, null);
Button btn_camera = (Button) mView.findViewById(R.id.icon_btn_camera);
Button btn_select = (Button) mView.findViewById(R.id.icon_btn_choose);
Button btn_cancel = (Button) mView.findViewById(R.id.icon_btn_cancel);
btn_select.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
choosePhoto();
popupWindow.dismiss();
}
});
btn_camera.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
takePhoto();
popupWindow.dismiss();
}
});
btn_cancel.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
popupWindow.dismiss();
}
});
// 导入布局
popupWindow.setContentView(mView);
// 设置动画效果
popupWindow.setAnimationStyle(R.anim.in_top);
popupWindow.setWidth(WindowManager.LayoutParams.MATCH_PARENT);
popupWindow.setHeight(WindowManager.LayoutParams.WRAP_CONTENT);
// 设置可触
popupWindow.setFocusable(true);
ColorDrawable dw = new ColorDrawable(0x0000000);
popupWindow.setBackgroundDrawable(dw);
// 单击弹出窗以外处 关闭弹出窗
mView.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
int height = mView.findViewById(R.id.ll_pop).getTop();
int y = (int) event.getY();
if (event.getAction() == MotionEvent.ACTION_UP) {
if (y < height) {
popupWindow.dismiss();
}
}
return true;
}
});
popupWindow.showAtLocation(showView,
Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL, 0, 0);
}
}
/**
* 先初始化好要保存的文件以及对应的uri
*/
private void initSavedFile(boolean isCrop) {
if (!isContinuous && outImageUri != null && !TextUtils.isEmpty(fileName)) {//不是连拍 同时已经存在
return;
}
File parentFile = new File(Environment.getExternalStorageDirectory(), activity.getPackageName());//先创建包名对应的文件夹
if (!parentFile.exists()) {
parentFile.mkdir();
} else if (!parentFile.isDirectory()) {
parentFile.delete();
parentFile.mkdir();
}
File outputImage = new File(Environment.getExternalStorageDirectory(), activity.getPackageName() + "/" + fileName);
try {
if (outputImage.exists()) {
if (isContinuous || isCrop) {//如果是连拍模式,就需要重新起一个名字
if (isCrop) {//如果是裁剪的话 当这个文件已存在表示是通过相机拍照过来的,所以需要额外指定裁剪之后存储的uri
oldFileName = fileName;
}
fileName = System.currentTimeMillis() + ".jpg";
initSavedFile(isCrop);//重新来一下
return;
} else {
outputImage.delete();
}
}
outputImage.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
//以下uri的处理在targetVersion大于23时,同时在7.0版本以上时需要做区分
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N || isCrop) {//裁剪的时候 保存需要使用一般的uri 要不然出现无法保存裁剪的图片的错误
outImageUri = Uri.fromFile(outputImage);
} else {
//Android 7.0系统开始 使用本地真实的Uri路径不安全,使用FileProvider封装共享Uri(相机拍照输出的uri)
outImageUri = FileProvider.getUriForFile(activity, activity.getPackageName() + ".fileprovider", outputImage);
}
}
/**
* 从相机拍照
*/
private void takePhoto() {
initSavedFile(false);
Intent takeIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
// 下面这句指定调用相机拍照后的照片存储的路径(7.0以上需要使用privider获取的uri)
takeIntent.putExtra(MediaStore.EXTRA_OUTPUT, outImageUri);
activity.startActivityForResult(takeIntent, TAKE_PHOTO_CODE);
}
/**
* 从相册选择
*/
private void choosePhoto() {
Intent intent = new Intent("android.intent.action.GET_CONTENT");
intent.setType("image/*");
activity.startActivityForResult(intent, CHOOSE_PHOTO_CODE); // 打开相册
}
/**
* 裁剪图片
*/
private void cropPicture(Uri pictureUri) {
initSavedFile(true);//从相册先择时,是没有初始化要保存的文件路径以及对应的uri
Intent cropIntent = new Intent("com.android.camera.action.CROP");
cropIntent.setDataAndType(pictureUri, "image/*");//7.0以上 输入的uri需要是provider提供的
// 开启裁剪:打开的Intent所显示的View可裁剪
cropIntent.putExtra("crop", "true");
// 裁剪宽高比
cropIntent.putExtra("aspectX", aspectX);
cropIntent.putExtra("aspectY", aspectY);
// 裁剪输出大小
cropIntent.putExtra("outputX", outputX);
cropIntent.putExtra("outputY", outputY);
cropIntent.putExtra("scale", isScale);
/**
* return-data
* 这个属性决定onActivityResult 中接收到的是什么数据类型,
* true data将会返回一个bitmap
* false,则会将图片保存到本地并将我们指定的对应的uri。
*/
cropIntent.putExtra("return-data", false);
// 当 return-data 为 false 的时候需要设置输出的uri地址
cropIntent.putExtra(MediaStore.EXTRA_OUTPUT, outImageUri);//输出的uri为普通的uri,通过provider提供的uri会出现无法保存的错误
// 图片输出格式
cropIntent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());
cropIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);//不加会出现无法加载此图片的错误
cropIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);// 这两句是在7.0以上版本当targeVersion大于23时需要
activity.startActivityForResult(cropIntent, PICTURE_CROP_CODE);
}
/**
* 收到图片结果后的处理逻辑
*
* @param requestCode
* @param resultCode
* @param data
*/
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode == Activity.RESULT_OK) {
File imageFile = new File(Environment.getExternalStorageDirectory(), activity.getPackageName() + "/" + fileName);
Uri inImageUri = null;//需要裁剪时输入的uri
switch (requestCode) {
case TAKE_PHOTO_CODE:
// TODO: 调用相机拍照
if (imageFile == null && !imageFile.exists()) {
if (pictureSelectListner != null) {
pictureSelectListner.throwError(new NullPointerException("没有找到对应的路径"));
}
return;
}
if (this.isNeedCrop) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
inImageUri = Uri.fromFile(imageFile);
} else {
//Android 7.0系统开始 使用本地真实的Uri路径不安全,使用FileProvider封装共享Uri
inImageUri = FileProvider.getUriForFile(activity, activity.getPackageName() + ".fileprovider", imageFile);
}
cropPicture(inImageUri);
} else {
if (pictureSelectListner != null) {
pictureSelectListner.onPictureSelect(imageFile.getAbsolutePath());
}
}
break;
case CHOOSE_PHOTO_CODE:
// TODO: 从相册选择
Uri uri = data.getData();
String filePath = Util.getFilePathByUri(activity, uri);
if (TextUtils.isEmpty(filePath)) {
if (pictureSelectListner != null) {
pictureSelectListner.throwError(new NullPointerException("没有找到对应的路径"));
}
return;
}
if (this.isNeedCrop) {
File cropFile = new File(filePath);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
inImageUri = Uri.fromFile(cropFile);
} else {
//Android 7.0系统开始 使用本地真实的Uri路径不安全,使用FileProvider封装共享Uri
inImageUri = FileProvider.getUriForFile(activity, activity.getPackageName() + ".fileprovider", cropFile);
}
cropPicture(inImageUri);
} else {
if (pictureSelectListner != null) {
pictureSelectListner.onPictureSelect(filePath);
}
}
break;
case PICTURE_CROP_CODE:
// TODO: 图片裁剪
String cropFile = Util.getFilePathByUri(activity, outImageUri);
if (!TextUtils.isEmpty(oldFileName)) {//相机拍照裁剪时删掉原来的照片
File oldImageFile = new File(Environment.getExternalStorageDirectory(), activity.getPackageName() + "/" + oldFileName);
oldImageFile.deleteOnExit();
}
if (TextUtils.isEmpty(cropFile)) {
if (pictureSelectListner != null) {
pictureSelectListner.throwError(new NullPointerException("没有找到对应的路径"));
}
} else {
if (pictureSelectListner != null) {
pictureSelectListner.onPictureSelect(cropFile);
}
}
break;
}
}
}
/**
* 6.0版本以上需要检查权限 动态的权限控制 这个地方做了相机、存储权限的模糊检查,虽然单从相册选择时不需要相机权限
*/
private boolean checkPermission() {
String[] permissions = new String[]{Manifest.permission.CAMERA,
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.READ_EXTERNAL_STORAGE};
//检查权限
if (!checkSelfPermission(activity, Manifest.permission.CAMERA)
|| !checkSelfPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE)
|| !checkSelfPermission(activity, Manifest.permission.READ_EXTERNAL_STORAGE)
) {
ActivityCompat.requestPermissions(activity, permissions, REQUEST_PERMISSIONS);//没有权限时进行权限申请
return false;
}
return true;
}
/**
* 根部不同的情况进行权限检查,在targetSdkVersion设置为23以下时,不管权限是否开启,一般的checkSelfPermission都返回true
*
* @param activity
* @param permission
* @return
*/
private boolean checkSelfPermission(Activity activity, String permission) {
int sdkVersion = activity.getApplicationInfo().targetSdkVersion;
boolean ret = true;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (sdkVersion >= Build.VERSION_CODES.M) {
ret = activity.checkSelfPermission(permission) == PackageManager.PERMISSION_GRANTED;
} else {
ret = PermissionChecker.checkSelfPermission(activity, permission) == PermissionChecker.PERMISSION_GRANTED;
}
}
return ret;
}
/**
* 6。0版本以上的权限申请
*
* @param requestCode
* @param permissions
* @param grantResults
*/
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
switch (requestCode) {
case REQUEST_PERMISSIONS:
boolean isGrant = true;
for (int index = 0; index < grantResults.length; index++) {
if (grantResults[0] != PackageManager.PERMISSION_GRANTED) {
isGrant = false;
}
}
if (isGrant) {
showSelectPicturePopupWindow(showView);
} else {
Toast.makeText(activity, "没有获取对应的权限", Toast.LENGTH_SHORT).show();
/**
* 跳转到 APP 详情的权限设置页
*/
Intent intent = Util.getSettingIntent(activity);
activity.startActivity(intent);
}
break;
default:
}
}
/**
* 图片选定之后的回调监听
*/
public interface PictureSelectListner {
void onPictureSelect(String imagePath);//返回图片的路径
void throwError(Exception e);//当错误时返回对应的异常
}
/**
* 工具类 为了保持使用方便,将该类直接放到这里,如果觉得不便可以单独出来一个类
*/
static class Util {
/**
* 打开系统的设置界面 让用户自己授权
*
* @param context
* @return
*/
public static Intent getSettingIntent(Context context) {
Intent localIntent = new Intent();
localIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
if (Build.VERSION.SDK_INT >= 9) {
localIntent.setAction("android.settings.APPLICATION_DETAILS_SETTINGS");
localIntent.setData(Uri.fromParts("package", context.getPackageName(), null));
} else if (Build.VERSION.SDK_INT <= 8) {
localIntent.setAction(Intent.ACTION_VIEW);
localIntent.setClassName("com.android.settings", "com.android.settings.InstalledAppDetails");
localIntent.putExtra("com.android.settings.ApplicationPkgName", context.getPackageName());
}
return localIntent;
}
/**
* 获取uri对应的真是路径
*
* @param context
* @param uri
* @param selection
* @return
*/
public static String getImagePath(Context context, Uri uri, String selection) {
String path = null;
// 通过Uri和selection来获取真实的图片路径
Cursor cursor = context.getContentResolver().query(uri, null, selection, null, null);
if (cursor != null) {
if (cursor.moveToFirst()) {
path = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA));
}
cursor.close();
}
return path;
}
/**
* 4。4以上获取相册的地址 相册图片返回的uri是经过系统封装过的
*
* @param data
*/
public static String getFilePathByUri(Context context, Uri uri) {
String imagePath = null;
if (context == null || uri == null) {
return imagePath;
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
if (DocumentsContract.isDocumentUri(context, uri)) {
// 如果是document类型的Uri,则通过document id处理
String docId = DocumentsContract.getDocumentId(uri);
if ("com.android.providers.media.documents".equals(uri.getAuthority())) {
String id = docId.split(":")[1]; // 解析出数字格式的id
String selection = MediaStore.Images.Media._ID + "=" + id;
imagePath = getImagePath(context, MediaStore.Images.Media.EXTERNAL_CONTENT_URI, selection);
} else if ("com.android.providers.downloads.documents".equals(uri.getAuthority())) {
Uri contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"), Long.valueOf(docId));
imagePath = getImagePath(context, contentUri, null);
}
} else if ("content".equalsIgnoreCase(uri.getScheme())) {
// 如果是content类型的Uri,则使用普通方式处理
imagePath = getImagePath(context, uri, null);
} else if ("file".equalsIgnoreCase(uri.getScheme())) {
// 如果是file类型的Uri,直接获取图片路径即可
imagePath = uri.getPath();
}
} else {
if ("content".equalsIgnoreCase(uri.getScheme())) {
// 如果是content类型的Uri,则使用普通方式处理
imagePath = getImagePath(context, uri, null);
} else if ("file".equalsIgnoreCase(uri.getScheme())) {
// 如果是file类型的Uri,直接获取图片路径即可
imagePath = uri.getPath();
}
}
return imagePath;
}
}
}
- 布局文件(pop_window_view.xml)
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#55000000"
android:orientation="vertical">
<LinearLayout
android:id="@+id/ll_pop"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_marginLeft="15dp"
android:layout_marginRight="15dp"
android:orientation="vertical">
<Button
android:id="@+id/icon_btn_camera"
android:layout_width="match_parent"
android:layout_height="42dp"
android:background="@drawable/d_arc_5_white"
android:text="拍照"
android:textColor="@color/blue" />
<View
android:layout_width="match_parent"
android:layout_height="10px" />
<Button
android:id="@+id/icon_btn_choose"
android:layout_width="match_parent"
android:layout_height="42dp"
android:background="@drawable/d_arc_5_white"
android:text="从相册选择"
android:textColor="@color/blue" />
<Button
android:id="@+id/icon_btn_cancel"
android:layout_width="match_parent"
android:layout_height="42dp"
android:layout_marginBottom="15dp"
android:layout_marginTop="10dp"
android:background="@drawable/d_arc_5_white"
android:text="取消"
android:textColor="@color/black" />
</LinearLayout>
</RelativeLayout>
- 按钮背景(d_arc_5_white.xml)
<?xml version="1.0" encoding="UTF-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<!-- 填充的颜色 -->
<solid android:color="@color/white" />
<!-- 设置按钮的四个角为弧形 -->
<!-- android:radius 弧形的半径 -->
<corners android:radius="5dip" />
</shape>
- 颜色
color/white=“#FFFFFF”
color/blue=“#0000FF”
- 7.0以上Provider设置
写在AndroidManifest.xml中,其中authorities对应的值要和代码的中保持一致,否则会抛出异常
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="{packagename}.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_xml" />
</provider>
- provider_xml.xml
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="external-path" path="."/>
</paths>
- Provider中paths节点中参数对应说明
子节点 | 对应路径 |
---|---|
files-path | Context.getFilesDir() |
cache-path | Context.getCacheDir() |
external-path | Environment.getExternalStorageDirectory() |
external-files-path | Context.getExternalFilesDir(null) |
external-cache-path | Context.getExternalCacheDir() |
- 使用方法(fragment中)对应的activity中也需要设置传递onActivityResult和onRequestPermissionsResult
SelectPictureManager selectPictureManager;
void initSelectPictureManager() {
selectPictureManager = new SelectPictureManager(this.getActivity());
selectPictureManager.setPictureSelectListner(new SelectPictureManager.PictureSelectListner() {
@Override
public void onPictureSelect(String imagePath) {
LogUtil.i("选择图片的路径是:" + imagePath);
ImageUtil.displayImage(MineFragmentTest.this.getContext(), new File(imagePath), imgv_userhead);
}
@Override
public void throwError(Exception e) {
e.printStackTrace();
}
});
selectPictureManager.setNeedCrop(true);//需要裁剪
selectPictureManager.setOutPutSize(400, 400);//输入尺寸
selectPictureManager.setContinuous(true);//设置连拍
selectPictureManager.showSelectPicturePopupWindow(this.getView());
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
selectPictureManager.onActivityResult(requestCode, resultCode, data);
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
selectPictureManager.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
- 配置权限
<uses-feature android:name="android.hardware.camera" />
<!--相机权限-->
<uses-permission android:name="android.permission.CAMERA" />
<!--写入SD卡的权限:保存相机拍照后的照片-->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<!--读取SD卡的权限:打开相册选取图片所必须的权限-->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
参考资料
以上!
网友评论