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华为

功能

  1. 动态申请拍照,读,写权限
  2. 自定义弹出框
  3. 调用系统相机拍照
    3.1 调用系统相机申请拍照权限回调
    3.2 拍照完成回调
  4. 自动获取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" />
<provider
      android:name="androidx.core.content.FileProvider"
      android:authorities="com.harry.takepicture.provider"
      android:exported="false"
      android:grantUriPermissions="true">
          <meta-data
             android:name="android.support.FILE_PROVIDER_PATHS"
             android:resource="@xml/filepaths" />
</provider>

filepaths.xml文件:

<?xml version="1.0" encoding="utf-8"?>
<paths>
    <paths>
        <external-path
            name="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类型的Uri
                    if (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 value
                    index = 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 + <调用相机拍照 & 选择相册> + 数码相机

       感兴趣的小伙伴们,大家赶快去测试一下吧。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/334260.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

一键AI智能改写,自动修改文章效率高

在当今信息爆炸的时代&#xff0c;写作成为了人们日常生活和工作中不可或缺的一部分。不论是学生写作业&#xff0c;还是职场人士起草报告&#xff0c;都需要投入大量的时间和精力来构思和修改文章。然而&#xff0c;随着科技的不断进步&#xff0c;一项新的技术正在改变着写作…

HTML111111111

在线编辑器 在线 HTML 空元素 没有内容的 HTML 元素被称为空元素。空元素是在开始标签中关闭的。 即使 在所有浏览器中都是有效的&#xff0c;但使用 其实是更长远的保障。 HTML 水平线 标签在 HTML 页面中创建水平线。 hr 元素可用于分隔内容。 HTML 折行 如果您希望…

免费的爬虫软件【2024最新】

在国际市场竞争日益激烈的背景下&#xff0c;国外网站的SEO排名直接关系到网站在搜索引擎中的曝光度和用户点击量。良好的SEO排名能够带来更多的有针对性的流量&#xff0c;提升网站的知名度和竞争力。 二、国外网站SEO排名的三种方法 关键词优化&#xff1a; 关键词优化是SEO…

Java基础面试题-2day

面向对象 创建一个对象用什么运算符&#xff0c;对象实体和对象引用有什么不同&#xff1f; 创建对象使用new String A new String(); A即为对象引用&#xff0c;通过new运算符&#xff0c;创建String()类型的对象实体。 对象引用的存储位置在栈内存 对象实体的存储位置在堆…

分布式事务Seata实战-AT模式(注册中心为Eureka)

大致记录Seata的AT模式下创建项目过程中需要注意的点和可能遇到的问题。 本项目是以官网的给的示例&#xff08;即下图&#xff09;进行创建的&#xff0c;以Eureka为注册中心。 官网&#xff1a;Seata AT 模式 | Apache Seata™ 官方代码示例&#xff1a; 快速启动 | Apac…

C++编写、生成、调用so库详解(一)

开发中经常会用到so库,大多是调用第三方的so库,偶尔也需要自己封装一个so库给别人调用,这边就记录一下开发so库的一个过程. 首先我们这边是在Android Studio中开发的,所以仅描述在Android环境下开发过程,当然也可以用其他工具开发. 目录 1.第一步新建项目,配置需要的工具 2…

MFTCoder 重磅升级 v0.3.0 发布,支持 Mixtral 等更多模型,支持收敛均衡,支持 FSDP

1. MFTCoder 简介 CodeFuse在2023年9月开源了一种多任务微调框架——MFTCoder&#xff0c;它可以实现在多个任务上同时并行地进行微调。通过结合多种损失函数&#xff0c;我们有效地解决了多任务学习中常见的任务间数据量不平衡、难易不一和收敛速度不一致等挑战。大量实验结果…

『Open3D』1.10 Tensor数据处理

open3d中实现了自身的数据类型,用于open3d中内部算法的数值计算,但基础使用上与numpy类似。 目录 1、tensor创建 2、tensor数据属性 3、 Tensor数据在CPU与GPU上的转换

An incompatible version [1.2.33] of the Apache Tomcat Native library is installed

ERROR&#xff1a;An incompatible version [1.2.33] of the Apache Tomcat Native library is installed, while Tomcat requires version [1.2.34] 意为&#xff1a;安装了不兼容的Apache Tomcat原生库版本[1.2.33]&#xff0c;而Tomcat需要的版本[1.2.34] ERROR 14496 ---…

工业相机与镜头参数及选型

文章目录 1、相机成像系统模型1.1 视场1.2 成像简化模型 2、工业相机参数2.1 分辨率2.2 靶面尺寸2.3 像元尺寸2.4 帧率/行频2.5 像素深度2.6 动态范围2.7 信噪比2.8 曝光时间2.9 相机接口 3、工业镜头参数3.1 焦距3.2 光圈3.3 景深3.4 镜头分辨率3.5 工作距离&#xff08;Worki…

表的增删改查 进阶(二)

&#x1f3a5; 个人主页&#xff1a;Dikz12&#x1f525;个人专栏&#xff1a;MySql&#x1f4d5;格言&#xff1a;那些在暗处执拗生长的花&#xff0c;终有一日会馥郁传香欢迎大家&#x1f44d;点赞✍评论⭐收藏 目录 3.新增 4.查询 聚合查询 聚合函数 GROUP BY子句 HA…

【GitHub项目推荐--一键换脸】【转载】

FaceSwap 是一种利用深度学习算法来换掉图片和视频中的人脸的工具。基于 Tensorflow、Keras 和 Python&#xff0c;Faceswap 可以在 Windows、macOS 和 Linux 上运行。 安装了这个应用&#xff0c;你就能在你电脑上通过可视化交互的方式构建自己的换脸模型了。 地址&#xff…

时序分解 | Matlab实现CEEMDAN+PE自适应噪声完备集合经验模态分解+排列熵计算

时序分解 | Matlab实现CEEMDANPE自适应噪声完备集合经验模态分解排列熵计算 目录 时序分解 | Matlab实现CEEMDANPE自适应噪声完备集合经验模态分解排列熵计算效果一览基本介绍程序设计参考资料 效果一览 基本介绍 CEEMDANPE自适应噪声完备集合经验模态分解排列熵计算 运行环境m…

第六回 花和尚倒拔垂杨柳 豹子头误入白虎堂-安装服务器管理面板AMH和cyberpanel

且说鲁智深踏入菜园之时&#xff0c;二三十个泼皮无赖正聚集于此&#xff0c;他们手持果盒酒礼&#xff0c;脸上嬉皮笑脸&#xff0c;口称前来庆贺。然而&#xff0c;当这群人走到粪窖边缘&#xff0c;打头阵的张三和李四竟妄想搬动鲁智深&#xff0c;结果却被他轻描淡写地一脚…

element中表格组件的row-class-name和class-name属性的使用以及无效处理

1.这两个属性的使用&#xff0c;row-class-name用在el-table标签上&#xff0c;class-name用在el-table-column标签上。两个属性即可绑定类名也可绑定函数 <!-- 这里是绑定函数&#xff0c;也可以绑定类名 --> <el-table :data"tableData" selection-chang…

【C++】string的基本使用

从这篇博客开始&#xff0c;我们的C部分就进入到了STL&#xff0c;STL的出现可以说是C发展历史上非常关键的一步&#xff0c;自此C和C语言有了较为明显的差别。那么什么是STL呢&#xff1f; 后来不断的演化&#xff0c;发展成了知名的两个版本&#xff0c;一个叫做P.J.版本&am…

Liunx:线程控制

目录 创建线程&#xff1a;pthread_create(); 线程等待&#xff1a;pthread_join(); 线程退出&#xff1a;pthread_exit(); 线程取消&#xff1a;pthread_cancel() 说线程的时候说过&#xff0c;liunx没有选择单独定义线程的数据结构和适配算法&#xff0c;而是用轻量级进程…

关于java的继承

关于java的继承 我们在上一篇文章中&#xff0c;了解到了封装&#xff0c;我们本篇文章来介绍一下面向对象的第二大特点&#xff0c;继承&#xff0c;还是遵循结合现实生活中的实际情况&#xff0c;理解着去学习&#xff0c;能更好的加深印象&#x1f600;。 一、继承 继承的…

【从零开始学习Java重要知识 | 第三篇】暴打ReentrantLock底层源码

目录 前言&#xff1a; 前置知识&#xff1a; 什么是公平锁与非公平锁&#xff1f; 尝试自己构造一把锁&#xff1a; ReentrantLock源码&#xff1a; 加锁&#xff1a; 解锁&#xff1a; 总结&#xff1a; 前言&#xff1a; 在并发编程中&#xff0c;线程安全是一个重…