Android 设置头像 - 相册拍照

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

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

界面实现

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

registerForActivityResult介绍

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

    @NonNull@Overridepublic 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.*/@NonNullpublic 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@Overridepublic 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*/@Overridepublic 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一直,及如下代码
        <providerandroid:name="androidx.core.content.FileProvider"android:authorities="com.***.***.provider"android:grantUriPermissions="true"><meta-dataandroid: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三种组合布局。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

React Context

Context https://juejin.cn/post/7244838033454727227?searchId202404012120436CD549D66BBD6C542177 context 提供了一个无需为每层组件手动添加 props, 就能在组件树间进行数据传递的方法 React 中数据通过 props 属性自上而下(由父及子)进行传递&#xff0c;但此种用法对…

Matlab|二阶锥松弛在配电网最优潮流计算中的应用

目录 一、主要内容 二、部分代码 三、程序代码 四、下载链接 一、主要内容 最优潮流计算是电网规划、优化运行的重要基础。首先建立了配电网全天有功损耗最小化的最优潮流计算模型&#xff1b;其次结合辐射型配电网潮流特点建立支路潮流约束&#xff0c;并考虑配电网中的可…

macOS sonoma 14.4.1编译JDK 12

macOS sonoma 14.4.1编译JDK 12 环境参考文档开始简述问题心路历程着手解决最终解决(前面有点啰嗦了&#xff0c;可以直接看这里) 记录一次靠自己看代码解决问题的经历(总之就是非常开心)。 首先&#xff0c;先diss一下bing&#xff0c;我差一点就放弃了。 环境 macOS sonom…

[力扣]——125.验证回文串

class Solution {public static boolean isValidChar(char ch){if((ch > a && ch < z) ||(ch > 0 && ch < 9)){return true;}return false;}public boolean isPalindrome(String s) {// 将大小写统一起来s s.toLowerCase();int left 0, right s…

vulnhub靶场之FunBox-2

一.环境搭建 1.靶场描述 Boot2Root ! This can be a real life scenario if rockies becomes admins. Easy going in round about 15 mins. Bit more, if you are find and stuck in the rabbit-hole first. This VM is created/tested with Virtualbox. Maybe it works with…

百面算法工程师 | 支持向量机——SVM

文章目录 15.1 SVM15.2 SVM原理15.3 SVM解决问题的类型15.4 核函数的作用以及特点15.5 核函数的表达式15.6 SVM为什么引入对偶问题15.7 SVM使用SGD及步骤15.8 为什么SVM对缺失数据敏感15.9 SVM怎么防止过拟合 欢迎大家订阅我的专栏一起学习共同进步 祝大家早日拿到offer&#x…

利用亚马逊云科技GenAI企业助手Amazon Q Business构建企业代码开发知识库

2024年五一节假日的前一天&#xff0c;亚马逊云科技正式重磅发布了云计算行业期待已久的服务——Amazon Q Business。Amazon Q Business是专为企业用户打造的一个开箱即用的完善而强大企业GenAI助手。企业用户只需要将Amazon Q Business连接到现有的企业内部数据源&#xff0c;…

小程序地理位置接口权限直接抄作业

小程序地理位置接口有什么功能&#xff1f; 随着小程序生态的发展&#xff0c;越来越多的小程序开发者会通过官方提供的自带接口来给用户提供便捷的服务。但是当涉及到地理位置接口时&#xff0c;却经常遇到申请驳回的问题&#xff0c;反复修改也无法通过&#xff0c;给的理由也…

【自研网关系列】过滤器链 -- 限流过滤器

&#x1f308;Yu-Gateway&#xff1a;&#xff1a;基于 Netty 构建的自研 API 网关&#xff0c;采用 Java 原生实现&#xff0c;整合 Nacos 作为注册配置中心。其设计目标是为微服务架构提供高性能、可扩展的统一入口和基础设施&#xff0c;承载请求路由、安全控制、流量治理等…

ESP32-C3模组上跑通MQTT(1)

本文内容参考&#xff1a; 《ESP32-C3 物联网工程开发实战》 特此致谢&#xff01; 一、远程控制的介绍 什么是远程控制&#xff1f;顾名思义&#xff0c;远程控制就是远距离控制&#xff0c;是指控制设备&#xff08;如智能手机、计算机等网络设备&#xff09;通过广域网控制…

FIFO Generate IP核使用——FIFO写操作详解及Status Flags页配置

本文介绍了FIFO的写操作及Status Flags页的配置信息。 1 FIFO 写入操作 当FIFO的写入使能&#xff08;write enable&#xff09;被置位&#xff0c;并且FIFO未满时&#xff0c;数据会从输入总线&#xff08;din&#xff09;被添加到FIFO中&#xff0c;并且写入确认&#xff0…

Mac环境下ollama部署和体验

欢迎访问我的GitHub 这里分类和汇总了欣宸的全部原创(含配套源码)&#xff1a;https://github.com/zq2599/blog_demos 关于ollama ollama和LLM&#xff08;大型语言模型&#xff09;的关系&#xff0c;类似于docker和镜像&#xff0c;可以在ollama服务中管理和运行各种LLM&…

逻辑漏洞:支付逻辑漏洞

目录 1、直接修改商品的价格 2、修改支付状态 3、修改商品数量 4、另类支付 5、修改支付接口 6、重复支付 7、最小支付和最大支付 8、越权支付 9、无线次试用 10、线程并发问题 前两天学习了逻辑漏洞中的越权漏洞&#xff0c;今天开始学习支付逻辑漏洞&#xff0c;这…

数据分析--客户价值分析RFM(分箱法/标准化)

原数据 原数据如果有异常或者缺失等情况&#xff0c;要先对数据进行处理 &#xff0c;再进行下面的操作&#xff0c;要不然会影响结果的正确性 一、根据RFM计算客户价值并对客户进行细分 1. 数据预处理 1.1 创建视图存储 R、F、M的最大最小值 创建视图存储R 、F、M 的最大最小…

Sublime Vim模式配置:q关闭当前标签页

在Sublime安装目录下的->Packages文件夹下新建User文件夹创建文件Vintage.sublime-commands 路径为Sublime安装目录->Packages->User->Vintage.sublime-commands文件内容如下[{"caption": ":w - Save","command": "save"}…

淘宝新店铺一般多久开始有单

淘宝新店铺一般多久开始有单 淘宝推广可以使用3an推客。3an推客&#xff08;CPS模式&#xff09;给商家提供的营销工具&#xff0c;由商家自主设置佣金比例&#xff0c;激励推广者去帮助商家推广商品链接&#xff0c;按最终有效交易金额支付佣金&#xff0c;不成交不扣费。是商…

堆排序以及TOP-K问题

片头 嗨&#xff01;小伙伴们&#xff0c;大家好&#xff01;今天我们来深入理解堆这种数据结构&#xff0c;分析一下堆排序以及TOP-K问题&#xff0c;准备好了吗&#xff1f;我要开始咯&#xff01; 一、堆排序 这里我们先假设要排成升序&#xff0c;也就是从左到右&#xf…

【Leetcode每日一题】 动态规划 - 简单多状态 dp 问题 - 删除并获得点数(难度⭐⭐)(70)

1. 题目解析 题目链接&#xff1a;740. 删除并获得点数 这个问题的理解其实相当简单&#xff0c;只需看一下示例&#xff0c;基本就能明白其含义了。 2.算法原理 问题分析 本题是「打家劫舍」问题的变种&#xff0c;但核心逻辑依然保持一致。题目要求从给定的数组nums中选择…

【面试经典 150 | Kadane】环形子数组的最大和

文章目录 写在前面Tag题目来源解题思路方法一&#xff1a;求最大非空子数组和最小子数组和 写在最后 写在前面 本专栏专注于分析与讲解【面试经典150】算法&#xff0c;两到三天更新一篇文章&#xff0c;欢迎催更…… 专栏内容以分析题目为主&#xff0c;并附带一些对于本题涉及…

C++:输入输出运算符重载

在C中&#xff0c;输入输出运算符是用于从标准输入设备&#xff08;通常是键盘&#xff09;读取数据或将数据输出到标准输出设备&#xff08;通常是屏幕&#xff09;的运算符。常用的输入输出运算符包括&#xff1a; 输入运算符 (>>)&#xff1a; 用于从输入流&#xff0…