1.使用glide进行图片的展示效果
2.PermissionGen动态权限获取
代码:
一、准备工作
1.在AndroidManifest.xml添加权限
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.CAMERA" />
2.在build.gradle中添加依赖
dependencies { implementation 'com.github.bumptech.glide:glide:4.10.0' implementation 'com.lovedise:permissiongen:0.0.6' }
二、主代码
1.util工具类(toast和照片)
import android.content.Context; import android.widget.Toast; public class ToastUtil { public static Toast mToast; public static void showMsg(Context context, String msg){ if(mToast == null){ mToast = Toast.makeText(context,msg,Toast.LENGTH_SHORT); }else{ mToast.setText(msg); } mToast.show(); } }
import android.content.ContentValues; import android.content.Context; import android.content.Intent; import android.database.Cursor; import android.graphics.Bitmap; import android.net.Uri; import android.os.Build; import android.os.Environment; import android.provider.MediaStore; import androidx.appcompat.app.AppCompatActivity; import java.io.File; /** * CSDN_LQR * 图片选择工具类 */ public class LQRPhotoSelectUtils { public static final int REQ_TAKE_PHOTO = 10001; public static final int REQ_SELECT_PHOTO = 10002; public static final int REQ_ZOOM_PHOTO = 10003; private AppCompatActivity mActivity; //拍照或剪切后图片的存放位置(参考file_provider_paths.xml中的路径) private String imgPath = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + String.valueOf(System.currentTimeMillis()) + ".jpg"; //FileProvider的主机名:一般是包名+".fileprovider",严格上是build.gradle中defaultConfig{}中applicationId对应的值+".fileprovider" private String AUTHORITIES = "packageName" + ".fileprovider"; private boolean mShouldCrop = false;//是否要裁剪(默认不裁剪) private Uri mOutputUri = null; private File mInputFile; private File mOutputFile = null; //剪裁图片宽高比 private int mAspectX = 1; private int mAspectY = 1; //剪裁图片大小 private int mOutputX = 800; private int mOutputY = 480; PhotoSelectListener mListener; /** * 可指定是否在拍照或从图库选取照片后进行裁剪 * <p> * 默认裁剪比例1:1,宽度为800,高度为480 * * @param activity 上下文 * @param listener 选取图片监听 * @param shouldCrop 是否裁剪 */ public LQRPhotoSelectUtils(AppCompatActivity activity, PhotoSelectListener listener, boolean shouldCrop) { mActivity = activity; mListener = listener; mShouldCrop = shouldCrop; AUTHORITIES = activity.getPackageName() + ".fileprovider"; imgPath = generateImgePath(); } /** * 可以拍照或从图库选取照片后裁剪的比例及宽高 * * @param activity 上下文 * @param listener 选取图片监听 * @param aspectX 图片裁剪时的宽度比例 * @param aspectY 图片裁剪时的高度比例 * @param outputX 图片裁剪后的宽度 * @param outputY 图片裁剪后的高度 */ public LQRPhotoSelectUtils(AppCompatActivity activity, PhotoSelectListener listener, int aspectX, int aspectY, int outputX, int outputY) { this(activity, listener, true); mAspectX = aspectX; mAspectY = aspectY; mOutputX = outputX; mOutputY = outputY; } /** * 设置FileProvider的主机名:一般是包名+".fileprovider",严格上是build.gradle中defaultConfig{}中applicationId对应的值+".fileprovider" * <p> * 该工具默认是应用的包名+".fileprovider",如项目build.gradle中defaultConfig{}中applicationId不是包名,则必须调用此方法对FileProvider的主机名进行设置,否则Android7.0以上使用异常 * * @param authorities FileProvider的主机名 */ public void setAuthorities(String authorities) { this.AUTHORITIES = authorities; } /** * 修改图片的存储路径(默认的图片存储路径是SD卡上 Android/data/应用包名/时间戳.jpg) * * @param imgPath 图片的存储路径(包括文件名和后缀) */ public void setImgPath(String imgPath) { this.imgPath = imgPath; } /** * 拍照获取 */ public void takePhoto() { File imgFile = new File(imgPath); if (!imgFile.getParentFile().exists()) { imgFile.getParentFile().mkdirs(); } Uri imgUri = null; // if (Build.VERSION.SDK_INT >= 24) {//这里用这种传统的方法无法调起相机 // imgUri = FileProvider.getUriForFile(mActivity, AUTHORITIES, imgFile); // } else { // imgUri = Uri.fromFile(imgFile); // } /* * 1.现象 在项目中调用相机拍照和录像的时候,android4.x,Android5.x,Android6.x均没有问题,在Android7.x下面直接闪退 2.原因分析 Android升级到7.0后对权限又做了一个更新即不允许出现以file://的形式调用隐式APP,需要用共享文件的形式:content:// URI 3.解决方案 下面是打开系统相机的方法,做了android各个版本的兼容: * */ if (Build.VERSION.SDK_INT < 24) { // 从文件中创建uri imgUri = Uri.fromFile(imgFile); } else { //兼容android7.0 使用共享文件的形式 ContentValues contentValues = new ContentValues(1); contentValues.put(MediaStore.Images.Media.DATA, imgFile.getAbsolutePath()); imgUri = mActivity.getApplication().getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues); } Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); intent.putExtra(MediaStore.EXTRA_OUTPUT, imgUri); mActivity.startActivityForResult(intent, REQ_TAKE_PHOTO); } /** * 从图库获取 */ public void selectPhoto() { Intent intent = new Intent(Intent.ACTION_PICK, null); intent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*"); mActivity.startActivityForResult(intent, REQ_SELECT_PHOTO); // Intent intent = new Intent(Intent.ACTION_GET_CONTENT); // intent.setType("image/*"); // mActivity.startActivityForResult(intent, REQ_SELECT_PHOTO); } private void zoomPhoto(File inputFile, File outputFile) { File parentFile = outputFile.getParentFile(); if (!parentFile.exists()) { parentFile.mkdirs(); } Intent intent = new Intent("com.android.camera.action.CROP"); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { intent.setDataAndType(getImageContentUri(mActivity, inputFile), "image/*"); } else { intent.setDataAndType(Uri.fromFile(inputFile), "image/*"); } intent.putExtra("crop", "true"); //设置剪裁图片宽高比 intent.putExtra("mAspectX", mAspectX); intent.putExtra("mAspectY", mAspectY); //设置剪裁图片大小 intent.putExtra("mOutputX", mOutputX); intent.putExtra("mOutputY", mOutputY); // 是否返回uri intent.putExtra("return-data", false); intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(outputFile)); intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString()); mActivity.startActivityForResult(intent, REQ_ZOOM_PHOTO); } public void attachToActivityForResult(int requestCode, int resultCode, Intent data) { if (resultCode == AppCompatActivity.RESULT_OK) { switch (requestCode) { case LQRPhotoSelectUtils.REQ_TAKE_PHOTO://拍照 mInputFile = new File(imgPath); if (mShouldCrop) {//裁剪 mOutputFile = new File(generateImgePath()); mOutputUri = Uri.fromFile(mOutputFile); zoomPhoto(mInputFile, mOutputFile); } else {//不裁剪 mOutputUri = Uri.fromFile(mInputFile); if (mListener != null) { mListener.onFinish(mInputFile, mOutputUri); } } break; case LQRPhotoSelectUtils.REQ_SELECT_PHOTO://图库 if (data != null) { Uri sourceUri = data.getData(); String[] proj = {MediaStore.Images.Media.DATA}; Cursor cursor = mActivity.managedQuery(sourceUri, proj, null, null, null); int columnIndex = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA); cursor.moveToFirst(); String imgPath = cursor.getString(columnIndex); mInputFile = new File(imgPath); if (mShouldCrop) {//裁剪 mOutputFile = new File(generateImgePath()); mOutputUri = Uri.fromFile(mOutputFile); zoomPhoto(mInputFile, mOutputFile); } else {//不裁剪 mOutputUri = Uri.fromFile(mInputFile); if (mListener != null) { mListener.onFinish(mInputFile, mOutputUri); } } } break; case LQRPhotoSelectUtils.REQ_ZOOM_PHOTO://裁剪 if (data != null) { if (mOutputUri != null) { //删除拍照的临时照片 File tmpFile = new File(imgPath); if (tmpFile.exists()) tmpFile.delete(); if (mListener != null) { mListener.onFinish(mOutputFile, mOutputUri); } } } break; } } } /** * 安卓7.0裁剪根据文件路径获取uri */ private Uri getImageContentUri(Context context, File imageFile) { String filePath = imageFile.getAbsolutePath(); Cursor cursor = context.getContentResolver().query( MediaStore.Images.Media.EXTERNAL_CONTENT_URI, new String[]{MediaStore.Images.Media._ID}, MediaStore.Images.Media.DATA + "=? ", new String[]{filePath}, null); if (cursor != null && cursor.moveToFirst()) { int id = cursor.getInt(cursor .getColumnIndex(MediaStore.MediaColumns._ID)); Uri baseUri = Uri.parse("content://media/external/images/media"); return Uri.withAppendedPath(baseUri, "" + id); } else { if (imageFile.exists()) { ContentValues values = new ContentValues(); values.put(MediaStore.Images.Media.DATA, filePath); return context.getContentResolver().insert( MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values); } else { return null; } } } /** * 产生图片的路径,带文件夹和文件名,文件名为当前毫秒数 */ private String generateImgePath() { return getExternalStoragePath() + File.separator + String.valueOf(System.currentTimeMillis()) + ".jpg"; // return Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + String.valueOf(System.currentTimeMillis()) + ".jpg";//测试用 } /** * 获取SD下的应用目录 */ private String getExternalStoragePath() { StringBuilder sb = new StringBuilder(); sb.append(Environment.getExternalStorageDirectory().getAbsolutePath()); sb.append(File.separator); String ROOT_DIR = "Android/data/" + mActivity.getPackageName(); sb.append(ROOT_DIR); sb.append(File.separator); return sb.toString(); } public interface PhotoSelectListener { void onFinish(File outputFile, Uri outputUri); } }
2.MainActivity
import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.app.AppCompatActivity; import androidx.core.app.ActivityCompat; import androidx.core.content.ContextCompat; import android.Manifest; import android.app.Activity; import android.content.Intent; import android.content.pm.PackageManager; import android.net.Uri; import android.os.Bundle; import android.view.View; import android.widget.Button; import android.widget.ImageView; import android.widget.TextView; import android.widget.Toast; import com.bumptech.glide.Glide; import com.example.camera.utils.LQRPhotoSelectUtils; import com.example.camera.utils.ToastUtil; import java.io.File; import kr.co.namee.permissiongen.PermissionGen; public class MainActivity extends AppCompatActivity { //定义控件 private Button mBtnTakePhoto; private Button mBtnSelectPic; private Button mBtnFinish; private ImageView mIvPic; private TextView mTvPath; private TextView mTvUri; private static final int REQUEST_CODE = 0x001; private LQRPhotoSelectUtils mLqrPhotoSelectUtils; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //绑定控件 mBtnTakePhoto = findViewById(R.id.btn_take_photo); mBtnSelectPic = findViewById(R.id.btn_select_pic); mTvPath = findViewById(R.id.tvPath); mTvUri = findViewById(R.id.tvUri); mIvPic = findViewById(R.id.iv_pic); mBtnFinish = findViewById(R.id.btn_finish); init(); initListener(); } private void init() {//初始化 //创建LQRPhotoSelectUtils(一个Activity对应一个LQRPhotoSelectUtils) mLqrPhotoSelectUtils = new LQRPhotoSelectUtils(this, new LQRPhotoSelectUtils.PhotoSelectListener() { @Override public void onFinish(File outputFile, Uri outputUri) { //当拍照或从图库选取图片成功后回调 mTvPath.setText(outputFile.getAbsolutePath());//显示图片路径 mTvUri.setText(outputUri.toString());//显示图片uri //System.out.println(outputUri.toString()); Glide.with(MainActivity.this).load(outputUri).into(mIvPic);//使用Glide进行图片展示 } }, true);//是否裁剪。true裁剪,false不裁剪 //检查权限 if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.READ_EXTERNAL_STORAGE)!= PackageManager.PERMISSION_GRANTED|| ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE)!= PackageManager.PERMISSION_GRANTED|| ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.CAMERA)!= PackageManager.PERMISSION_GRANTED){ ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.CAMERA}, REQUEST_CODE); }else{ ToastUtil.showMsg(MainActivity.this, "已经获得了所有权限"); } } private void initListener(){//初始化监听器 mBtnTakePhoto.setOnClickListener(new View.OnClickListener(){ @Override public void onClick(View v) { //获取权限,调用拍照方法 PermissionGen.with(MainActivity.this) .addRequestCode(LQRPhotoSelectUtils.REQ_TAKE_PHOTO) .permissions(Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.CAMERA ).request(); //拍照 mLqrPhotoSelectUtils.takePhoto(); } }); mBtnSelectPic.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // 选择图片 mLqrPhotoSelectUtils.selectPhoto(); } }); mBtnFinish.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (mTvPath.getText().toString().equals("图片路径:")){ // 如果一张图片都没选 ToastUtil.showMsg(getApplicationContext(), "您还没有选择图片呢!"); }else{ ToastUtil.showMsg(getApplicationContext(), "选择图片成功:"+mTvPath.getText().toString()); Bundle bundle = new Bundle(); Intent intent = new Intent(); bundle.putString("uri", mTvUri.getText().toString()); intent.putExtras(bundle); setResult(Activity.RESULT_OK, intent); finish(); } } }); } public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { if (requestCode == REQUEST_CODE) { if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { initListener(); } else { Toast.makeText(this, "您已拒绝授予权限", Toast.LENGTH_LONG).show(); } } } @Override protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { super.onActivityResult(requestCode, resultCode, data); //在Activity中的onActivityResult()方法里与LQRPhotoSelectUtils关联 mLqrPhotoSelectUtils.attachToActivityForResult(requestCode, resultCode, data); } }
3.xml界面
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <Button android:id="@+id/btn_take_photo" android:text="拍照" android:layout_width="match_parent" android:layout_height="wrap_content"/> <Button android:id="@+id/btn_select_pic" android:text="从图库中选取" android:layout_width="match_parent" android:layout_height="wrap_content"/> <TextView android:id="@+id/tvPath" android:layout_width="wrap_content" android:layout_height="50dp" android:text="图片路径:"/> <TextView android:id="@+id/tvUri" android:layout_width="wrap_content" android:layout_height="50dp" android:text="图片Uri:"/> <ImageView android:id="@+id/iv_pic" app:srcCompat="@drawable/head_portrait" android:layout_width="300dp" android:layout_height="300dp"/> <Button android:id="@+id/btn_finish" android:layout_width="match_parent" android:layout_height="50dp" android:text="完成"/> </LinearLayout>
演示: