Android 设置头像 - 相册拍照

    Android开发在个人信息管理中,如果设置头像,一般都提供了从相册选择和拍照两种方式。下午将针对设置用户头像相册和拍照两种方式的具体实现进行详细说明。

    在实际实现过程中需要使用到权限管理,新版本的Android需要动态申请权限,权限的相关内容参考: Android 设置头像 - 权限申请一文。

界面实现

    根据分析,个人头像设置的界面中需要使用到ImageView、底部选择框两种组件进行实现。实现效果如下图所示,在该界面中底部选择框使用了XPopup组件库提供的底部选择框组件。
在这里插入图片描述

registerForActivityResult介绍

    registerForActivityResult()是startActivityForResult()的替代,简化了数据回调的写法。并且目前在新版本的Android开发中,官方建议弃用startActivityForResult()方法。因此本demo的实现过程中将采用registerForActivityResult进行实现。
    我们都知道startActivityForResult是实现activity切换回调的一个方法,在使用这个旧方法的过程中我们需要传入一个intent并且在activity中配置回调接收事件。与之不同的是,在使用registerForActivityResult方法时并不会设计到intent的调用,该方法将返回一个ActivityResultLauncher(activity结果启动器)。该方法如下代码所示:

    @NonNull
    @Override
    public final <I, O> ActivityResultLauncher<I> registerForActivityResult(
            @NonNull ActivityResultContract<I, O> contract,
            @NonNull ActivityResultCallback<O> callback) {
        return registerForActivityResult(contract, mActivityResultRegistry, callback);
    }

    通过代码最终可知,最终该方法将调用register方法进行注册结果转化器和回调还是,register方法如下:

 /**
  * Register a new callback with this registry.
  *
  * This is normally called by a higher level convenience methods like
  * {@link ActivityResultCaller#registerForActivityResult}.
  *
  * @param key a unique string key identifying this call
  * @param lifecycleOwner a {@link LifecycleOwner} that makes this call.
  * @param contract the contract specifying input/output types of the call
  * @param callback the activity result callback
  *
  * @return a launcher that can be used to execute an ActivityResultContract.
  */
 @NonNull
 public final <I, O> ActivityResultLauncher<I> register(
         @NonNull final String key,
         @NonNull final LifecycleOwner lifecycleOwner,
         @NonNull final ActivityResultContract<I, O> contract,
         @NonNull final ActivityResultCallback<O> callback

    从上述源码中不难看出在调用registerForActivityResult方法时需要传入一个结果转化器(ActivityResultContract)和处理回调函数(ActivityResultCallback)。
    ActivityResultContract的源码如下:

/**
 * A contract specifying that an activity can be called with an input of type [I]
 * and produce an output of type [O].
 *
 * Makes calling an activity for result type-safe.
 *
 * @see androidx.activity.result.ActivityResultCaller
 */
abstract class ActivityResultContract<I, O> {
    /**
     * Create an intent that can be used for [android.app.Activity.startActivityForResult].
     * 将intent进行加工,可以通过这个方法加过传入的intent
     */
    abstract fun createIntent(context: Context, input: I): Intent

    /**
     * Convert result obtained from [android.app.Activity.onActivityResult] to [O].
     * 加工结果返回的intent
     */
    abstract fun parseResult(resultCode: Int, intent: Intent?): O

    /**
     * An optional method you can implement that can be used to potentially provide a result in
     * lieu of starting an activity.
     *
     * @return the result wrapped in a [SynchronousResult] or `null` if the call
     * should proceed to start an activity.
     */
    open fun getSynchronousResult(context: Context, input: I): SynchronousResult<O>? {
        return null
    }

    /**
     * The wrapper for a result provided in [getSynchronousResult]. This allows differentiating
     * between a null [T] synchronous result and no synchronous result at all.
     */
    class SynchronousResult<T>(val value: T)
}

    在该抽象类中createIntent方法和parseResult方法尤为重要,一个对acticity跳转时的intent进行加工,一个对跳回时携带的intent进行处理。在这里需要说明的时,当我们进行拍照时,如果自己设定了一个uri(当我们指定文件名的时候需要自己设定这个uri)时,跳回携带的intent中将不再携带uri,及intent.getData()方法将返回null。
    ActivityResultCallback的源码如下:


/**
 * A type-safe callback to be called when an {@link Activity#onActivityResult activity result}
 * is available.
 *
 * @param <O> result type
 */
public interface ActivityResultCallback<O> {

    /**
     * Called when result is available
     */
    void onActivityResult(@SuppressLint("UnknownNullness") O result);
}

    在开发的过程中我们需要实现onActivityResult进行接收处理跳回携带的intent数据。
通过以上简单说明可以梳理出以下逻辑:

  • 在oncreate方法中调用 ActivityResultLauncher resultLauncher = registerForActivityResult(new TakeImageAndVideoUri(), callback);进行获取ActivityResultLauncher对象,该方法的调用建议在oncreate方法中进行,引用该方法将使用到ActivityResultRegistry对象。
  • 在点击拍照或者图库的过程中调用resultLauncher.launch(intent);进行界面跳转
  • 跳转的过程中android会自动调用你实现的TakeImageAndVideoUri对象中的createIntent方法进行intent加工。
  • 用户进行拍照或者选择图片并进行跳回
  • 跳回的过程中android将自动调用你实现的TakeImageAndVideoUri对象中的parseResult方法进行结果判断,以及携带intent加工
  • 最后将调用callback对象中的onActivityResult方法进行intent数据获取和处理。

    以上内容为registerForActivityResult()方法的简单说明,在学习了解该方法的过程中并未进行深入源码或官网学习,如描述存在问题欢迎斧正。

图片选择实现

intent跳转

    无论图库中选择图片还是拍照,其实都是通过intent配置进行实现的,该部分代码如下

        binding.headSculptureLayout.setOnClickListener(e->{
            BottomListPopupView popupView = new XPopup.Builder(PersonalInformationActivity.this)
                    .asBottomList("", ImageSelectSourceEnums.getLabels().toArray(new String[0]),
                            (position, text) -> {
                                if (text.equals(ImageSelectSourceEnums.PHOTO.getLabel())) {
                                    resultLauncher.launch(new Intent(MediaStore.ACTION_IMAGE_CAPTURE));
                                } else {
                                    Intent intent = new Intent(Intent.ACTION_PICK);
                                    intent.setType("image/*");
                                    resultLauncher.launch(intent);
                                }
                            });
            TextView cancel = popupView.findViewById(com.lxj.xpopup.R.id.tv_cancel);
            cancel.setText("取消");
            popupView.show();
        });

    代码中定义了一个点击事件,当点击headSculptureLayout布局时将进行BottomListPopupView底部选择视图的弹出,在该视图中存在两个选择【拍照】和【图库】,并配置了不同item的点击事件。及点击不同的选项将进行不同的intent配置,然后通过resultLauncher(ActivityResultLauncher的对象,在oncreate方法中通过registerForActivityResult获取的)进行跳转。
    在此额外补充一点,如果是选择进行录像则对intent进行如下配置:

Intent intent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
// 设置图像质量
intent.putExtra(MediaStore.EXTRA_VIDEO_QUALITY, 1);
resultLauncher.launch(intent);

TakeImageAndVideoUri的实现

    ActivityResultContract的实现类Android已经默认提供了很多种,针对图片处理的也提供了一种。

    但为了需求的定制化,在这里我自己实现了一个ActivityResultContract的实现类TakeImageAndVideoUri。
    该实现类的源码如下:

/**
 * 拍照、录像、选择图库的ActivityResultContract
 *
 * @author baiyang
 * @since 2024-04-27
 */
public class TakeImageAndVideoUri extends ActivityResultContract<Intent, Intent> {
    private Uri uri;
    private Bundle bundle;
    private String type;
    private String action;
    public static final String IMAGE_TYPE = "image/jpeg";
    public static final String VIDEO_TYPE = "video/*";
    public static final String JPG_TYPE = "jpg";
    public static final String MP4_TYPE = "mp4";
    public static final String TYPE = "type";
    /**
    * 设置为你自己的AUTHORITY 
    */
    public static final String AUTHORITY = "com.**.***.provider";

    @NonNull
    @Override
    public Intent createIntent(@NonNull Context context, Intent input) {
        action = input.getAction();
        String mimeType = null;
        String fileName = null;
        Uri mediaUri = null;
        if (MediaStore.ACTION_IMAGE_CAPTURE.equals(action)) {
            mimeType = IMAGE_TYPE;
            fileName = System.currentTimeMillis() + "." + JPG_TYPE;
            mediaUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
            type = JPG_TYPE;
        } else if (MediaStore.ACTION_VIDEO_CAPTURE.equals(action)) {
            mimeType = VIDEO_TYPE;
            fileName = System.currentTimeMillis() + "." + MP4_TYPE;
            mediaUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
            type = MP4_TYPE;
        } else if(Intent.ACTION_PICK.equals(action)){
            type = JPG_TYPE;
            bundle = input.getBundleExtra("bundle");
            return input;
        }
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            ContentValues values = new ContentValues();
            values.put(MediaStore.MediaColumns.DISPLAY_NAME, fileName);
            values.put(MediaStore.MediaColumns.MIME_TYPE, mimeType);
            values.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DCIM);
            uri = context.getContentResolver()
                    .insert(mediaUri, values);
        } else {
            uri = FileProvider.getUriForFile(context, AUTHORITY,
                    new File(context.getExternalCacheDir().getAbsolutePath(), fileName));
        }
        input.putExtra(MediaStore.EXTRA_OUTPUT, uri);
        bundle = input.getBundleExtra("bundle");
        return input;
    }

    /**
     * 返回拍照结果,因为在调用相机的过程中设置了EXTRA_OUTPUT,因此返回时intent=null,需要重新设置一下
     *
     * @param resultCode
     * @param intent
     * @return
     */
    @Override
    public Intent parseResult(int resultCode, @Nullable Intent intent) {
        if (resultCode != Activity.RESULT_OK) {
            return null;
        }
        if(Intent.ACTION_PICK.equals(action)){
            intent.putExtra(MediaStore.EXTRA_OUTPUT, intent.getData());
            intent.putExtra(TYPE, type);
            return intent;
        }
        intent = new Intent();
        intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
        intent.putExtra(TYPE, type);
        intent.putExtra("bundle", bundle);
        return intent;
    }
}

