【Android入门到项目实战-- 7.3】—— 如何调用手机摄像头和相册
目录
一、调用摄像头拍照
二、打开相册选择照片
学完本篇文章可以收获如何调用手机的摄像头和打开手机相册选择图片功能。
一、调用摄像头拍照
先新建一个CameraAlbumTest项目。
修改activity_main.xml,代码如下:
按钮打开摄像头,ImageView将拍到的图片显示出来。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:orientation="vertical"android:layout_width="match_parent"android:layout_height="match_parent"><Buttonandroid:id="@+id/take_photo"android:layout_width="match_parent"android:layout_height="wrap_content"android:text="打开摄像头"/><ImageViewandroid:id="@+id/picture"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center_horizontal"/></LinearLayout>
下面编写调用摄像头的具体逻辑,修改MainActivity代码,如下:
按钮点击事件里首先创建一个File对象,用于存放摄像头拍下的照片,并命名为output_image.jpg,并将它存放在手机SD卡的应用关联缓存目录下(SD卡中专门存放当前应用缓存数据的位置),调用getExternalCacheDir()方法可以得到这个目录,目录具体路径是:/sdcard/Android/data/包名/cache。
放入缓存目录下的目的是可以不需要处理运行时权限。
接着进行一个判断,如果Android版本低于7.0,调用Uri的fromFile()方法将File对象转换成Uri对象。否则,将File对象转换为Uri对象,Uri对象标识着output_image.jpg这张图片的本地真实路径。这里使用FileProvider的getUriForFile()方法转换为Uri对象,此方法有3个参数,第一个是Context对象,第二个是任意唯一的字符串,第三个是刚刚创建的File对象。
之所以进行转换,是因为直接使用本地真实路径Uri被认为是不安全的,会抛出异常,而FileProvider是一种特殊的内容提供器,它使用了和内容提供其类似的机制来对数据进行保护,可以选择性将封装过的Uri共享给外部,提高了安全性。
接下来构建Intent对象,调用putExtra()方法指定图片的输出地址,这里填入的是刚刚得到的Uri对象,最后调用startActivityForResult()来启动活动,拍下的照片输出到output_image.jpg中。
由于使用的是startActivityForResult()来启动活动,因此拍完照后结果会返回到onActivityResult()方法中,就可以调用BitmapFactory的decodeStream()方法将照片解析成Bitmap对象,然后设置到ImageView中显示出来。
public class MainActivity extends AppCompatActivity {public static final int TAKE_PHOTO = 1;private ImageView picture;private Uri imageUri;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);Button takePhoto = (Button) findViewById(R.id.take_photo);picture = (ImageView) findViewById(R.id.picture);takePhoto.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {
// 创建File对象,用于存储拍照后的照片File outputImage = new File(getExternalCacheDir(),"output_image.jpg");try {if(outputImage.exists()){outputImage.delete();}outputImage.createNewFile();}catch (IOException e){e.printStackTrace();}if(Build.VERSION.SDK_INT >= 24){imageUri = FileProvider.getUriForFile(MainActivity.this,"com.example.cameraalbumtest.fileprovider",outputImage);}else{imageUri = Uri.fromFile(outputImage);}
// 启动相机Intent intent = new Intent("android.media.action.IMAGE_CAPTURE");intent.putExtra(MediaStore.EXTRA_OUTPUT,imageUri);startActivityForResult(intent,TAKE_PHOTO);}});}protected void onActivityResult(int requestCode,int resultCode,Intent data) {super.onActivityResult(requestCode, resultCode, data);switch (requestCode) {case TAKE_PHOTO:if (resultCode == RESULT_OK) {try {
// 显示拍摄的照片Bitmap bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(imageUri));picture.setImageBitmap(bitmap);} catch (FileNotFoundException e) {e.printStackTrace();}}break;default:break;}}
}
然后在res目录下新建一个名字为xml的目录(如果有则忽略这步),右键xml目录 -> New -> File,创建一个file_paths.xml文件,修改内容,代码如下:
external-path用来指定Uri共享,name属性值随意填,path属性的值表示共享的具体路径,这里设置空值表示将整个SD卡共享,也可以仅共享我们存放output_image.jpg这张图片的途径。
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android"><external-pathname="my_images" path=""/>
</paths>
最后修改AndroidManifest.xml文件代码,如下:
由于刚才使用了内容提供器,所以需要进行注册。
需要声明访问SD卡的权限。
android:name值是固定的,android:authorities的值必须要和刚才的FileProvider.getUriForFile()方法中的第二个参数的值一致,<meta-data>指定Uri的共享路径。
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"package="com.example.cameraalbumtest"><uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/><applicationandroid:allowBackup="true"android:dataExtractionRules="@xml/data_extraction_rules"..........<providerandroid:authorities="com.example.cameraalbumtest.fileprovider"android:name="androidx.core.content.FileProvider"android:exported="false"android:grantUriPermissions="true"><meta-dataandroid:name="android.support.FILE_PROVIDER_PATHS"android:resource="@xml/file_paths"/></provider>...................
效果如下:

