当前位置: 首页 > news >正文

【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,代码如下&#xff1a; 按钮打开摄像头&#xff0c;ImageView将拍到的…...

浅聊AIOT

引言 IoT是(Internet of Things)的简称&#xff0c;也就是人们常说的物联网&#xff1b;随着智能硬件的发展和推广&#xff0c;制造成本也随之下降&#xff0c;很多的厂家也慢慢地拥抱网络互联&#xff0c;逐步实现设备互联&#xff0c;也就进入了人们常说的万物互联时代。虽然…...

Python之模块和包(九)

1、模块 1、模块概述 模块是一个包含了定义的函数和变量等的文件。模块可以被程序引入&#xff0c;以使用该模块中的函数等功能。通俗讲&#xff1a;模块就好比是工具包&#xff0c;要想使用这个工具包中的工具(就好比函数)&#xff0c;就需要导入这个模块。 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 &#xff0c;请你判断它是否 有效 。 字符串 s 有效 需要满足&#xff1a;假设开始有一个空字符串 t "" &#xff0c;你可以执行 任意次 下述操作将 t 转换为 s &#xff1a; 将字符串 "abc" 插入到 t…...

简单理解什么是序列化

为什么要序列化 序列化的目的就是为了对象可以在网络层进行传输&#xff0c; 比如通过后端传给前端数据。 什么是序列化 我们以Java为例。 序列化就是把对象转化为可传输的字节序列过程&#xff0c;这个字节序列可以是字符串&#xff0c;比如JSON格式的字符串&#xff0c;把…...

Django初识

1、简介 Django&#xff0c;是用python语言写的开源web开发框架&#xff0c;并遵循MVC设计。劳伦斯出版集团为了开发以新闻内容为主的网站&#xff0c;而开发出来了这个框架&#xff0c;于2005年7月在BSD许可证下发布。这个名称来源于比利时的爵士音乐家DjangoReinhardt&#…...

ARM嵌入式编译器-volatile关键字对编译器优化的影响

volatile限定符告知计算机&#xff0c;其他agent&#xff08;而不是变量所在的程序&#xff09;可以改变该变量的值。通常它被用于硬件地址以及在其他程序或同时运行的线程中共享数据。要求编译器不要对其描述的对象作优化处理&#xff0c;对它的读写都需要从内存中访问。 使用…...

销售数据分析怎么做?这篇文章说清楚了

如何分析销售数据&#xff1f;分析销售数据有哪些指标&#xff1f;销售数据分析有什么作用&#xff1f; 销售数据是不是得通过数据分析软件啊&#xff1f; 本文将为您解答疑惑—— 一、分析销售数据的指标 从两个层面上来讲&#xff0c;一个是对销售情况的整体把控&#xf…...

二十六、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机床到期解锁

我们从操作系统的发展讲起&#xff0c;为什么要有线程这个概念出现。《Java多线程学习笔记(一) 初遇篇》讲Java平台下的线程&#xff0c;如何使用和创建&#xff0c;以及引入线程后所面临的问题&#xff0c;为了解决线程安全问题&#xff0c;Java引入的机制&#xff0c;这也是《…...

InnoDB 磁盘结构之数据字典和双写缓冲区

数据字典&#xff08;InnoDB Data Dictionary&#xff09; MySQL中&#xff0c;数据字典包括了: 表结构、数据库名或表名、字段的数据类型、视图、索引、表字段信息、MySQL版本信息、存储过程、触发器等内容 InnoDB数据字典由内部系统表组成&#xff0c;这些表包含用于查找表…...

Django模型层part two - 多表关系创建和多表操作

前言 继续上面一篇文章的内容&#xff0c;本文介绍多表操作。使用django ORM可以创建多表关系&#xff0c;并且也支持多张表之间的操作&#xff0c;以创建表关系和查询两部分说明django ORM的多表操作。以作者、图书、出版社和作者信息几张表作为案例进行说明。 创建表关系 …...

智能优化算法:浣熊优化算法-附代码

智能优化算法&#xff1a;浣熊优化算法 文章目录 智能优化算法&#xff1a;浣熊优化算法1.浣熊优化算法1.1 初始化1.2 阶段一&#xff1a;狩猎和攻击&#xff08;探索阶段&#xff09; 2.实验结果3.参考文献4. Matlab 摘要&#xff1a;浣熊优化算法&#xff08;Coati Optimizat…...

【51单片机】数码管显示(样例展示以及异常分析)

🎊专栏【51单片机】 🍔喜欢的诗句:更喜岷山千里雪 三军过后尽开颜。 🎆音乐分享【如愿】 大一同学小吉,欢迎并且感谢大家指出我的问题🥰 ⭐数码管 比如要显示“6”,那么下面图片中,AFEDCG=1,B=0 对应到数码管上,就是 ⭐原理 🎊P22~P24控制LED1~...