在这里需要说明的有以下几点

  • 一旦在createIntent方法中给intent设置了MediaStore.EXTRA_OUTPUT,则在parseResult方法中返回的intent则无法通过getData获取uri。
  • createIntent方法和parseResult方法参数intent并不是同一个对象
  • 在该实现类中使用了Bundle bundle进行intent携带额外参数的实现
  • 在进行拍照、录像、选择相册的过程中需要权限认证,实现类中的参数AUTHORITY 需要和你在AndroidManifest.xml中配置的提供者provider中的android:authorities一直,及如下代码
        <provider
            android:name="androidx.core.content.FileProvider"
            android:authorities="com.***.***.provider"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/file_paths" />
        </provider>

    其中file_paths为配置的资源访问范围xml配置文件。该部分内容可自行百度了解。

  • 代码中拍照和录像都配置了uri,指定了文件名称,但是从图库中选择为进行uri的配置,因此当intent的action为ACTION_PICK时,parseResult方法中intent已经携带了uri,因此无需再进行设置

ActivityResultCallback的实现

    通过上述方法,已经实现了拍照、图库的界面跳转和回挑过程,并且在回跳的时已经携带了我们需要的参数数据。因此我们需要实现ActivityResultCallback进行数据的业务逻辑处理。例如图片回显、裁剪、上传等操作。目前我仅仅实现了简单的回显功能,并且是通过Glide工具进行回显的。代码如下:

    /**
     * 回调
     */
    private final ActivityResultCallback<Intent> callback = result -> {
        if (Objects.nonNull(result)) {
            Uri uri = result.getParcelableExtra(MediaStore.EXTRA_OUTPUT);
            String type = result.getStringExtra(TakeImageAndVideoUri.TYPE);
            Glide.with(getApplicationContext()).load(uri).into(binding.headSculpture);
        } else {
            LogUtils.e("拍照、录像数据回调失败,未回传相关数据");
        }
    };

    后续我将实现图片的裁剪、上传等业务逻辑,可见其他文章。

