Android 拍照以及相册中选择(适配高版本)————上传头像并裁剪(一)

前言
在项目研发中,相信大家都遇到过给用户增加头像照片的需求。
随着手机版本的不断更新,android 8、android 9、android 10、android 12、android 13、鸿蒙系统等等;遇到这个功能需求,大家肯定会想,“这还不好写? 之前就已经写过了。” 把老项目跑了一遍之后发现无法运行。要不大多数就会出现奔溃的情况!
这也就遇到常见的 高版本适配情况,以及针对不同版本该如何处理?
碰到这种情况也不要慌张,博主将为大家推出两篇热乎连载篇。从两个不同开发场景下,来给大家分享两篇文章,来更加详细的了解该如何去实现?【特此来记录】
本篇将为大家详细讲解如何调用摄像头拍照 & 选择相册,并裁剪图片。
效果
实测android 8、android 9、android 11、android 13、鸿蒙系统均有效;
手机机型分别为OPPO、华为、VIVO手机。
对于效果演示,将单独拿出两个来举例:
- VIVO android 13
- 华为 鸿蒙系统2.0.1
| vivo | 华为 |
|---|---|
![]() | ![]() |
功能
- 动态申请拍照,读,写权限
- 自定义弹出框
- 调用系统相机拍照
3.1 调用系统相机申请拍照权限回调
3.2 拍照完成回调 - 自动获取sdk权限
4.1 访问相册完成回调
具体实现
.gradle配置文件:

