Android 兼容Android 7拍摄照片/打开相册/选择照片/剪裁照片/显示照片 带demo

  • 前言
  • 效果
  • 上代码
    • 共享文件夹
    • 配置
    • 声明权限
    • 创建文件管理工具
    • 布局文件
    • 活动
    • 服务类
  • GitHub
  • 完事

前言

项目里需要给用户修改头像的功能。就需要能做到标题的功能。
先拟定需要完成的任务

/** * 首页 * ------------------------- * 1、点击拍照,先判断是否有相机权限和文件写入读取权限,没有就请求,有就打开相机 * 2、点击选择照片,先判断是否有文件读取权限,没有就请求,有就打开图册 * 3、拿到照片返回进行剪裁 * 4、剪裁成功后显示 * @author D10NG * @date on 2019-05-15 09:02 */

先上参考文章表示感谢
@那一夜_ ------ 适配Android7.0应用间文件共享FileProvider
@那一夜_ ------ android从相册选择图片和拍照选择图片

效果

GIF图

上代码

共享文件夹

首先在项目res目录下新建xml目录,并新建file_paths.xml,这个文件主要用来配置应用共享文件的路径

<?xml version="1.0" encoding="utf-8"?><paths xmlns:android="http://schemas.android.com/apk/res/android">    <root-path        name="root"        path="" />    <files-path        name="files"        path="." />    <cache-path        name="cache"        path="." />    <external-path        name="external"        path="." />    <external-files-path        name="external_file_path"        path="." />    <external-cache-path        name="external_cache_path"        path="." /></paths>

其中配置的path="."的意思就是应用可以使用整个目录

配置

在AndroidManifest.xml的application节点下增加FileProvider的声明

   <application        ...        <!-- 适配android 7.0文件访问 org.rydc.phototest是应用的包名 -->        <provider            android:name="android.support.v4.content.FileProvider"            android:authorities="org.rydc.phototest.fileprovider"            android:exported="false"            android:grantUriPermissions="true">            <meta-data                android:name="android.support.FILE_PROVIDER_PATHS"                android:resource="@xml/file_paths" />        </provider>    </application>

声明权限

    <!-- 相册读取 -->    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>    <!-- 拍照 -->    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>    <uses-permission android:name="android.permission.CAMERA"/>

创建文件管理工具

直接复制下面的即可