总结

    本文主要阐述了android通过图库和拍照两种方法设置头像的功能实现。在具体实现过程中主要使用了以下技术:

  • XPopup的BottomListPopupView实现底部选择试图
  • ActivityResultLauncher对象、 registerForActivityResult()方法、TakeImageAndVideoUri(ActivityResultContract实现类型)和ActivityResultCallback实现类;分别进行intent跳转,intent输入输出配置以及回跳回调。
  • Glide 本地图片回显,后续还将通过该工具进行网络图片回显
  • 在布局方面主要使用了ConstraintLayout、LinearLayoutCompat、RelativeLayout三种组合布局。

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

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

相关文章

rabbitmq下载安装最新版本--并添加开机启动图文详解!!

一、简介 RabbitMQ是一个开源的遵循AMQP协议实现的消息中间件支持多种客户端语言,用于分布式系统中存储和转发消息, 这是 Release RabbitMQ 3.13.0 rabbitmq/rabbitmq-server GitHub 二、安装前准备 1、查看自己系统 确认操作系统版本兼容性 uname -a2、下载Erlang依赖包…

【12580无线通信技术】第十一章 Ad hoc网络无线通信技术期末复习自考复习

第十一章 Ad hoc网络无线通信技术 P283&#xff08;名词&#xff09;Ad hoc技术&#xff1a;是一种特定的无线网络结构&#xff0c;强调的是多跳、自组织、无中心的概念。P285&#xff08;简答&#xff09;Ad hoc网络的特点:①自组织和无中心特性&#xff1b;②网络拓补动态变…

