一行代码实现安卓照片选取上传服务器,并适配安卓7.0无法调起相机的问题

来源:互联网 发布:更改淘宝密码 编辑:程序博客网 时间:2024/04/26 02:52

最近在开发时需要实现头像更换功能,这里就要用到拍照和相册选取功能了。但是在安卓7.0的手机上遇到无法打开相机的问题。下面就针对7.0做了一些处理优化。

1,原因分析

Android升级到7.0后对权限又做了一个更新即不允许出现以file://的形式调用隐式APP,需要用共享文件的形式:content:// URI

2,解决方案

下面是打开系统相机的方法,做了android各个版本的兼容:

 public void takePhoto() {        File imgFile = new File(imgPath);        if (!imgFile.getParentFile().exists()) {            imgFile.getParentFile().mkdirs();        }        Uri imgUri = null;       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);    }

上面只是简单的一段代码,下面我把优化之后,并且支持一下功能

1,适配7.0拍照

2,相册选取图片

3,可以设置是否进行裁剪

4,通过glide实时显示选中的图片

5,如果用户拒绝了权限,可以给提示框进到用户跳到权限设置页

下面是效果图



下面贴出主要实现代码

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 Activity 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(Activity 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(Activity 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);    }    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 == Activity.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);    }}

简单使用
package com.takephoto_android70;import android.Manifest;import android.content.DialogInterface;import android.content.Intent;import android.net.Uri;import android.os.Bundle;import android.support.annotation.NonNull;import android.support.v7.app.AlertDialog;import android.support.v7.app.AppCompatActivity;import android.view.View;import android.widget.Button;import android.widget.ImageView;import android.widget.TextView;import com.bumptech.glide.Glide;import java.io.File;import kr.co.namee.permissiongen.PermissionFail;import kr.co.namee.permissiongen.PermissionGen;import kr.co.namee.permissiongen.PermissionSuccess;public class MainActivity extends AppCompatActivity {    private Button mBtnTakePhoto;    private Button mBtnSelectPhoto;    private TextView mTvPath;    private TextView mTvUri;    private LQRPhotoSelectUtils mLqrPhotoSelectUtils;    private ImageView mIvPic;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        mBtnTakePhoto = (Button) findViewById(R.id.btnTakePhoto);        mBtnSelectPhoto = (Button) findViewById(R.id.btnSelectPhoto);        mTvPath = (TextView) findViewById(R.id.tvPath);        mTvUri = (TextView) findViewById(R.id.tvUri);        mIvPic = (ImageView) findViewById(R.id.ivPic);        init();        initListener();    }    private void init() {        // 1、创建LQRPhotoSelectUtils(一个Activity对应一个LQRPhotoSelectUtils)        mLqrPhotoSelectUtils = new LQRPhotoSelectUtils(this, new LQRPhotoSelectUtils.PhotoSelectListener() {            @Override            public void onFinish(File outputFile, Uri outputUri) {                // 4、当拍照或从图库选取图片成功后回调                mTvPath.setText(outputFile.getAbsolutePath());                mTvUri.setText(outputUri.toString());                Glide.with(MainActivity.this).load(outputUri).into(mIvPic);            }        }, false);//true裁剪,false不裁剪        //        mLqrPhotoSelectUtils.setAuthorities("com.lqr.lqrnativepicselect.fileprovider");        //        mLqrPhotoSelectUtils.setImgPath(Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + String.valueOf(System.currentTimeMillis()) + ".jpg");    }    private void initListener() {        mBtnTakePhoto.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                // 3、调用拍照方法                PermissionGen.with(MainActivity.this)                        .addRequestCode(LQRPhotoSelectUtils.REQ_TAKE_PHOTO)                        .permissions(Manifest.permission.READ_EXTERNAL_STORAGE,                                Manifest.permission.WRITE_EXTERNAL_STORAGE,                                Manifest.permission.CAMERA                        ).request();            }        });        mBtnSelectPhoto.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                // 3、调用从图库选取图片方法                PermissionGen.needPermission(MainActivity.this,                        LQRPhotoSelectUtils.REQ_SELECT_PHOTO,                        new String[]{Manifest.permission.READ_EXTERNAL_STORAGE,                                Manifest.permission.WRITE_EXTERNAL_STORAGE}                );            }        });    }    @PermissionSuccess(requestCode = LQRPhotoSelectUtils.REQ_TAKE_PHOTO)    private void takePhoto() {        mLqrPhotoSelectUtils.takePhoto();    }    @PermissionSuccess(requestCode = LQRPhotoSelectUtils.REQ_SELECT_PHOTO)    private void selectPhoto() {        mLqrPhotoSelectUtils.selectPhoto();    }    @PermissionFail(requestCode = LQRPhotoSelectUtils.REQ_TAKE_PHOTO)    private void showTip1() {        //        Toast.makeText(getApplicationContext(), "不给我权限是吧,那就别玩了", Toast.LENGTH_SHORT).show();        showDialog();    }    @PermissionFail(requestCode = LQRPhotoSelectUtils.REQ_SELECT_PHOTO)    private void showTip2() {        //        Toast.makeText(getApplicationContext(), "不给我权限是吧,那就别玩了", Toast.LENGTH_SHORT).show();        showDialog();    }    @Override    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {        PermissionGen.onRequestPermissionsResult(this, requestCode, permissions, grantResults);    }    @Override    protected void onActivityResult(int requestCode, int resultCode, Intent data) {        super.onActivityResult(requestCode, resultCode, data);        // 2、在Activity中的onActivityResult()方法里与LQRPhotoSelectUtils关联        mLqrPhotoSelectUtils.attachToActivityForResult(requestCode, resultCode, data);    }    public void showDialog() {        //创建对话框创建器        AlertDialog.Builder builder = new AlertDialog.Builder(this);        //设置对话框显示小图标        builder.setIcon(android.R.drawable.ic_dialog_alert);        //设置标题        builder.setTitle("权限申请");        //设置正文        builder.setMessage("在设置-应用-虎嗅-权限 中开启相机、存储权限,才能正常使用拍照或图片选择功能");        //添加确定按钮点击事件        builder.setPositiveButton("去设置", new DialogInterface.OnClickListener() {//点击完确定后,触发这个事件            @Override            public void onClick(DialogInterface dialog, int which) {                //这里用来跳到手机设置页,方便用户开启权限                Intent intent = new Intent(android.provider.Settings.ACTION_MANAGE_WRITE_SETTINGS);                intent.setData(Uri.parse("package:" + MainActivity.this.getPackageName()));                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);                startActivity(intent);            }        });        //添加取消按钮点击事件        builder.setNegativeButton("取消", new DialogInterface.OnClickListener() {            @Override            public void onClick(DialogInterface dialog, int which) {            }        });        //使用构建器创建出对话框对象        AlertDialog dialog = builder.create();        dialog.show();//显示对话框    }}

布局文件:

activity_main.xml

<?xml version="1.0" encoding="utf-8"?><LinearLayout    xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:orientation="vertical"    >    <Button        android:id="@+id/btnTakePhoto"        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:text="拍照"/>    <Button        android:id="@+id/btnSelectPhoto"        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:text="从图库中选取"/>    <TextView        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:text="图片路径:"/>    <TextView        android:id="@+id/tvPath"        android:layout_width="wrap_content"        android:layout_height="wrap_content"/>    <TextView        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:text="图片Uri:"/>    <TextView        android:id="@+id/tvUri"        android:layout_width="wrap_content"        android:layout_height="wrap_content"/>    <ImageView        android:id="@+id/ivPic"        android:layout_width="match_parent"        android:layout_height="wrap_content"/></LinearLayout>

最后一定不要忘记配置权限

<?xml version="1.0" encoding="utf-8"?><manifest package="com.takephoto_android70"          xmlns:android="http://schemas.android.com/apk/res/android">    <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"/>    <application        android:allowBackup="true"        android:icon="@mipmap/ic_launcher"        android:label="@string/app_name"        android:supportsRtl="true"        android:theme="@style/AppTheme">        <activity android:name=".MainActivity">            <intent-filter>                <action android:name="android.intent.action.MAIN"/>                <category android:name="android.intent.category.LAUNCHER"/>            </intent-filter>        </activity>    </application></manifest>


GitHub源码地址:https://github.com/qiushi123/TakePhoto_android7.0


阅读全文
0 0
原创粉丝点击