package org.rydc.phototest.utils;import android.annotation.SuppressLint;import android.content.ContentUris;import android.content.Context;import android.content.Intent;import android.content.pm.PackageManager;import android.content.pm.ResolveInfo;import android.database.Cursor;import android.net.Uri;import android.os.Build;import android.os.Environment;import android.provider.DocumentsContract;import android.provider.MediaStore;import android.util.Log;import java.io.File;import java.io.IOException;import java.util.List;/** * 适配Android 7 读取文件工具 * * @author D10NG * @date on 2019-05-15 08:45 */public class FileProviderUtils {    /**     * 获取文件的Uri     * @param context     * @param file     * @return     */    public static Uri getUriForFile(Context context, File file) {        Uri fileUri = null;        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {            fileUri = getUriForFile24(context, file);        } else {            fileUri = Uri.fromFile(file);        }        return fileUri;    }    /**     * Android 7 获取文件的Uri     * @param context     * @param file     * @return     */    private static Uri getUriForFile24(Context context, File file) {        return android.support.v4.content.FileProvider.getUriForFile(context,                context.getPackageName() + ".fileprovider", file);    }    /**     * 设定intent的data和type     * @param context     * @param intent     * @param type     * @param file     * @param writeAble     */    public static void setIntentDataAndType(Context context,                                            Intent intent,                                            String type,                                            File file,                                            boolean writeAble) {        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {            intent.setDataAndType(getUriForFile(context, file), type);            intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);            if (writeAble) {                intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);            }        } else {            intent.setDataAndType(Uri.fromFile(file), type);            // apk放在cache文件中,需要获取读写权限            chmod("777", file.getAbsolutePath());        }    }    /**     * 修改文件权限     * @param permission     * @param path     */    public static void chmod(String permission, String path) {        try {            String command = "chmod " + permission + " " + path;            Runtime runtime = Runtime.getRuntime();            runtime.exec(command);        } catch (IOException e) {            e.printStackTrace();        }    }    /**     * 设定intent的data     * @param context     * @param intent     * @param file     * @param writeAble     */    public static void setIntentData(Context context,                                     Intent intent,                                     File file,                                     boolean writeAble) {        if (Build.VERSION.SDK_INT >= 24) {            intent.setData(getUriForFile(context, file));            intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);            if (writeAble) {                intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);            }        } else {            intent.setData(Uri.fromFile(file));        }    }    /**     * 授予权限     * @param context     * @param intent     * @param uri     * @param writeAble     */    public static void grantPermissions(Context context, Intent intent, Uri uri, boolean writeAble) {        int flag = Intent.FLAG_GRANT_READ_URI_PERMISSION;        if (writeAble) {            flag |= Intent.FLAG_GRANT_WRITE_URI_PERMISSION;        }        intent.addFlags(flag);        List<ResolveInfo> resInfoList = context.getPackageManager()                .queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);        for (ResolveInfo resolveInfo : resInfoList) {            String packageName = resolveInfo.activityInfo.packageName;            context.grantUriPermission(packageName, uri, flag);        }    }    /**     * 根据URI获取文件真实路径(兼容多机型)     * @param context     * @param uri     * @return     */    public static String getFilePathByUri(Context context, Uri uri) {        // 判断uri的标头是 content 还是 file,分别用不同的方法处理        if ("content".equalsIgnoreCase(uri.getScheme())) {            int sdkVersion = Build.VERSION.SDK_INT;            if (sdkVersion >= 19) {                // api >= 19                return getRealPathFromUriAboveApi19(context, uri);            } else {                // api < 19                return getRealPathFromUriBelowAPI19(context, uri);            }        } else if ("file".equalsIgnoreCase(uri.getScheme())) {            return uri.getPath();        }        return null;    }    /**     * 适配api19及以上,根据uri获取图片的绝对路径     *     * @param context 上下文对象     * @param uri     图片的Uri     * @return 如果Uri对应的图片存在, 那么返回该图片的绝对路径, 否则返回null     */    @SuppressLint("NewApi")    private static String getRealPathFromUriAboveApi19(Context context, Uri uri) {        String filePath = null;        if (DocumentsContract.isDocumentUri(context, uri)) {            // 如果是document类型的 uri, 则通过document id来进行处理            String documentId = DocumentsContract.getDocumentId(uri);            if (isMediaDocument(uri)) { // MediaProvider                // 使用':'分割                String type = documentId.split(":")[0];                String id = documentId.split(":")[1];                String selection = MediaStore.Images.Media._ID + "=?";                String[] selectionArgs = {id};                // 判断文件类型                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;                }                filePath = getDataColumn(context, contentUri, selection, selectionArgs);            } else if (isDownloadsDocument(uri)) {                // DownloadsProvider                Uri contentUri = ContentUris.withAppendedId(                        Uri.parse("content://downloads/public_downloads"),                        Long.valueOf(documentId));                filePath = getDataColumn(context, contentUri, null, null);            }else if (isExternalStorageDocument(uri)) {                // ExternalStorageProvider                final String docId = DocumentsContract.getDocumentId(uri);                final String[] split = docId.split(":");                final String type = split[0];                if ("primary".equalsIgnoreCase(type)) {                    filePath = Environment.getExternalStorageDirectory() + "/" + split[1];                }            }else {                Log.e("FileHandlerUtil", "路径错误");            }        } else if ("content".equalsIgnoreCase(uri.getScheme())) {            // 如果是 content 类型的 Uri            filePath = getDataColumn(context, uri, null, null);        } else if ("file".equals(uri.getScheme())) {            // 如果是 file 类型的 Uri,直接获取图片对应的路径            filePath = uri.getPath();        }        return filePath;    }    /**     * 适配api19以下(不包括api19),根据uri获取图片的绝对路径     *     * @param context 上下文对象     * @param uri     图片的Uri     * @return 如果Uri对应的图片存在, 那么返回该图片的绝对路径, 否则返回null     */    private static String getRealPathFromUriBelowAPI19(Context context, Uri uri) {        return getDataColumn(context, uri, null, null);    }    /**     * 获取数据库表中的 _data 列,即返回Uri对应的文件路径     *     * @return     */    private static String getDataColumn(Context context, Uri uri, String selection, String[] selectionArgs) {        String path = null;        String[] projection = new String[]{MediaStore.Images.Media.DATA};        Cursor cursor = null;        try {            cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, null);            if (cursor != null && cursor.moveToFirst()) {                int columnIndex = cursor.getColumnIndexOrThrow(projection[0]);                path = cursor.getString(columnIndex);            }        } catch (Exception e) {            if (cursor != null) {                cursor.close();            }        }        return path;    }    /**     * @param uri the Uri to check     * @return Whether the Uri authority is MediaProvider     */    private static boolean isMediaDocument(Uri uri) {        return "com.android.providers.media.documents".equals(uri.getAuthority());    }    private static boolean isExternalStorageDocument(Uri uri) {        return "com.android.externalstorage.documents".equals(uri.getAuthority());    }    /**     * @param uri the Uri to check     * @return Whether the Uri authority is DownloadsProvider     */    private static boolean isDownloadsDocument(Uri uri) {        return "com.android.providers.downloads.documents".equals(uri.getAuthority());    }}

布局文件

activity_main.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"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:orientation="vertical"    tools:context=".MainActivity">    <Button        android:id="@+id/btn_take_photo"        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:text="拍照" />    <Button        android:id="@+id/btn_choose_photo"        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:text="从相册获取" />    <ImageView        android:id="@+id/img_photo"        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:src="@drawable/ic_launcher_background" /></LinearLayout>

活动

MainActivity.java

package org.rydc.phototest;import android.content.Context;import android.content.Intent;import android.content.pm.PackageManager;import android.net.Uri;import android.support.annotation.NonNull;import android.support.annotation.Nullable;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.widget.Toast;import org.rydc.phototest.utils.FileProviderUtils;/** * 首页 * ------------------------- * 1、点击拍照,先判断是否有相机权限和文件写入读取权限,没有就请求,有就打开相机 * 2、点击选择照片,先判断是否有文件读取权限,没有就请求,有就打开图册 * 3、拿到照片返回进行剪裁 * 4、剪裁成功后显示 * @author D10NG * @date on 2019-05-15 09:02 */public class MainActivity extends AppCompatActivity {    private Context mContext = this;    private MainService mService;    public static final int RC_CHOOSE_PHOTO = 10;    public static final int RC_TAKE_PHOTO = 11;    public static final int RC_CROP_PHOTO = 12;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        mService = new MainService(mContext);        setContentView(mService.mView.getView());    }    @Override    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {        super.onActivityResult(requestCode, resultCode, data);        if (resultCode != RESULT_OK) {            Toast.makeText(mContext, "操作取消", Toast.LENGTH_SHORT).show();            return;        }        switch (requestCode) {            case RC_CHOOSE_PHOTO:                if (null == data) {                    Toast.makeText(mContext, "没有拿到图片", Toast.LENGTH_SHORT).show();                    return;                }                Uri uri = data.getData();                if (null == uri) {                    Toast.makeText(mContext, "没有拿到图片路径", Toast.LENGTH_SHORT).show();                    return;                }                // 剪裁图片                mService.cropPhoto(FileProviderUtils.getFilePathByUri(mContext, uri), 200);                break;            case RC_TAKE_PHOTO:                // 剪裁图片                mService.cropPhoto(mService.tempPhotoPath, 200);                break;            case RC_CROP_PHOTO:                // 显示图片                mService.showPhoto(mService.cropImgUri);                break;        }    }    @Override    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {        boolean allPass = true;        for (int i = 0; i < permissions.length; i++) {            if (grantResults[i] != PackageManager.PERMISSION_GRANTED) {                allPass = false;            }        }        if (!allPass) {            Toast.makeText(mContext, "没有获得相应权限", Toast.LENGTH_SHORT).show();            return;        }        switch (requestCode) {            case RC_CHOOSE_PHOTO:                // 继续去打开图册                mService.choosePhoto();                break;            case RC_TAKE_PHOTO:                // 继续去拍照                mService.takePhoto();                break;        }    }}

服务类

MainService.java

package org.rydc.phototest;import android.Manifest;import android.annotation.SuppressLint;import android.app.Activity;import android.content.Context;import android.content.Intent;import android.content.pm.PackageManager;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.net.Uri;import android.os.Environment;import android.os.Handler;import android.os.Message;import android.provider.MediaStore;import android.support.v4.app.ActivityCompat;import android.support.v4.content.ContextCompat;import android.text.TextUtils;import android.util.Log;import org.rydc.phototest.utils.FileProviderUtils;import java.io.File;/** * @author D10NG * @date on 2019-05-15 09:15 */public class MainService {    private Context mContext;    public MainView mView;    /** 拍照输出真实路径 */    public String tempPhotoPath;    /** 剪裁输出uri路径 */    public final Uri cropImgUri = Uri.parse("file:///"+Environment.getExternalStorageDirectory()+"/photo_crop.jpg");    public static final int CLICK_VIEW = 1;    @SuppressLint("HandlerLeak")    private Handler mHandler = new Handler() {        @Override        public void handleMessage(Message msg) {            switch (msg.what) {                case CLICK_VIEW:                    // 页面控件点击事件                    switch (msg.arg1) {                        case R.id.btn_take_photo:                            takePhoto();                            break;                        case R.id.btn_choose_photo:                            choosePhoto();                            break;                    }                    break;            }        }    };    public MainService(Context context) {        mContext = context;        mView = new MainView(mContext, mHandler);    }    /**     * 打开相机     */    public void takePhoto() {        if (ContextCompat.checkSelfPermission(mContext, Manifest.permission.READ_EXTERNAL_STORAGE)                != PackageManager.PERMISSION_GRANTED ||                ContextCompat.checkSelfPermission(mContext, Manifest.permission.WRITE_EXTERNAL_STORAGE)                != PackageManager.PERMISSION_GRANTED ||                ContextCompat.checkSelfPermission(mContext, Manifest.permission.CAMERA)                != PackageManager.PERMISSION_GRANTED) {            // 未授权,申请授权            ActivityCompat.requestPermissions((Activity)mContext,                    new String[]{Manifest.permission.READ_EXTERNAL_STORAGE,                            Manifest.permission.WRITE_EXTERNAL_STORAGE,                            Manifest.permission.CAMERA},                    MainActivity.RC_TAKE_PHOTO);            return;        }        // 已授权        Intent intentToTakePhoto = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);        // 设置照片输出位置        File photoFile = new File(Environment.getExternalStorageDirectory(), "photo.jpg");        tempPhotoPath = photoFile.getAbsolutePath();        Uri tempImgUri = FileProviderUtils.getUriForFile(mContext, photoFile);        intentToTakePhoto.putExtra(MediaStore.EXTRA_OUTPUT, tempImgUri);        ((Activity)mContext).startActivityForResult(intentToTakePhoto, MainActivity.RC_TAKE_PHOTO);    }    /**     * 选图     */    public void choosePhoto() {        if (ContextCompat.checkSelfPermission(mContext, Manifest.permission.READ_EXTERNAL_STORAGE)                != PackageManager.PERMISSION_GRANTED) {            // 未授权,申请授权(从相册选择图片需要读取存储卡的权限)            ActivityCompat.requestPermissions((Activity)mContext,                    new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},                    MainActivity.RC_CHOOSE_PHOTO);            return;        }        // 已授权,获取照片        Intent intentToPickPic = new Intent(Intent.ACTION_PICK, null);        intentToPickPic.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*");        ((Activity)mContext).startActivityForResult(intentToPickPic, MainActivity.RC_CHOOSE_PHOTO);    }    /**     * 剪裁图片     *     * @param path     * @param size     */    public void cropPhoto(String path, int size) {        Intent intent = new Intent("com.android.camera.action.CROP");        FileProviderUtils.setIntentDataAndType(mContext, intent, "image/*", new File(path), true);        intent.putExtra("crop", "true");        intent.putExtra("aspectX", 1);        intent.putExtra("aspectY", 1);        intent.putExtra("outputX", size);        intent.putExtra("outputY", size);        intent.putExtra("scale", true);        intent.putExtra("return-data", false);        intent.putExtra(MediaStore.EXTRA_OUTPUT, cropImgUri);        intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());        ((Activity)mContext).startActivityForResult(intent, MainActivity.RC_CROP_PHOTO);    }    /**     * 显示图片     */    public void showPhoto(Uri uri) {        String path = FileProviderUtils.getFilePathByUri(mContext, uri);        Log.e("main", "path=" + path);        if (!TextUtils.isEmpty(path)) {            // 从文件路径读取文件            Bitmap bitmap = BitmapFactory.decodeFile(path);            mView.setImgPhoto(bitmap);        } else {            Log.e("main", "没有图片");        }    }}

GitHub

demo:D10NGYANG/photoTest
欢迎提供建议或意见

完事

更多相关文章

  1. Android(安卓)合并AAR踩坑之旅
  2. Android将so库封装到jar包中并加载其中的so库
  3. Android(安卓)性能优化之Loading Big Bitmaps
  4. android和ios,音频互通方案
  5. android音乐播放器-------使用android系统自带的数据库
  6. Android的.9.png图片分析
  7. 我的Android进阶之旅------>Android使用9Patch图片作为不失真背
  8. 工程中导入sqlite -sqlite 基础教程(1)
  9. 解决 Android(安卓)Studio 创建项目时极其的慢的尴尬

随机推荐

  1. ICS4.0.3 将声音设置中的震动和响铃默认
  2. Android笔记之adb命令应用实例1(手机端与
  3. Android的几种分辨率
  4. Android组合控件自定义标题栏
  5. 在android 中调用其他的功能
  6. Android Studio 配置输出apk的名字
  7. android fragment 面试
  8. android通过图片名称获取资源识别码
  9. android中使用static、application、本地
  10. Android G711A编解码