SpringCloud系列(20)--Ribbon的简介及使用

1、Ribbon的简介 Spring Cloud Ribbon是基于Netflix Ribboh实现的一套客户端负载均衡的工具&#xff0c;简单的说&#xff0c;Ribbon是Netflix发布的开源项目&#xff0c;主要功能是提供客户端的软件负载均衡算法和服务调用。Ribbon客户端组件提供一系列完善的配置项如连接超时…

学习100个Unity Shader (14) ---透明效果

文章目录 渲染队列透明度测试&#xff08;Alpha Test&#xff09;效果Shader 透明度混合&#xff08;Alpha Blending&#xff09;效果Shader 参考 渲染队列 由”Queue“ 标签决定&#xff0c;索引号越小越早被渲染&#xff1a; 名称队列索引号Background1000Geometry2000Alph…

论文阅读之MMSD2.0: Towards a Reliable Multi-modal Sarcasm Detection System

文章目录 论文地址主要内容主要贡献模型图技术细节数据集改进多视图CLIP框架文本视图图像视图图像-文本交互视图 实验结果 论文地址 https://arxiv.org/pdf/2307.07135 主要内容 这篇文章介绍了一个名为MMSD2.0的多模态讽刺检测系统的构建&#xff0c;旨在提高现有讽刺检测系…

通过大模型(LLM)的多模态辩论的恶意表情包识别

Towards Explainable Harmful Meme Detection through Multimodal Debate between Large Language Models https://arxiv.org/abs/2401.13298https://arxiv.org/abs/2401.13298 1.概论 对于恶意表情包的识别,以往的研究方法没有能够深入表情包所隐含的复杂意义和文化背景,因…

vue-manage-system 更新,后台管理系统开发更简单

vue-manage-system 近期进行了一次版本升级&#xff0c;主要是支持了更多功能、升级依赖版本和优化样式&#xff0c;并且上线了官方文档网站&#xff0c;大部分功能都有文档或者使用示例&#xff0c;更加适合新手上手开发&#xff0c;只需要根据实际业务简单修改&#xff0c;就…

用fgets()替换fscanf()解决文件读取在小熊猫C++失败问题

fscanf&#xff08;&#xff09;遇到空格就结束读取&#xff0c;导致文件读取数据没完就退出读取以至于不能导入游戏地图工程。 看看到右侧小方块轨迹知晓采样区移动情况 也已经实现摄像机追随玩家效果 // 程序&#xff1a;2D RPG 地图编辑器与摄像机追随 // 作者&#xff1…

C语言自定义类型【联合体与枚举】

文章目录 1.联合体1.1联合体的声明1.2联合体的特点1.3联合体的大小计算联合体的使用案例 2.枚举2.1枚举类型的声明2.2枚举类型的优点(为什么使用枚举)2.3枚举类型的使用 结语 1.联合体 1.1联合体的声明 和结构体一样&#xff0c;联合体也是由一个或多个成员构成&#xff0c;同…

如何在 Visual Studio 中通过 NuGet 添加包

在安装之前要先确定Nuget的包源是否有问题。 Visual Studio中怎样更改Nuget程序包源-CSDN博客 1.图形界面安装 打开您的项目&#xff0c;并在解决方案资源管理器中选择您的项目。单击“项目”菜单&#xff0c;然后选择“管理 NuGet 程序包”选项。在“NuGet 包管理器”窗口中…

