首先是权限
<!-- 拍照权限 --> <uses-permission android:name="android.permission.CAMERA" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-feature android:name="android.hardware.camera" /> <uses-feature android:name="android.hardware.camera.autofocus" /> <uses-permission android:name="android.permission.WRITE_CALL_LOG" /> <uses-permission android:name="android.permission.CALL_PHONE" /> <uses-permission android:name="android.permission.READ_CALL_LOG" /> <uses-permission android:name="android.permission.READ_PHONE_STATE" /> <uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS" /> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
首先是布局,首页的布局,很简单,就是两个按钮和一个图片
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" android:id="@+id/llImage" > <Button android:id="@+id/button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="选择图片" /> <Button android:id="@+id/button2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="拍照图片" /> <ImageView android:id="@+id/image" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerVertical="true" android:layout_centerHorizontal="true" android:scaleType="centerCrop" android:layout_margin="10dp"/> </LinearLayout>
然后是封装的工具类,调用照相和相册,以及裁剪等
/** * 工具类,用来执行调用相机拍照和图库,裁剪等 * Created by Administrator on 2018/3/21. */ public class PhotoUtils { private static final String TAG = "PhotoUtils"; /** * @param activity 当前activity * @param imageUri 拍照后照片存储路径 * @param requestCode 调用系统相机请求码 */ public static void takePicture(Activity activity, Uri imageUri, int requestCode) { //调用系统相机 Intent intentCamera = new Intent(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { //添加这一句表示对目标应用临时授权该Uri所代表的文件 intentCamera.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); } intentCamera.setAction(MediaStore.ACTION_IMAGE_CAPTURE); //将拍照结果保存至photo_file的Uri中,不保留在相册中 intentCamera.putExtra(MediaStore.EXTRA_OUTPUT, imageUri); activity.startActivityForResult(intentCamera, requestCode); } /** * @param activity 当前activity * @param requestCode 打开相册的请求码 */ public static void openPic(Activity activity, int requestCode) { Intent photoPickerIntent = new Intent(Intent.ACTION_GET_CONTENT); photoPickerIntent.setType("image/*"); activity.startActivityForResult(photoPickerIntent, requestCode); } /** * @param activity 当前activity * @param orgUri 剪裁原图的Uri * @param desUri 剪裁后的图片的Uri * @param aspectX X方向的比例 * @param aspectY Y方向的比例 * @param width 剪裁图片的宽度 * @param height 剪裁图片高度 * @param requestCode 剪裁图片的请求码 */ public static void cropImageUri(Activity activity, Uri orgUri, Uri desUri, int aspectX, int aspectY, int width, int height, int requestCode) { Intent intent = new Intent("com.android.camera.action.CROP"); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); } intent.setDataAndType(orgUri, "image/*"); intent.putExtra("crop", "true"); intent.putExtra("aspectX", aspectX); intent.putExtra("aspectY", aspectY); intent.putExtra("outputX", width); intent.putExtra("outputY", height); intent.putExtra("scale", true); intent.putExtra("scaleUpIfNeeded", true); //将剪切的图片保存到目标Uri中 intent.putExtra(MediaStore.EXTRA_OUTPUT, desUri); intent.putExtra("return-data", false); intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString()); intent.putExtra("noFaceDetection", true); activity.startActivityForResult(intent, requestCode); } /** * 读取uri所在的图片 * * @param uri 图片对应的Uri * @param mContext 上下文对象 * @return 获取图像的Bitmap */ public static Bitmap getBitmapFromUri(Uri uri, Context mContext) { try { Bitmap bitmap = MediaStore.Images.Media.getBitmap(mContext.getContentResolver(), uri); return bitmap; } catch (Exception e) { e.printStackTrace(); return null; } } /** * @param context 上下文对象 * @param uri 当前相册照片的Uri * @return 解析后的Uri对应的String */ @SuppressLint("NewApi") public static String getPath(final Context context, final Uri uri) { final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT; String pathHead = "file:///"; // DocumentProvider if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) { // ExternalStorageProvider if (isExternalStorageDocument(uri)) { final String docId = DocumentsContract.getDocumentId(uri); final String[] split = docId.split(":"); final String type = split[0]; if ("primary".equalsIgnoreCase(type)) { return pathHead + Environment.getExternalStorageDirectory() + "/" + split[1]; } } // DownloadsProvider else if (isDownloadsDocument(uri)) { final String id = DocumentsContract.getDocumentId(uri); final Uri contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"), Long.valueOf(id)); return pathHead + getDataColumn(context, contentUri, null, null); } // MediaProvider else if (isMediaDocument(uri)) { final String docId = DocumentsContract.getDocumentId(uri); final String[] split = docId.split(":"); final String type = split[0]; Uri contentUri = null; if ("image".equals(type)) { contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI; } else if ("video".equals(type)) { contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI; } else if ("audio".equals(type)) { contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI; } final String selection = "_id=?"; final String[] selectionArgs = new String[]{split[1]}; return pathHead + getDataColumn(context, contentUri, selection, selectionArgs); } } // MediaStore (and general) else if ("content".equalsIgnoreCase(uri.getScheme())) { return pathHead + getDataColumn(context, uri, null, null); } // File else if ("file".equalsIgnoreCase(uri.getScheme())) { return pathHead + uri.getPath(); } return null; } /** * Get the value of the data column for this Uri. This is useful for * 获取此Uri的数据列的值。这是有用的 * MediaStore Uris, and other file-based ContentProviders. *mediastore URI,和基于内容提供商的其他文件 * * @param context The context.上下文 * @param uri The Uri to query. uri路径 * @param selection (Optional) Filter used in the query.(可选的)滤波器中使用的查询。 * @param selectionArgs (Optional) Selection arguments used in the query.(可选)查询中使用的选择参数。 * @return The value of the _data column, which is typically a file path. 该_data列的值,这是一个典型的文件路径。 */ private static String getDataColumn(Context context, Uri uri, String selection, String[] selectionArgs) { Cursor cursor = null; final String column = "_data"; final String[] projection = {column}; try { cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, null); if (cursor != null && cursor.moveToFirst()) { final int column_index = cursor.getColumnIndexOrThrow(column); return cursor.getString(column_index); } } finally { if (cursor != null) cursor.close(); } return null; } /** * @param uri The Uri to check. * @return Whether the Uri authority is ExternalStorageProvider. * 不管Uri我自己的存储路径(百度翻译自行理解) */ private static boolean isExternalStorageDocument(Uri uri) { return "com.android.externalstorage.documents".equals(uri.getAuthority()); } /** * @param uri The Uri to check.要检查的URI。 * @return Whether the Uri authority is DownloadsProvider. */ private static boolean isDownloadsDocument(Uri uri) { return "com.android.providers.downloads.documents".equals(uri.getAuthority()); } /** * @param uri The Uri to check. * @return Whether the Uri authority is MediaProvider. * Uri当局是否是媒体提供者? */ private static boolean isMediaDocument(Uri uri) { return "com.android.providers.media.documents".equals(uri.getAuthority()); } }
随后,就是在主类中进行调用,而大部分7.0的权限需要自己申请,由用户同意,而不是向4.0以下那种,给了权限你就是大爷。
我直接将整个类贴出来,包括对图片的压缩与转码成base64,base64大部分都是用来将图片上传到后台的,所以可以根据个人需求来取消。
public class MainActivity extends AppCompatActivity implements View.OnClickListener{ private static int STORAGE_PERMISSIONS_REQUEST_CODE = 0x04; private static final int CODE_RESULT_REQUEST = 0xa2; private static final int CODE_GALLERY_REQUEST = 0xa0; private static final int CODE_CAMERA_REQUEST = 0xa1; private static final int CAMERA_PERMISSIONS_REQUEST_CODE = 0x03; private ImageView imageVie; private Uri cropImageUri; private Button button; private Uri imageUri; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); imageVie = findViewById(R.id.image); button = findViewById(R.id.button); Button button2=findViewById(R.id.button2); imageVie.setOnClickListener(this); button.setOnClickListener(this); button2.setOnClickListener(this); } @Override public void onClick(View view) { switch (view.getId()){ case R.id.image: break; case R.id.button: //调用相册授权 autoObtainCameraPermission(); break; case R.id.button2://拍照 autoObtainStoragePermission(); break; } } //自动获取相机权限 private void autoObtainCameraPermission(){ if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE)!= PackageManager.PERMISSION_GRANTED){ ActivityCompat.requestPermissions(this,new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},STORAGE_PERMISSIONS_REQUEST_CODE ); }else { PhotoUtils.openPic(this,CODE_GALLERY_REQUEST); } } //返回值回掉 private static final int OUTPUT_X = 480; private static final int OUTPUT_Y = 480; @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); switch (requestCode){ //拍照完成回调 case CODE_CAMERA_REQUEST: cropImageUri = Uri.fromFile(new File(Environment.getExternalStorageDirectory().getPath() + "/crop_photo"+ UUID.randomUUID()+".jpg")); if (imageUri!=null&&cropImageUri!=null){ //---------------------不裁剪图片,也就是调用系统,可以自定义大小的裁剪框,上传证书啊之类的----------------------------------------------------------------------------------------------------------------- Intent intent = new Intent("com.android.camera.action.CROP"); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); } intent.setDataAndType(imageUri, "image/*"); intent.putExtra("scale", true); intent.putExtra("scaleUpIfNeeded", true); //将剪切的图片保存到目标Uri中 intent.putExtra(MediaStore.EXTRA_OUTPUT, cropImageUri); intent.putExtra("return-data", false); intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString()); intent.putExtra("noFaceDetection", true); startActivityForResult(intent, CODE_RESULT_REQUEST); //---------------------------规定大小裁剪图片,只能选择放大或缩小图片,裁剪固定范围,图片无法完全展示,头像什么的没有问题--------------------------------------------------------------------------------------------------- // PhotoUtils.cropImageUri(this, imageUri, cropImageUri, 1, 1, OUTPUT_X, OUTPUT_Y, CODE_RESULT_REQUEST); } break; //调用系统裁剪等回调 case CODE_RESULT_REQUEST: Bitmap bitmap = PhotoUtils.getBitmapFromUri(cropImageUri, this); if (bitmap != null) { InputStream input=null; try { input = getContentResolver().openInputStream(cropImageUri); int available = input.available(); Log.e("LOG",(available/1024/1024)+"m"); if (available/1024/1024<4){ String path = cropImageUri.getPath(); //二次采样,通过长宽对图片进行压缩,减小内存,图片尺寸越大,压缩的越模糊 Bitmap bitmap1 = getBitmap(path, 600, 600); //转为base64,上传后台或自行处理 showImages(bitmap1); //展示压缩后的图片,可以去掉 imageVie.setImageBitmap(bitmap1); }else { Toast.makeText(this, "图片不能超过4M", Toast.LENGTH_SHORT).show(); } input.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } break; case CODE_GALLERY_REQUEST: if (hasSdcard()) { if (data!=null){ cropImageUri = Uri.fromFile(new File(Environment.getExternalStorageDirectory().getPath() + "/crop_photo"+UUID.randomUUID()+".jpg")); Uri newUri = Uri.parse(PhotoUtils.getPath(this, data.getData())); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { newUri = FileProvider.getUriForFile(this, "text.jdyx.com.jdyx.fileprovider", new File(newUri.getPath())); } //------------------------不裁剪图片,也就是调用系统,可以自定义大小的裁剪框,上传证书啊之类的----------------------------------------------------------------------------------------------------------------- Intent intent = new Intent("com.android.camera.action.CROP"); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); } intent.setDataAndType(newUri, "image/*"); intent.putExtra("scale", true); intent.putExtra("scaleUpIfNeeded", true); //将剪切的图片保存到目标Uri中 intent.putExtra(MediaStore.EXTRA_OUTPUT, cropImageUri); intent.putExtra("return-data", false); intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString()); intent.putExtra("noFaceDetection", true); startActivityForResult(intent, CODE_RESULT_REQUEST); //------------------------------规定大小裁剪图片,只能选择放大或缩小图片,裁剪固定范围,图片无法完全展示,头像什么的没有问题------------------------------------------------------------------------------------------------------------- // PhotoUtils.cropImageUri(this, newUri, cropImageUri, 1, 1, OUTPUT_X, OUTPUT_Y, CODE_RESULT_REQUEST);//跳转裁剪图片 } } else { Toast.makeText(this, "设备没有SD卡!", Toast.LENGTH_SHORT).show(); } break; } } /** * 检查设备是否存在SDCard的工具方法 */ public static boolean hasSdcard() { String state = Environment.getExternalStorageState(); return state.equals(Environment.MEDIA_MOUNTED); } /** * @param filePath 要加载的图片路径 * @param destWidth 显示图片的控件宽度 * @param destHeight 显示图片的控件的高度 * @return */ public Bitmap getBitmap(String filePath, int destWidth, int destHeight) { //第一次采样 BitmapFactory.Options options = new BitmapFactory.Options(); //该属性设置为true只会加载图片的边框进来,并不会加载图片具体的像素点 options.inJustDecodeBounds = true; //第一次加载图片,这时只会加载图片的边框进来,并不会加载图片中的像素点 BitmapFactory.decodeFile(filePath, options); //获得原图的宽和高 int outWidth = options.outWidth; int outHeight = options.outHeight; //定义缩放比例 int sampleSize = 1; while (outHeight / sampleSize > destHeight || outWidth / sampleSize > destWidth) { //如果宽高的任意一方的缩放比例没有达到要求,都继续增大缩放比例 // /mpleSize应该为2的n次幂,如果给sampleSize设置的数字不是2的n次幂,那么系统会就近取值 sampleSize *= 2; } /********************************************************************************************/ //至此,第一次采样已经结束,我们已经成功的计算出了sampleSize的大小 /********************************************************************************************/ //二次采样开始 //二次采样时我需要将图片加载出来显示,不能只加载图片的框架,因此inJustDecodeBounds属性要设置为false options.inJustDecodeBounds = false; //设置缩放比例 options.inSampleSize = sampleSize; options.inPreferredConfig = Bitmap.Config.ARGB_8888; //加载图片并返回 return BitmapFactory.decodeFile(filePath, options); } //可以在这里执行向后台上传图片base64 private void showImages(Bitmap bitmap) { // 压缩图片,此为质量压缩,但是为了保证上传的清晰度,不进行压缩,同时转为base64上传到后台 ByteArrayOutputStream onputStream = new ByteArrayOutputStream(); bitmap.compress(Bitmap.CompressFormat.JPEG, 100, onputStream); byte[] bytes = onputStream.toByteArray(); //转为base64 String base64 = Base64.encodeToString(bytes, Base64.DEFAULT); Log.e("TAG","Base64:"+base64); JSONObject jsonObjectUpImage = new JSONObject(); try { String newUriStr = cropImageUri.toString(); //判断图片格式,选择保存 String imageType = newUriStr.substring(newUriStr.length() - 3, newUriStr.length()).trim(); if (imageType.equals("jpg")){ jsonObjectUpImage.put("contentType","JPG"); } if (imageType.equals("png")){ jsonObjectUpImage.put("contentType","PNG"); } if (imageType.equals("jpeg")){ jsonObjectUpImage.put("contentType","JPEG"); } if (imageType.equals("gif")){ jsonObjectUpImage.put("contentType","GIF"); } } catch (JSONException e) { e.printStackTrace(); } } //拍照,自动获取存储权限 private void autoObtainStoragePermission() { if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED || ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CAMERA)) { Toast.makeText(this, "您已经拒绝过一次", Toast.LENGTH_SHORT).show(); } ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA, Manifest.permission.READ_EXTERNAL_STORAGE}, CAMERA_PERMISSIONS_REQUEST_CODE); } else {//有权限直接调用系统相机拍照 if (hasSdcard()) { imageUri = Uri.fromFile(new File(Environment.getExternalStorageDirectory().getPath() + "/photo"+ UUID.randomUUID()+".jpg")); //通过FileProvider创建一个content类型的Uri if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { imageUri = FileProvider.getUriForFile(MainActivity.this, "text.jdyx.com.jdyx.fileprovider", new File(Environment.getExternalStorageDirectory().getPath() + "/photo"+UUID.randomUUID()+".jpg")); } PhotoUtils.takePicture(this, imageUri, CODE_CAMERA_REQUEST); } else { Toast.makeText(this, "设备没有SD卡", Toast.LENGTH_SHORT).show(); } } } }