二、打开相册选择照片
在以上项目的基础上进行修改。
修改activity_main.xml文件,在布局中添加一个按钮用于从相册中选择照片,如下:
...................<Buttonandroid:id="@+id/choose_from_album"android:layout_width="match_parent"android:layout_height="wrap_content"android:text="选择照片"/>
...................
修改MainActivity代码,如下:
代码稍微有点长,因为对Android4.4以上和以下版本分别做了处理,如果你的应用不希望版本低的使用,那么可以不用处理4.4以下版本。
public class MainActivity extends AppCompatActivity {public static final int TAKE_PHOTO = 1;private ImageView picture;private Uri imageUri;public static final int CHOOSE_PHOTO = 2;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);Button takePhoto = (Button) findViewById(R.id.take_photo);Button chooseFromAlbum = (Button) findViewById(R.id.choose_from_album);picture = (ImageView) findViewById(R.id.picture);takePhoto.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {
// 创建File对象,用于存储拍照后的照片File outputImage = new File(getExternalCacheDir(),"output_image.jpg");try {if(outputImage.exists()){outputImage.delete();}outputImage.createNewFile();}catch (IOException e){e.printStackTrace();}if(Build.VERSION.SDK_INT >= 24){imageUri = FileProvider.getUriForFile(MainActivity.this,"com.example.cameraalbumtest.fileprovider",outputImage);}else{imageUri = Uri.fromFile(outputImage);}
// 启动相机Intent intent = new Intent("android.media.action.IMAGE_CAPTURE");intent.putExtra(MediaStore.EXTRA_OUTPUT,imageUri);startActivityForResult(intent,TAKE_PHOTO);}});chooseFromAlbum.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {if(ContextCompat.checkSelfPermission(MainActivity.this,Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED){ActivityCompat.requestPermissions(MainActivity.this,new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},1);}else{openAlbum();}}});}private void openAlbum(){Intent intent = new Intent("android.intent.action.GET_CONTENT");intent.setType("image/*");startActivityForResult(intent,CHOOSE_PHOTO);//打开相册}public void onRequestPermissionsResult(int requestCode,String[] permissions,int[] grantResults){
super.onRequestPermissionsResult(requestCode, permissions, grantResults);switch (requestCode){case 1:if(grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED){openAlbum();}else{Toast.makeText(this, "你拒绝了权限申请", Toast.LENGTH_SHORT).show();}break;default:}}protected void onActivityResult(int requestCode,int resultCode,Intent data) {super.onActivityResult(requestCode, resultCode, data);switch (requestCode) {case TAKE_PHOTO:if (resultCode == RESULT_OK) {try {
// 显示拍摄的照片Bitmap bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(imageUri));picture.setImageBitmap(bitmap);} catch (FileNotFoundException e) {e.printStackTrace();}}break;case CHOOSE_PHOTO:if(resultCode == RESULT_OK){
// 判断手机系统版本号if(Build.VERSION.SDK_INT >= 19){
// 4.4及以上系统使用这个方法处理图片handleImageOnKitKat(data);}else{
// 4.4以下用这个方法handleImageBeforeKitKat(data);}}break;default:break;}}@TargetApi(19)private void handleImageOnKitKat(Intent data) {String imagePath = null;Uri uri = data.getData();Log.d("TAG", "handleImageOnKitKat: uri is " + uri);if (DocumentsContract.isDocumentUri(this, uri)) {// 如果是document类型的Uri,则通过document id处理String docId = DocumentsContract.getDocumentId(uri);if("com.android.providers.media.documents".equals(uri.getAuthority())) {String id = docId.split(":")[1]; // 解析出数字格式的idString selection = MediaStore.Images.Media._ID + "=" + id;imagePath = getImagePath(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, selection);} else if ("com.android.providers.downloads.documents".equals(uri.getAuthority())) {Uri contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"), Long.valueOf(docId));imagePath = getImagePath(contentUri, null);}} else if ("content".equalsIgnoreCase(uri.getScheme())) {// 如果是content类型的Uri,则使用普通方式处理imagePath = getImagePath(uri, null);} else if ("file".equalsIgnoreCase(uri.getScheme())) {// 如果是file类型的Uri,直接获取图片路径即可imagePath = uri.getPath();}displayImage(imagePath); // 根据图片路径显示图片}private void handleImageBeforeKitKat(Intent data) {Uri uri = data.getData();String imagePath = getImagePath(uri, null);displayImage(imagePath);}@SuppressLint("Range")private String getImagePath(Uri uri, String selection) {String path = null;// 通过Uri和selection来获取真实的图片路径Cursor cursor = getContentResolver().query(uri, null, selection, null, null);if (cursor != null) {if (cursor.moveToFirst()) {path = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA));}cursor.close();}return path;}private void displayImage(String imagePath) {if (imagePath != null) {Bitmap bitmap = BitmapFactory.decodeFile(imagePath);picture.setImageBitmap(bitmap);} else {Toast.makeText(this, "failed to get image", Toast.LENGTH_SHORT).show();}}
}
最后提醒一下,因为有些照片尺寸很大,直接加载到内存中会导致程序崩溃,需要根据需求对照片进行压缩,再加载到内存中。
相关文章:
【Android入门到项目实战-- 7.3】—— 如何调用手机摄像头和相册
目录 一、调用摄像头拍照 二、打开相册选择照片 学完本篇文章可以收获如何调用手机的摄像头和打开手机相册选择图片功能。 一、调用摄像头拍照 先新建一个CameraAlbumTest项目。 修改activity_main.xml,代码如下: 按钮打开摄像头,ImageView将拍到的…...
浅聊AIOT
引言 IoT是(Internet of Things)的简称,也就是人们常说的物联网;随着智能硬件的发展和推广,制造成本也随之下降,很多的厂家也慢慢地拥抱网络互联,逐步实现设备互联,也就进入了人们常说的万物互联时代。虽然…...
Python之模块和包(九)
1、模块 1、模块概述 模块是一个包含了定义的函数和变量等的文件。模块可以被程序引入,以使用该模块中的函数等功能。通俗讲:模块就好比是工具包,要想使用这个工具包中的工具(就好比函数),就需要导入这个模块。 2、import 在P…...
C++-----动态规划
目录 一、动态规划的基本思想 二、设计动态规划法的步骤 三、动态规划问题的特征 4.1 矩阵连乘积问题 4.1.1 分析最优解的结构 4.1.2 建立递归关系 4.1.3 计算最优值 4.1.3 计算最优值 4.1.3 构造最优解 4.2 动态规划算法的基本要素 4.2.1 最优子结构 4.2.2 重叠子问题 …...
2.2 Linux控制台访问CLI
系列文章目录 第1章 Linux Shell简介 第2章 Shell基础 <本章所在位置> 第3章 Bash Shell基础命令 第4章 Bash Shell命令进阶 第5章 Linux Shell深度理解 第6章 Linux环境变量 第7章 Linux文件权限 第8章 Linux文件系统的管理 第9章 Linux软件安装 第10章 Linux文本编辑器…...
代码随想录补打卡 509 斐波那契数列
代码如下 //斐波那契数列的第0项是0 第一项是1 func fib(n int) int { if n < 1 { return n } dp : make([]int,n1) dp[0] 0 dp[1] 1 for i : 2 ; i < n ; i { dp[i] dp[i-1] dp[i-2] } return dp[n] } 70 爬楼梯 代码如下 func climbStairs(n int) int …...
【每日一题Day195】LC1003检查替换后的词是否有效 | 栈
检查替换后的词是否有效【LC1003】 给你一个字符串 s ,请你判断它是否 有效 。 字符串 s 有效 需要满足:假设开始有一个空字符串 t "" ,你可以执行 任意次 下述操作将 t 转换为 s : 将字符串 "abc" 插入到 t…...
简单理解什么是序列化
为什么要序列化 序列化的目的就是为了对象可以在网络层进行传输, 比如通过后端传给前端数据。 什么是序列化 我们以Java为例。 序列化就是把对象转化为可传输的字节序列过程,这个字节序列可以是字符串,比如JSON格式的字符串,把…...
Django初识
1、简介 Django,是用python语言写的开源web开发框架,并遵循MVC设计。劳伦斯出版集团为了开发以新闻内容为主的网站,而开发出来了这个框架,于2005年7月在BSD许可证下发布。这个名称来源于比利时的爵士音乐家DjangoReinhardt&#…...
ARM嵌入式编译器-volatile关键字对编译器优化的影响
volatile限定符告知计算机,其他agent(而不是变量所在的程序)可以改变该变量的值。通常它被用于硬件地址以及在其他程序或同时运行的线程中共享数据。要求编译器不要对其描述的对象作优化处理,对它的读写都需要从内存中访问。 使用…...
销售数据分析怎么做?这篇文章说清楚了
如何分析销售数据?分析销售数据有哪些指标?销售数据分析有什么作用? 销售数据是不是得通过数据分析软件啊? 本文将为您解答疑惑—— 一、分析销售数据的指标 从两个层面上来讲,一个是对销售情况的整体把控…...
二十六、ISIS技术总结
文章目录 ISIS 概述一、路由协议总结1、路由优先级2、分类 二、ISIS 协议特点1、特点2、ISIS 路由器的种类 三、ISIS 配置1、基础配置2、network-entity含义3、router id 和系统id转换规则 四、ISIS 开销计算1、Narrow 模式2、Wide 模式 五、 ISIS 和 OSPF 的区别 ISIS 概述 I…...
三菱m70 m80系统解密 三菱m80机床到期解锁
我们从操作系统的发展讲起,为什么要有线程这个概念出现。《Java多线程学习笔记(一) 初遇篇》讲Java平台下的线程,如何使用和创建,以及引入线程后所面临的问题,为了解决线程安全问题,Java引入的机制,这也是《…...
InnoDB 磁盘结构之数据字典和双写缓冲区
数据字典(InnoDB Data Dictionary) MySQL中,数据字典包括了: 表结构、数据库名或表名、字段的数据类型、视图、索引、表字段信息、MySQL版本信息、存储过程、触发器等内容 InnoDB数据字典由内部系统表组成,这些表包含用于查找表…...
Django模型层part two - 多表关系创建和多表操作
前言 继续上面一篇文章的内容,本文介绍多表操作。使用django ORM可以创建多表关系,并且也支持多张表之间的操作,以创建表关系和查询两部分说明django ORM的多表操作。以作者、图书、出版社和作者信息几张表作为案例进行说明。 创建表关系 …...
智能优化算法:浣熊优化算法-附代码
智能优化算法:浣熊优化算法 文章目录 智能优化算法:浣熊优化算法1.浣熊优化算法1.1 初始化1.2 阶段一:狩猎和攻击(探索阶段) 2.实验结果3.参考文献4. Matlab 摘要:浣熊优化算法(Coati Optimizat…...
【51单片机】数码管显示(样例展示以及异常分析)
🎊专栏【51单片机】 🍔喜欢的诗句:更喜岷山千里雪 三军过后尽开颜。 🎆音乐分享【如愿】 大一同学小吉,欢迎并且感谢大家指出我的问题🥰 ⭐数码管 比如要显示“6”,那么下面图片中,AFEDCG=1,B=0 对应到数码管上,就是 ⭐原理 🎊P22~P24控制LED1~...
Android InputChannel事件发送接收系统分析
本文基于Android12。 InputChannel表示其他进程通过文件描述符传递输入事件到View的通道,因为需要跨进程传输,实现了Parcelable序列化接口,所以也能够理解Java层的InputChannel后面为什么使用copyTo()方法初始化。 输入事件的接收方是View&…...
Java时间类(五)-- LocalDate()类
目录 引言: 1. LocalDate的概述: 2. LocalDate的常用方法: 引言: (1)Date存在的缺陷: 如果不格式化,打印出的日期可读性差://获取当前时间Date date = new Date();System.out.println("date = " + date); //date = Wed May 03 22:30:24 CST...
用手机号码归属地 API 开发的应用推荐
引言 手机号码归属地 API是一种提供手机号码归属地信息的接口,通过该接口,可以获取手机号码所属的省份、城市、运营商等信息。它可以帮助企业更好地了解客户,为个性化推荐和精准广告投放提供数据支持。作为一种数据服务,手机号码…...
全球首个30米分辨率湿地数据集(2000—2022)
数据简介 今天我们分享的数据是全球30米分辨率湿地数据集,包含8种湿地亚类,该数据以0.5X0.5的瓦片存储,我们整理了所有属于中国的瓦片名称与其对应省份,方便大家研究使用。 该数据集作为全球首个30米分辨率、覆盖2000–2022年时间…...
转转集团旗下首家二手多品类循环仓店“超级转转”开业
6月9日,国内领先的循环经济企业转转集团旗下首家二手多品类循环仓店“超级转转”正式开业。 转转集团创始人兼CEO黄炜、转转循环时尚发起人朱珠、转转集团COO兼红布林CEO胡伟琨、王府井集团副总裁祝捷等出席了开业剪彩仪式。 据「TMT星球」了解,“超级…...
令牌桶 滑动窗口->限流 分布式信号量->限并发的原理 lua脚本分析介绍
文章目录 前言限流限制并发的实际理解限流令牌桶代码实现结果分析令牌桶lua的模拟实现原理总结: 滑动窗口代码实现结果分析lua脚本原理解析 限并发分布式信号量代码实现结果分析lua脚本实现原理 双注解去实现限流 并发结果分析: 实际业务去理解体会统一注…...
解决本地部署 SmolVLM2 大语言模型运行 flash-attn 报错
出现的问题 安装 flash-attn 会一直卡在 build 那一步或者运行报错 解决办法 是因为你安装的 flash-attn 版本没有对应上,所以报错,到 https://github.com/Dao-AILab/flash-attention/releases 下载对应版本,cu、torch、cp 的版本一定要对…...
C++.OpenGL (20/64)混合(Blending)
混合(Blending) 透明效果核心原理 #mermaid-svg-SWG0UzVfJms7Sm3e {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-SWG0UzVfJms7Sm3e .error-icon{fill:#552222;}#mermaid-svg-SWG0UzVfJms7Sm3e .error-text{fill…...
[大语言模型]在个人电脑上部署ollama 并进行管理,最后配置AI程序开发助手.
ollama官网: 下载 https://ollama.com/ 安装 查看可以使用的模型 https://ollama.com/search 例如 https://ollama.com/library/deepseek-r1/tags # deepseek-r1:7bollama pull deepseek-r1:7b改token数量为409622 16384 ollama命令说明 ollama serve #:…...
【LeetCode】3309. 连接二进制表示可形成的最大数值(递归|回溯|位运算)
LeetCode 3309. 连接二进制表示可形成的最大数值(中等) 题目描述解题思路Java代码 题目描述 题目链接:LeetCode 3309. 连接二进制表示可形成的最大数值(中等) 给你一个长度为 3 的整数数组 nums。 现以某种顺序 连接…...
《Offer来了:Java面试核心知识点精讲》大纲
文章目录 一、《Offer来了:Java面试核心知识点精讲》的典型大纲框架Java基础并发编程JVM原理数据库与缓存分布式架构系统设计二、《Offer来了:Java面试核心知识点精讲(原理篇)》技术文章大纲核心主题:Java基础原理与面试高频考点Java虚拟机(JVM)原理Java并发编程原理Jav…...
李沐--动手学深度学习--GRU
1.GRU从零开始实现 #9.1.2GRU从零开始实现 import torch from torch import nn from d2l import torch as d2l#首先读取 8.5节中使用的时间机器数据集 batch_size,num_steps 32,35 train_iter,vocab d2l.load_data_time_machine(batch_size,num_steps) #初始化模型参数 def …...
生信服务器 | 做生信为什么推荐使用Linux服务器?
原文链接:生信服务器 | 做生信为什么推荐使用Linux服务器? 一、 做生信为什么推荐使用服务器? 大家好,我是小杜。在做生信分析的同学,或是将接触学习生信分析的同学,<font style"color:rgb(53, 1…...
