一行代码实现安卓照片选取上传服务器,并适配安卓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
- 一行代码实现安卓照片选取上传服务器,并适配安卓7.0无法调起相机的问题
- 使用微信的 JS SDK 选取手机照片并进行上传,Iphone无法显示缩略图
- 微信JS-SDK选取手机照片上传并下载保存至自己的服务器
- 两句代码搞定安卓拍照,选取照片,截取照片的所有操作
- android 实现调用相机拍照 获取照片路径并上传
- 安卓 相机或相册图片上传至手机界面并显示 最后上传至服务器(界面xml布局代码前一个文章有)
- 安卓h5混合开发照片上传的问题
- 安卓webview上传照片的实现,2017最新总结
- Android调用相册或相机拍照选取照片并裁剪
- Android调用相册或相机拍照选取照片并裁剪
- Android调用相册或相机拍照选取照片并裁剪
- Android使用相机获取照片并显示的代码
- ionic 的项目实现从手机相册选取图片或拍照并上传至服务器
- 打开相册相机上传照片代码
- Android调取系统相册和相机照片设置到ImageView并上传到服务器
- 安卓实现调用系统图库与相机设置头像并保存在本地及服务器
- Android选取图片并上传的一种实现方式
- 安卓拍照和选择照片上传功能代码
- 设计Mysql索引的原则
- 关于Eclipse 环境搭建的问题
- List 循环删除多个元素
- 比AlphaGo更可怕!10大黑科技或将改变人类未来
- mysql 将时间戳直接转换成日期时间
- 一行代码实现安卓照片选取上传服务器,并适配安卓7.0无法调起相机的问题
- 滑动导航栏,标签页切换
- C++builder RTL与VCl
- spring @Entity @Table
- Linux下查看网卡驱动和版本信息
- Java入门学习-学习static的用法
- 在CentOS 7.2上部署Kubernetes集群
- 哈利·波特的考试
- C++算法学习——预备知识(2)——函数模板