Android InputChannel事件发送接收系统分析

本文基于Android12。 InputChannel表示其他进程通过文件描述符传递输入事件到View的通道&#xff0c;因为需要跨进程传输&#xff0c;实现了Parcelable序列化接口&#xff0c;所以也能够理解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是一种提供手机号码归属地信息的接口&#xff0c;通过该接口&#xff0c;可以获取手机号码所属的省份、城市、运营商等信息。它可以帮助企业更好地了解客户&#xff0c;为个性化推荐和精准广告投放提供数据支持。作为一种数据服务&#xff0c;手机号码…...

Objective-C常用命名规范总结

【OC】常用命名规范总结 文章目录 【OC】常用命名规范总结1.类名&#xff08;Class Name)2.协议名&#xff08;Protocol Name)3.方法名&#xff08;Method Name)4.属性名&#xff08;Property Name&#xff09;5.局部变量/实例变量&#xff08;Local / Instance Variables&…...

VM虚拟机网络配置(ubuntu24桥接模式):配置静态IP

编辑-虚拟网络编辑器-更改设置 选择桥接模式&#xff0c;然后找到相应的网卡&#xff08;可以查看自己本机的网络连接&#xff09; windows连接的网络点击查看属性 编辑虚拟机设置更改网络配置&#xff0c;选择刚才配置的桥接模式 静态ip设置&#xff1a; 我用的ubuntu24桌…...

AGain DB和倍数增益的关系

我在设置一款索尼CMOS芯片时&#xff0c;Again增益0db变化为6DB&#xff0c;画面的变化只有2倍DN的增益&#xff0c;比如10变为20。 这与dB和线性增益的关系以及传感器处理流程有关。以下是具体原因分析&#xff1a; 1. dB与线性增益的换算关系 6dB对应的理论线性增益应为&…...

GitHub 趋势日报 (2025年06月06日)

&#x1f4ca; 由 TrendForge 系统生成 | &#x1f310; https://trendforge.devlive.org/ &#x1f310; 本日报中的项目描述已自动翻译为中文 &#x1f4c8; 今日获星趋势图 今日获星趋势图 590 cognee 551 onlook 399 project-based-learning 348 build-your-own-x 320 ne…...

【JVM】Java虚拟机(二)——垃圾回收

目录 一、如何判断对象可以回收 &#xff08;一&#xff09;引用计数法 &#xff08;二&#xff09;可达性分析算法 二、垃圾回收算法 &#xff08;一&#xff09;标记清除 &#xff08;二&#xff09;标记整理 &#xff08;三&#xff09;复制 &#xff08;四&#xff…...

6个月Python学习计划 Day 16 - 面向对象编程(OOP)基础

第三周 Day 3 &#x1f3af; 今日目标 理解类&#xff08;class&#xff09;和对象&#xff08;object&#xff09;的关系学会定义类的属性、方法和构造函数&#xff08;init&#xff09;掌握对象的创建与使用初识封装、继承和多态的基本概念&#xff08;预告&#xff09; &a…...

VisualXML全新升级 | 新增数据库编辑功能

VisualXML是一个功能强大的网络总线设计工具&#xff0c;专注于简化汽车电子系统中复杂的网络数据设计操作。它支持多种主流总线网络格式的数据编辑&#xff08;如DBC、LDF、ARXML、HEX等&#xff09;&#xff0c;并能够基于Excel表格的方式生成和转换多种数据库文件。由此&…...

DAY 45 超大力王爱学Python

来自超大力王的友情提示&#xff1a;在用tensordoard的时候一定一定要用绝对位置&#xff0c;例如&#xff1a;tensorboard --logdir"D:\代码\archive (1)\runs\cifar10_mlp_experiment_2" 不然读取不了数据 知识点回顾&#xff1a; tensorboard的发展历史和原理tens…...

基于Python的气象数据分析及可视化研究

目录 一.&#x1f981;前言二.&#x1f981;开源代码与组件使用情况说明三.&#x1f981;核心功能1. ✅算法设计2. ✅PyEcharts库3. ✅Flask框架4. ✅爬虫5. ✅部署项目 四.&#x1f981;演示效果1. 管理员模块1.1 用户管理 2. 用户模块2.1 登录系统2.2 查看实时数据2.3 查看天…...

初级程序员入门指南

初级程序员入门指南 在数字化浪潮中&#xff0c;编程已然成为极具价值的技能。对于渴望踏入程序员行列的新手而言&#xff0c;明晰入门路径与必备知识是开启征程的关键。本文将为初级程序员提供全面的入门指引。 一、明确学习方向 &#xff08;一&#xff09;编程语言抉择 编…...