AndroidManifest文件:
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<providerandroid:name="androidx.core.content.FileProvider"android:authorities="com.harry.takepicture.provider"android:exported="false"android:grantUriPermissions="true"><meta-dataandroid:name="android.support.FILE_PROVIDER_PATHS"android:resource="@xml/filepaths" />
</provider>
filepaths.xml文件:
<?xml version="1.0" encoding="utf-8"?>
<paths><paths><external-pathname="camera_photos"path="" /></paths><external-path name="rc_external_path" path="."/></paths>
/*** @author 拉莫帅* @date 2023/4/01* @address* @Desc TakePicture 上传头像*/
public class MainActivity extends BaseActivity implements View.OnClickListener {public static String[] permission = {Manifest.permission.WRITE_EXTERNAL_STORAGE,Manifest.permission.ACCESS_COARSE_LOCATION,Manifest.permission.READ_EXTERNAL_STORAGE,Manifest.permission.CAMERA};private CircleImg img;private View view;private String BaseUrl = "";private Uri newUri;private File outputImage;private Uri cropImageUri;private File fileCropUri;//裁剪的照片private Uri imageUri;//拍照所得到的图像的保存路径private static final int OUTPUT_X = 295;private static final int OUTPUT_Y = 413;private static final int CODE_GALLERY_REQUEST = 0xa0;private static final int CODE_CAMERA_REQUEST = 0xa1;private static final int CODE_RESULT_REQUEST = 0xa2;private static final int REQUESTCODE_CUTTING = 0xa3;private static final int CAMERA_PERMISSIONS_REQUEST_CODE = 0x03;private static final int STORAGE_PERMISSIONS_REQUEST_CODE = 0x04;protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setStatusBg(1);AppUtils.requestPermission(permission);img = findViewById(R.id.userinfo_iv_head);img.setOnClickListener(this);}protected View addContentLayout() {view = getLayoutInflater().inflate(R.layout.activity_main, contentLayout, false);return view;}public void onClick(View v) {switch (v.getId()) {case R.id.userinfo_iv_head:select();break;case R.id.rl_head_camera:takePhoto();AppUtils.dismiss();break;case R.id.rl_head_photo:autoObtainStoragePermission();AppUtils.dismiss();break;case R.id.rl_head_cancel:AppUtils.dismiss();break;}}private void select() { AppUtils.selectPhoto(MainActivity.this, R.layout.dialog_head, R.layout.activity_main, this);}/*** 拍照** @param*/private void takePhoto() {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)) {ToastUtils.showShort(this, "您已经拒绝过一次");}ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA, Manifest.permission.READ_EXTERNAL_STORAGE}, CAMERA_PERMISSIONS_REQUEST_CODE);} else {//有权限直接调用系统相机拍照if (AppUtils.hasSdcard()) {outputImage = new File(Environment.getExternalStorageDirectory().getPath(), System.currentTimeMillis() + ".jpg");//通过FileProvider创建一个content类型的Uriif (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {imageUri = FileProvider.getUriForFile(this, "com.harry.takepicture.provider", outputImage);} else {imageUri = Uri.fromFile(outputImage);}PhotoUtils.takePicture(this, imageUri, CODE_CAMERA_REQUEST); } else {ToastUtils.showShort(this, "设备没有SD卡!");}}}public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {super.onRequestPermissionsResult(requestCode, permissions, grantResults);switch (requestCode) {//调用系统相机申请拍照权限回调case CAMERA_PERMISSIONS_REQUEST_CODE: {if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {if (AppUtils.hasSdcard()) {if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {imageUri = FileProvider.getUriForFile(MainActivity.this, "com.harry.takepicture.provider", outputImage);} else {imageUri = Uri.fromFile(outputImage);}PhotoUtils.takePicture(this, imageUri, CODE_CAMERA_REQUEST); } else {ToastUtils.showShort(this, "设备没有SD卡!");}} else {ToastUtils.showShort(this, "请允许打开相机!!");}break;}}}/*** 自动获取sdk权限*/private void autoObtainStoragePermission() {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);}}protected void onActivityResult(int requestCode, int resultCode, Intent data) {super.onActivityResult(requestCode, resultCode, data);if (resultCode == RESULT_OK) {fileCropUri = new File(Environment.getExternalStorageDirectory(), System.currentTimeMillis() + ".jpg");switch (requestCode) {//拍照完成回调case CODE_CAMERA_REQUEST:if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {Uri contentUri = FileProvider.getUriForFile(this, "com.harry.takepicture.provider", outputImage);cropPhoto(contentUri);} else {imageUri = Uri.fromFile(outputImage);cropPhoto(imageUri);}//访问相册完成回调case CODE_GALLERY_REQUEST:if (AppUtils.hasSdcard()) {cropImageUri = Uri.fromFile(fileCropUri);if (data == null) {return;} else {newUri = getUri(data);}PhotoUtils.cropImageUri(this, newUri, cropImageUri, 1, 1, OUTPUT_X, OUTPUT_Y, CODE_RESULT_REQUEST);} else {ToastUtils.showShort(this, "设备没有SD卡!");}break;case REQUESTCODE_CUTTING:if (data != null) {setPicToView(data);}break;case CODE_RESULT_REQUEST:String a = cropImageUri.getPath();Log.e("tb", "a---" + a);Bitmap bitmap = PhotoUtils.getBitmapFromUri(cropImageUri, this);if (bitmap != null) {File file = new File(a);Log.e("tb", "file--------------------------" + file);BaseUrl = Base64Utils.getImageStr(file);Log.e("tb", "BaseUrl--------------------------" + BaseUrl);showImages(bitmap, a);}break;default:}}}private void setPicToView(Intent picdata) {Bundle extras = picdata.getExtras();if (extras != null) {// 取得SDCard图片路径做显示Bitmap photo = extras.getParcelable("data");String saveFile = FileUtil.saveFile(this, "crop", photo);if (photo != null) {File file = new File(saveFile);Log.e("tb", "file--------------------------" + file);BaseUrl = Base64Utils.getImageStr(file);Log.e("tb", "BaseUrl--------------------------" + BaseUrl);showImages(photo, saveFile);}}}/*** 裁剪* * @param uri*/private void cropPhoto(Uri uri) {Intent intent = new Intent("com.android.camera.action.CROP");intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);intent.setDataAndType(uri, "image/*");intent.putExtra("crop", "true");intent.putExtra("aspectX", 1);intent.putExtra("aspectY", 1);intent.putExtra("outputX", 300);intent.putExtra("outputY", 300);intent.putExtra("return-data", true);Log.e("tag", "intent====" + intent);startActivityForResult(intent, REQUESTCODE_CUTTING);}/*** 展示* * @param bitmap* @param urlpath*/private void showImages(Bitmap bitmap, String urlpath) {Drawable drawable = new BitmapDrawable(null, bitmap);Log.e("tag", "urlPath====" + urlpath);img.setImageDrawable(drawable);}/*** 解决手机上获取图片路径为null的情况** @param intent* @return*/public Uri getUri(android.content.Intent intent) {Uri uri = intent.getData();String type = intent.getType();if (uri.getScheme().equals("file") && (type.contains("image/"))) {String path = uri.getEncodedPath();if (path != null) {path = Uri.decode(path);ContentResolver cr = this.getContentResolver();StringBuffer buff = new StringBuffer();buff.append("(").append(MediaStore.Images.ImageColumns.DATA).append("=").append("'" + path + "'").append(")");Cursor cur = cr.query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,new String[]{MediaStore.Images.ImageColumns._ID},buff.toString(), null, null);int index = 0;for (cur.moveToFirst(); !cur.isAfterLast(); cur.moveToNext()) {index = cur.getColumnIndex(MediaStore.Images.ImageColumns._ID);// set _id valueindex = cur.getInt(index);}if (index == 0) {// do nothing} else {Uri uri_temp = Uri.parse("content://media/external/images/media/"+ index);if (uri_temp != null) {uri = uri_temp;}}}}return uri;}
}
总结
到这里就结束了。看到这里,关于上传头像的具体流程也已经清楚,最主要的代码也已经给大家粘贴了过来。
完整版源码下载地址:Android + <调用相机拍照 & 选择相册> + 数码相机
感兴趣的小伙伴们,大家赶快去测试一下吧。
相关文章:
Android 拍照以及相册中选择(适配高版本)————上传头像并裁剪(一)
前言 在项目研发中,相信大家都遇到过给用户增加头像照片的需求。 随着手机版本的不断更新,android 8、android 9、android 10、android 12、android 13、鸿蒙系统等等;遇到这个功能需求,大家肯定会想,“这还不好写&…...
带你了解现在的LED显示屏技术
随着LED显示屏技术的空前繁荣,LED显示屏产品备受关注,广泛应用于商业广告、实况播映、交通诱导、舞台演绎等领域,发展至今。你了解十大中国LED显示屏制造商吗? LED显示屏技术已经得到了长足的发展,现在的LED显示屏技术…...
AI模型推理(1)——入门篇
前言 本文主要介绍AI模型推理的相关基础概念,为后续云原生模型推理服务的学习做准备。 初识模型部署 对于深度学习模型来说,模型部署指让训练好的模型在特定环境中运行的过程。相比于常规的软件部署,模型部署会面临更多的难题: …...
MySQL--表的基本查询--0410--15
目录 1. Create 1.1 insert 1.1.2 插入否则更新 1.2 replace 2.Retrieve 2.1 select 2.1.1 全列查询 2.1.2 指定列查询 2.1.3 查询字段为表达式 2.1.4 为查询结果指定名称 2.1.5 去重 2.2 where 2.2.1 > and > and < and < and 2.2.2 in between…...
Scala语言入门以及基本语法
文章目录 前言1.环境搭建1) IDEA中插件下载2) SDK下载配置 2.基本使用1)var与val的区别2) .基本数据类型3).字符串的基本用法4) 控制结构1) if else2) for 循环3) while循环 5)类6) 函数 前言 scala在一种简洁的高级语言中结合了面向对象和函数式编程。Scala的静态…...
Linux shell编程 循环语句for continue break
for循环是编程语言中一种循环语句 示例1:循环读取user.txt中的用户名,创建用户。设置密码。 for i in $(cat /opt/user.txt) douseradd $iecho 123456 | passwd --stdin $i done 示例2:循环读取ipaddr文本文件中地址,执行ping命令…...
leetcode 643. 子数组最大平均数 I
题目描述解题思路执行结果 leetcode 643. 子数组最大平均数 I 题目描述 子数组最大平均数 I 给你一个由 n 个元素组成的整数数组 nums 和一个整数 k 。 请你找出平均数最大且 长度为 k 的连续子数组,并输出该最大平均数。 任何误差小于 10-5 的答案都将被视为正确答…...
TDA4VM/VH 芯片硬件 mailbox
请从官网下载 TD4VM 技术参考手册,地址如下: TDA4VM 技术参考手册地址 概述 (Mailbox 的介绍在 TRM 的第7.1章节) Mailbox 使用邮箱中断机制实现了 VM 芯片的核间通信。 Mailbox 是集成在 NAVSS0 域下的一个外设(NAVSS0 的说明可以查看&a…...
如何利用Trimble RealWorks三维激光扫描仪进行外业测量和内业处理?
文章目录 0.引言1.Trimble RealWorks介绍2.外业测量3.内业处理 0.引言 笔者所在资源与环境工程学院实验室采购有一台Trimble RealWorks三维激光扫描仪(仪器名:Trimble TX8),因项目需要,在学校实验场地进行实地测量训练…...
mysql数据备份
数据备份分类 数据库的备份类型 完全备份:对整个数据库的数据进行备份部分备份:对部分数据进行备份(可以是一张表也可以是多张表) 增量备份:是以上一次备份为基础来备份变更数据的,节约空间差异备份&#x…...
排队接水--贪心
排队接水 题目描述 有 n n n 个人在一个水龙头前排队接水,假如每个人接水的时间为 T i T_i Ti,请编程找出这 n n n 个人排队的一种顺序,使得 n n n 个人的平均等待时间最小。 输入格式 第一行为一个整数 n n n。 第二行 n n n 个…...
数字温度传感器-DS18B20
文章目录 一、DS18B20器件图二、DS18B20特点三、DS18B20内部结构内部构成 四、工作时序1.初始化时序2.ReadOneChar2.WriteOneChar 一、DS18B20器件图 DS18B20的管脚排列: GND为电源地;DQ为数字信号输入/输出端;VDD为外接供电电源…...
【算法】【算法杂谈】从M个数中等概率的选出n个数,保证每一个数的选中概率都是n/m(蓄水池算法)
目录 前言问题介绍解决方案代码编写java语言版本c语言版本c语言版本 思考感悟写在最后 前言 当前所有算法都使用测试用例运行过,但是不保证100%的测试用例,如果存在问题务必联系批评指正~ 在此感谢左大神让我对算法有了新的感悟认识! 问题介…...
vue3+ts+vite自适应项目——路由、layout布局
系列文章目录 第一章:搭建项目 目录 系列文章目录 前言 一、vue-router 1.安装vue-router 2.引入 2.1 新建页面 2.2 公共样式引入 2.3 layout 布局 2.4路由配置 总结 前言 上一章我们搭建了项目,这一张主要讲路由和layout布局,和…...
数据库之约束、索引和事务
一、约束 约束,顾名思义就是数据库对数据库中的数据所给出的一组检验规则.负责判断元素是否符合数据库要求.其目的就是为了提高效率以及准确性. 1.not null - > 数据元素非空 表示如果插入数据,则当前数据不能为空. //创建一张学生表,其班级id和年级id不为空 create …...
centos --libreoffice使用
您可以按照以下步骤在CentOS上安装LibreOffice: 打开终端并使用root用户登录。 运行以下命令更新系统软件包: yum update安装LibreOffice依赖项: yum install -y libreoffice-headless libreoffice-writer libreoffice-calc libreoffice-…...
Steam-V Rising 私人服务器架设教程
一、安装前的准备 一台服务器 拥有公网IP并且做好了端口映射 二、使用SteamCMD安装服务器 1.下载SteamCMD SteamCMD是Steam专用的命令行式客户端程序,所有的安装方式可以参照:https://developer.valvesoftware.com/wiki/SteamCMD 或者在其他站点自行…...
SpringBoot+Vue3实现登录验证码功能
系列文章目录 Redis缓存穿透、击穿、雪崩问题及解决方法Spring Cache的使用–快速上手篇分页查询–Java项目实战篇全局异常处理–Java实战项目篇 Java实现发送邮件(定时自动发送邮件)_java邮件通知_心态还需努力呀的博客-CSDN博客 该系列文章持续更新…...
spring2:创建和使用
目录 1.创建Spring项目 1.1创建Maven类 1.2添加Spring支持框架 1.3添加启动类 2.存储Bean对象 2.0 spring项目中添加配置文件(第一次) 2.1创建Bean 2.2把Bean注册到容器中 3.获取并使用Bean对象 3.1创建上下文 3.2获取指定Bean对象 getBean()方法 --> 获取什么…...
前端如何处理后端一次性传来的10w条数据?
写在前面 如果你在面试中被问到这个问题,你可以用下面的内容回答这个问题,如果你在工作中遇到这个问题,你应该先揍那个写 API 的人。 创建服务器 为了方便后续测试,我们可以使用node创建一个简单的服务器。 const http requir…...
SciencePlots——绘制论文中的图片
文章目录 安装一、风格二、1 资源 安装 # 安装最新版 pip install githttps://github.com/garrettj403/SciencePlots.git# 安装稳定版 pip install SciencePlots一、风格 简单好用的深度学习论文绘图专用工具包–Science Plot 二、 1 资源 论文绘图神器来了:一行…...
云启出海,智联未来|阿里云网络「企业出海」系列客户沙龙上海站圆满落地
借阿里云中企出海大会的东风,以**「云启出海,智联未来|打造安全可靠的出海云网络引擎」为主题的阿里云企业出海客户沙龙云网络&安全专场于5.28日下午在上海顺利举办,现场吸引了来自携程、小红书、米哈游、哔哩哔哩、波克城市、…...
Yolov8 目标检测蒸馏学习记录
yolov8系列模型蒸馏基本流程,代码下载:这里本人提交了一个demo:djdll/Yolov8_Distillation: Yolov8轻量化_蒸馏代码实现 在轻量化模型设计中,**知识蒸馏(Knowledge Distillation)**被广泛应用,作为提升模型…...
R语言速释制剂QBD解决方案之三
本文是《Quality by Design for ANDAs: An Example for Immediate-Release Dosage Forms》第一个处方的R语言解决方案。 第一个处方研究评估原料药粒径分布、MCC/Lactose比例、崩解剂用量对制剂CQAs的影响。 第二处方研究用于理解颗粒外加硬脂酸镁和滑石粉对片剂质量和可生产…...
如何做好一份技术文档?从规划到实践的完整指南
如何做好一份技术文档?从规划到实践的完整指南 🌟 嗨,我是IRpickstars! 🌌 总有一行代码,能点亮万千星辰。 🔍 在技术的宇宙中,我愿做永不停歇的探索者。 ✨ 用代码丈量世界&…...
RushDB开源程序 是现代应用程序和 AI 的即时数据库。建立在 Neo4j 之上
一、软件介绍 文末提供程序和源码下载 RushDB 改变了您处理图形数据的方式 — 不需要 Schema,不需要复杂的查询,只需推送数据即可。 二、Key Features ✨ 主要特点 Instant Setup: Be productive in seconds, not days 即时设置 :在几秒钟…...
java 局域网 rtsp 取流 WebSocket 推送到前端显示 低延迟
众所周知 摄像头取流推流显示前端延迟大 传统方法是服务器取摄像头的rtsp流 然后客户端连服务器 中转多了,延迟一定不小。 假设相机没有专网 公网 1相机自带推流 直接推送到云服务器 然后客户端拉去 2相机只有rtsp ,边缘服务器拉流推送到云服务器 …...
XXE漏洞知识
目录 1.XXE简介与危害 XML概念 XML与HTML的区别 1.pom.xml 主要作用 2.web.xml 3.mybatis 2.XXE概念与危害 案例:文件读取(需要Apache >5.4版本) 案例:内网探测(鸡肋) 案例:执行命…...
稻米分类和病害检测数据集(猫脸码客第237期)
稻米分类图像数据集:驱动农业智能化发展的核心资源 引言 在全球农业体系中,稻米作为最关键的粮食作物之一,其品种多样性为人类饮食提供了丰富选择。然而,传统稻米分类方法高度依赖人工经验,存在效率低、主观性强等缺…...
【知识扫盲】分布式系统架构或分布式服务中的管理面,数据面和业务面
🧩 一、三大“面”的定义与职责(以大模型推理平台为例) 层级英文名职责关键组件举例数据面Data Plane处理用户请求、模型推理、输入输出数据转换等核心任务模型服务引擎、Tokenizer/Detokenizer、推理加速器(TensorRT、ONNX Runt…...