CTF(web方向)--md5的“===”和“==”的绕过

一、PHP弱类型说明 1.简介 php是一种弱类型语言&#xff0c;对数据的类型要求并不严格&#xff0c;可以让数据类型互相转换。 在php中有两种比较符号: 一种是 &#xff0c;另外一种是 &#xff0c;都是用来比较两个数值是否相等的操作符&#xff0c;但他们也是有区别的: &a…

大数据架构相关知识总结

一、大数据处理系统架构特性 1. 鲁棒性和容错性&#xff1a; 系统必须对游bug的程序写入的错误数据游足够的适应能力 2. 低延迟读取和更新能力 3. 横向扩容&#xff1a; 可以通过增加机器数量来维持性能 4. 通用性&#xff1a; 需要支持绝大多数应用程序 5. 延展性&#xff1a;…

前端工程化Vue使用Node.js设置国内高速npm镜像源(踩坑记录版)

前端工程化Vue使用Node.js设置国内高速npm镜像源&#xff08;踩坑记录版&#xff09; 此篇仅为踩坑记录&#xff0c;并未成功更换高速镜像源&#xff0c;实际解决方法见文末跳转链接。 1.自身源镜像 自身镜像源创建Vue项目下载速度感人 2.更改镜像源 2.1 通过命令行配置 前提…

【工作】程序员工作压力八个常见来源与建议缓解压力小窍门

目录 ​编辑 一. 程序员工作压力八个常见来源与建议 1&#xff09;目标职位不对 2&#xff09;工作任务描述不清晰 3&#xff09;快节奏的工作环境 4&#xff09;项目后期突然被添加新的要求 5&#xff09;计划外的工作事务会打断并破坏注意力 6&#xff09;个人问题 7…

MySQL第一次作业

解压完安装包 以管理员进入命令行 初始化并记住初始随机密码 创建服务名称 启动mysql 使用随机密码登录 修改密码 退出并重登服务器 MySQL创建数据库和表 创建数据库 创建表 1.进入数据库 创建表 向表中插入数据

鸿蒙OpenHarmony【小型系统 编译】(基于Hi3516开发板)

编译 OpenHarmony支持hb和build.sh两种编译方式。此处介绍hb方式&#xff0c;build.sh脚本编译方式请参考[使用build.sh脚本编译源码]。 使用build.sh脚本编译源码 进入源码根目录&#xff0c;执行如下命令进行版本编译。 ./build.sh --product-name name --ccache 说明&…

[Java EE] 多线程(四):线程安全问题(下)

1.5 volatile关键字 我们在了解这个关键字之前,我们首先要把产生线程安全的第4个原因补齐,我们来说说由于内存可见性引起的线程安全问题. 我们来看下面这样一段代码: import java.util.Scanner;public class Demo16 {public static int count 0;public static void main(Str…

PotatoPie 4.0 实验教程(25) —— FPGA实现摄像头图像直方图均衡变换

图像的直方图均衡是什么&#xff1f; 图像的直方图均衡是一种用于增强图像对比度的图像处理技术。在直方图均衡中&#xff0c;图像的像素值被重新分配&#xff0c;以使得图像的直方图变得更均匀&#xff0c;即各个像素值的分布更加平衡。这意味着直方图中每个像素值的频率大致…

在PR中使用 obs 和 vokoscreen 录制的视频遇到的问题

1. obs 录制的视频 在 Adobe Premiere Pro CS6 中只有音频没有视频 2. vokoscreen 录制的视频&#xff0c;没有声音 这是是和视频录制的编码有关系&#xff0c;也和显卡驱动关系 首先 obs 点击 文件 ---> 设置 录制的视频都是可以正常播放的&#xff0c;在PR不行。更…

python爬虫 - 爬取 json 格式数据(股票行情信息:雪球网,自选股)

文章目录 1. 第一步&#xff1a;安装requests库2. 第二步&#xff1a;获取爬虫所需的header和cookie3. 第三步&#xff1a;获取网页4. 第四步&#xff1a;解析网页5. 第五步&#xff1a;解析 json 结构数据体6. 代码实例以及结果展示 python爬虫五部曲&#xff1a; 第一步&…