安卓开发 :实现打开一次相机连续拍多张照片返回后拿到多张照片,一个小功能的构思

本文主要是介绍安卓开发 :实现打开一次相机连续拍多张照片返回后拿到多张照片,一个小功能的构思,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

*本篇文章已授权微信公众号 hongyangAndroid(鸿洋)独家发布

今日,产品经理又疯了,搞这么一个功能,也是没事做了,算了,先来说一下具体功能吧,简化来就是:

在一个界面上,点击某个按钮,打开相机,拍很多张照片,返回以后,把刚才所拍的照片显示出来,照片数小于等于7。

各位看官,别着急,是不是觉得特简单,不就Intent跳转打开相机,拍几张照片,在onActivityResult方法里面拿到刚才拍的照片,显示出来不就可以了么?嗯,是的,是这么简单。我来帮您写过程,当然有任何问题可以加QQ群:661614986,进行询问。


这里写图片描述



一、第一个想法实现功能

1.1、新建工程,布局文件,初始化View,并添加点击事件:

这个过程非常的简单,就看下代码,直接跳过。

布局文件:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"tools:context="com.exampleenen.ruedy.moreimagetwo.MainActivity"><Button
        android:id="@+id/bt_main"android:layout_width="200dp"android:layout_height="70dp"android:layout_centerHorizontal="true"android:text="点击打开相机" /><GridView
        android:id="@+id/gv_main"android:layout_width="match_parent"android:layout_height="match_parent"android:layout_below="@id/bt_main"android:numColumns="3"></GridView></RelativeLayout>

初始化View:

public class MainActivity extends AppCompatActivity {private View btMain;private GridView gvMain;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);initViews();}private void initViews() {btMain = findViewById(R.id.bt_main);gvMain = ((GridView) findViewById(R.id.gv_main));}
}

1.2、添加点击事件打开相机:
这里也应该非常的简单和熟悉,直接上代码:

点击事件:

       btMain.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {putOnCamera();//打开相机的方法}});

打开相机:

       ACTION_IMAGE_CAPTURE //打开相机拍一次ACTION_IMAGE_CAPTURE_SECUREINTENT_ACTION_STILL_IMAGE_CAMERAINTENT_ACTION_STILL_IMAGE_CAMERA_SECURE

这个intent是隐式跳转的,有四个常量,第一个大家都很熟悉,就是打开相机拍一张照片完事,所以我们不选他,那该选择哪个呢?不着急,我们一个个试一下,打个log试试先

1.2.2、ACTION_IMAGE_CAPTURE_SECURE:

    /*** 打开相机*/private void putOnCamera() {Intent intent = new Intent();intent.setAction(MediaStore.ACTION_IMAGE_CAPTURE_SECURE);startActivityForResult(intent, REQUEST_SMALL);}

在onActivityResult中我们得到返回值

    @Overrideprotected void onActivityResult(int requestCode, int resultCode, Intent data) {if (requestCode == REQUEST_SMALL) {Log.e("datadata", "requestCode:"+requestCode+",resultCode:"+resultCode+",data:"+data);}}

开机运行啦,测试机是华为小米的6.0系统,看一下效果,
嗯,不错,打开相机是这样的:


这里写图片描述

图片比较大,就贴一张看一下,和直接打开系统相机是一样的,没错,可以多次拍照,哈哈,原来真的这么简单,拍完返回看一下log如下:

04-13 16:38:10.934 28785-28785/com.exampleenen.ruedy.moreimagetwo E/datadata: requestCode:11111,resultCode:0,data:null


这里写图片描述

那好,咱们看截图:

这里写图片描述

哈哈,眼看就要完成了,最后竟然 得到一个null,所以这个”ACTION_IMAGE_CAPTURE_SECURE”模式完成不了此功能,我们再看看另外两个参数,额。。。各位看官,不用试了,小生已经试过了,结果是一样的。
所以嘛,没这么简单,这么简单,我也不会费这么大力气写篇博客了。

既然这条路不通过,那咱们怎么办呢?和产品经理say NO? 我看行,等等,那位朋友讲话了:


这里写图片描述

嗯,想想,可以的,您先想,应该有其他方法,我也想到了一个方法,您看这样可以么?前方高能,同志请带好安全帽:

自己重新写个相机,点击按钮的时候 我们打开自己的相机,在相机里面写个方法,把照片数据都返回出去?嗯,听着可以啊,你去写呗,我可不写,我的项目估计还没写个相机复杂。那好吧,那这样行么?我给两组log数据您看下:

111小米6.0系统:

E/setsize: getContentProvider: /storage/emulated/0/DCIM/Camera/IMG_20170413_165443.jpg
E/setsize: getContentProvider: /storage/emulated/0/DCIM/Camera/IMG_20170413_165451.jpg
E/setsize: getContentProvider: /storage/emulated/0/DCIM/Camera/IMG_20170413_165447.jpg
E/setsize: getContentProvider: /storage/emulated/0/DCIM/Camera/IMG_20170413_165442.jpg
E/setsize: getContentProvider: /storage/emulated/0/DCIM/Camera/IMG_20170413_165448.jpg
E/setsize: getContentProvider: /storage/emulated/0/DCIM/Camera/IMG_20170413_165449.jpg
E/setsize: getContentProvider: /storage/emulated/0/DCIM/Camera/IMG_20170413_165445.jpg

222华为7.0系统:

E/setsize: getContentProvider: /storage/emulated/0/DCIM/Camera/IMG_20170413_165655.jpg
E/setsize: getContentProvider: /storage/emulated/0/DCIM/Camera/IMG_20170413_165652.jpg
E/setsize: getContentProvider: /storage/emulated/0/DCIM/Camera/IMG_20170413_165641.jpg
E/setsize: getContentProvider: /storage/emulated/0/DCIM/Camera/IMG_20170413_165647.jpg
E/setsize: getContentProvider: /storage/emulated/0/DCIM/Camera/IMG_20170413_165643.jpg
E/setsize: getContentProvider: /storage/emulated/0/DCIM/Camera/IMG_20170413_165645.jpg
E/setsize: getContentProvider: /storage/emulated/0/DCIM/Camera/IMG_20170413_165650.jpg
E/setsize: getContentProvider: /storage/emulated/0/DCIM/Camera/IMG_20170413_165659.jpg

不要问是什么 ,从哪里来的,您就看熟不熟悉?


这里写图片描述

对了,是在哪里见过,在哪里呢?打开你手机拍出来的照片,打开照片信息,看一下路径。对嘛,我就说在快播,额不 , 手机照片上见过嘛。

有点意思,有点意思

可这有个卵用啊 , 你的data里面还是拿不到刚才拍的照片呀,是的,说得对奥,data还是拿不到打开相机拍得照片,可是。。。。。,这样行么大兄弟,不一定非要从data里面拿呀,咱可以从系统里面拿呀,打开相机是必须的,所以东西还得在onActivityResult里面进行,说的是轻松,从系统里面拿?怎么拿?根据什么拿?

问的好啊 , 那我来说一下我的几个思路吧

111思路一

先看一下这个路径

/storage/emulated/0/DCIM/Camera/

手机拍的照片好像都在这个手机文件夹下面,直接读取这个文件夹下面的文件,不就能拿到了么。

是的,

不过有个问题,拍摄的照片所在的路径不一定,是的,照片的储存位置用户是可以设置、改变的,所以本着最近《人民的名义》 所教导的精神,做一名良心程序员,咱们还是把这个情况考虑进去吧,所以这个方法有缺陷,很大的缺陷。

222思路二
安卓有个四大组件之一的内容提供者,也就是ContentProvider,这玩意很少用到吧,安卓系统把手机里面所有的照片的 路径 (记住是路径啊) 做成了一个数据库 , 我们可以通过内容提供者ContentProvider拿到所有(记住是所有)照片的路径,然后根据某些规律 , 找到 打开相机以后所拍的照片不就可以了吗 ? 是的,那么问题转化为这个规律 是什么 ,仔细看这些数据:

/storage/emulated/0/DCIM/Camera/IMG_20170413_165655.jpg
/storage/emulated/0/DCIM/Camera/IMG_20170413_165652.jpg
/storage/emulated/0/DCIM/Camera/IMG_20170413_165641.jpg
/storage/emulated/0/DCIM/Camera/IMG_20170413_165647.jpg
/storage/emulated/0/DCIM/Camera/IMG_20170413_165643.jpg
/storage/emulated/0/DCIM/Camera/IMG_20170413_165645.jpg
/storage/emulated/0/DCIM/Camera/IMG_20170413_165650.jpg
/storage/emulated/0/DCIM/Camera/IMG_20170413_165659.jpg

规律发现不了的大兄弟应该不怎么会玩游戏吧,比如我认识的一位老司机:某浒,规律精华如下:

“IMG_20170413_165655.jpg”———这个是照片文件的文件名,中间的”20170413_165655“中,“_”前面是年月日嘛,后面是时分秒嘛,哈哈,可以了,规律可以了,这些照片名是按时间组合起来的,那我们就可以根据时间,判断哪些是我们打开相机后拍的,可以伐?可以。

二、正式开启思路二

根据思路 我们要用到的地方我做了以下总结,真正写的时候还是很多坑的,总结如下:

  • 内容提供者ContentProvider与内容接受者ContentResolver
  • 数据库、多线程(这个可以忽略但是要知道,数据库 ,上一条已经把这个做好了,多线程是因为查数据库其实是耗时的,最好在子线程里面,避免ANR,咱这里就不提了)
  • 路径转换出时间再转换成数字(时间格式转化,String类型转换成其他类型)
  • 塞选图片路径(正则表达式)
  • 根据路径path找到图片
  • 把图片压缩放后放到集合里面(二次采样)

目测东西不少呢,

等等,根据路径path找到图片,是要读取手机内存卡内容的呀,所以针对6.0及以上,还要做权限申请那一块,为了减少篇幅,这一块就不细说了,大家知道就好。

那么来吧

2.1、内容提供者ContentProvider与内容接受者ContentResolver

让我自己写肯定写不出来,不怎么好记,百度了一下,如下得到图片数据库,因为是退出相机以后查询,所以要在onActivityResult里面,如下:

@Overrideprotected void onActivityResult(int requestCode, int resultCode, Intent data) {if (requestCode == REQUEST_SMALL) {final String[] projection = {MediaStore.Images.Media._ID,MediaStore.Images.Media.DISPLAY_NAME,MediaStore.Images.Media.DATA};final String orderBy = MediaStore.Images.Media.DISPLAY_NAME;final Uri uri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;}}

这里拿到了uri,基本就能拿到内容提供者了,go on ,由于代码比较多,还是封装起来吧;

        getContentProvider(uri, projection, orderBy);
public void getContentProvider(Uri uri, String[] projection, String orderBy) {Cursor cursor = getContentResolver().query(uri, projection, null,null, orderBy);if (null == cursor) {return ;}while (cursor.moveToNext()) {for (int i = 0; i < projection.length; i++) {String string = cursor.getString(i);if (string != null) {     Log.e("string", "getContentProvider: "+string);}}}}

OK,run一下,打个log:


这里写图片描述

数据太大,截个图看一下吧就,还是比较喜观的,这里是所有图片的路径,下面我们来赛选。

2.2、正则表达式塞选图片
04-13 18:08:42.808 28206-28206/com.exampleenen.ruedy.moreimagetwo E/string: getContentProvider: /storage/emulated/0/DCIM/Camera/IMG_20170413_154316.jpg

“/storage/emulated/0/DCIM/Camera/IMG_20170413_154316.jpg”——这个就是我们的目标,我简化了一下,锁定方法,仁者见仁,智者见智,判断方法有很多种,我的判断代码如下, 在上面的if中加入代码:

                         int length = string.length();if (length >= 30) {//根据实际路径得到的,大一点保险ss = string.substring(length - 23, length);//判断后23位的字符串,String substring = ss.substring(0, 4);//大致判断一下是系统图片,后面严格塞选String hen = ss.substring(12, 13);if (substring.equals("IMG_") && hen.equals("_")) {//懒得写正则了,就这样吧,可自行拓展String laststring = ss.substring(4, 19).replace("_", "");Log.e("string", "getContentProvider: " + string);}}

再打个log看一下:


这里写图片描述

对了,逮住了,就是这些,下面就是拿到路径对应时间的数学数字time(注意是long,不是int,很多人会栽跟头的),在打开相机之前根据java代码,拿到时间systemTime1;方法为:

    public long getSystemTime() {
//("yyyy年MM月dd日 HH时MM分ss秒"SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");long times = System.currentTimeMillis();System.out.println(times);Date date = new Date(times);String time = sdf.format(date).toString();Log.e("timeintimet", "timeint: " + time.toString());long timeint = 0;try {;timeint = Long.valueOf(time).longValue();} catch (Exception e) {Log.e("exception", "getSystemTime: " + e.toString());}return timeint;}

然后返回界面以后拿到时间systemTime2;再在上上面代码中加入图下判断,

                      try {long time = Long.valueOf(laststring).longValue();if (time > systemTime1 && time <= systemTime2) {//位于两个时间之间的就是我们拍摄的图片}} catch (Exception e) {Log.e("exception", "getContentProvider: " + e.toString());}

加个try、catch保险一点。

继续run,拍照,返回,log如下:

这里写图片描述

嗯嗯,可以了,拿到了,终于拿到所谓的data了;

这里写图片描述

下面就简单了,根据路径拿到图片并且二次采样如下:

/*** 根据路径,二次采样并且压缩* @param filePath 路径* @param destWidth 压缩到的宽度* @param destHeight 压缩到的高度* @return*/public Bitmap convertToBitmap(String filePath, int destWidth, int destHeight) {//第一采样BitmapFactory.Options options = new BitmapFactory.Options();options.inJustDecodeBounds = true;BitmapFactory.decodeFile(filePath, options);int outWidth = options.outWidth;int outHeight = options.outHeight;int sampleSize = 1;while ((outWidth / sampleSize > destWidth) || (outHeight / sampleSize > destHeight)) {sampleSize *= 2;}//第二次采样options.inJustDecodeBounds = false;options.inSampleSize = sampleSize;options.inPreferredConfig = Bitmap.Config.RGB_565;return BitmapFactory.decodeFile(filePath, options);}

后面就可以把路径和图片放起来add到List里面就可以了,我是新建了一个实体类。

这样数据源就有了可以设置到GridView的适配器里面啦,哈哈,

工作我来做,您休息
。。。。。
。。。。。
。。。。。

搞定了,看下效果:


这里写图片描述

额,这次动图比较大,所以压缩了一下,不大好看,终于有效果了。不过 这里面还有几个问题,如果真的要搞到项目你需注意以下几点:
- 打开相机那里需要优化,各大厂商系统改的面目全非
- 权限必须打开 相机和读内存卡文件权限都得打开, 这个必须
- 按照上面代码,每张图片会得到两次,所以去重一下,我用Hashset去重的(实践而来)
- 如果用户打开手机刷刷刷,每一秒牌好几张,那一秒里面拍的照片 这里的代码这个是得不到的,因为这种情况到秒的时间都是一样的,多出来了毫秒加在了秒后面,如果想得到,请自己在塞选那里自行改善,不改善的话,就提醒用户慢点拍,因为一秒钟拍那么多,肯定是一个视角拍出来的,何必呢
- 真的要整合到项目里的话,这里还有个漏洞,比如拍完照片,用户,按Home键,然后改变时间,然后进我们的应用再按返回,应该不会有人这么做的,这里可以提醒一下用户,请拍完照片直接按返回,避免失败。

以上是自己的踩得坑,测试手机有限,问题肯定是有的,代码已经总结、全部上传github,点击查看 。

经验总结:

很多刚入手安卓的朋友,面对一个功能、需求的时候,有时候很迷茫,无从下手,其实我们一定要善于思考,知其然,知其所以然,才能把学到的知识糅合到一起,用哪里的时候知道去哪找,不用高端的东西,咱也能实现一些意想不到的功能。本篇文章的需求,应该还有其他方法,有兴趣的朋友可以探索一下。 有问题欢迎指正,一起进步,谢谢各位!

安卓交流群:661614986

这篇关于安卓开发 :实现打开一次相机连续拍多张照片返回后拿到多张照片,一个小功能的构思的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/735313

相关文章

这15个Vue指令,让你的项目开发爽到爆

1. V-Hotkey 仓库地址: github.com/Dafrok/v-ho… Demo: 戳这里 https://dafrok.github.io/v-hotkey 安装: npm install --save v-hotkey 这个指令可以给组件绑定一个或多个快捷键。你想要通过按下 Escape 键后隐藏某个组件,按住 Control 和回车键再显示它吗?小菜一碟: <template

Hadoop企业开发案例调优场景

需求 (1)需求:从1G数据中,统计每个单词出现次数。服务器3台,每台配置4G内存,4核CPU,4线程。 (2)需求分析: 1G / 128m = 8个MapTask;1个ReduceTask;1个mrAppMaster 平均每个节点运行10个 / 3台 ≈ 3个任务(4    3    3) HDFS参数调优 (1)修改:hadoop-env.sh export HDFS_NAMENOD

hdu1043(八数码问题,广搜 + hash(实现状态压缩) )

利用康拓展开将一个排列映射成一个自然数,然后就变成了普通的广搜题。 #include<iostream>#include<algorithm>#include<string>#include<stack>#include<queue>#include<map>#include<stdio.h>#include<stdlib.h>#include<ctype.h>#inclu

嵌入式QT开发:构建高效智能的嵌入式系统

摘要: 本文深入探讨了嵌入式 QT 相关的各个方面。从 QT 框架的基础架构和核心概念出发,详细阐述了其在嵌入式环境中的优势与特点。文中分析了嵌入式 QT 的开发环境搭建过程,包括交叉编译工具链的配置等关键步骤。进一步探讨了嵌入式 QT 的界面设计与开发,涵盖了从基本控件的使用到复杂界面布局的构建。同时也深入研究了信号与槽机制在嵌入式系统中的应用,以及嵌入式 QT 与硬件设备的交互,包括输入输出设

OpenHarmony鸿蒙开发( Beta5.0)无感配网详解

1、简介 无感配网是指在设备联网过程中无需输入热点相关账号信息,即可快速实现设备配网,是一种兼顾高效性、可靠性和安全性的配网方式。 2、配网原理 2.1 通信原理 手机和智能设备之间的信息传递,利用特有的NAN协议实现。利用手机和智能设备之间的WiFi 感知订阅、发布能力,实现了数字管家应用和设备之间的发现。在完成设备间的认证和响应后,即可发送相关配网数据。同时还支持与常规Sof

C++11第三弹:lambda表达式 | 新的类功能 | 模板的可变参数

🌈个人主页: 南桥几晴秋 🌈C++专栏: 南桥谈C++ 🌈C语言专栏: C语言学习系列 🌈Linux学习专栏: 南桥谈Linux 🌈数据结构学习专栏: 数据结构杂谈 🌈数据库学习专栏: 南桥谈MySQL 🌈Qt学习专栏: 南桥谈Qt 🌈菜鸡代码练习: 练习随想记录 🌈git学习: 南桥谈Git 🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈�

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

【Prometheus】PromQL向量匹配实现不同标签的向量数据进行运算

✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。 🏆《博客》:Python全栈,前后端开发,小程序开发,人工智能,js逆向,App逆向,网络系统安全,数据分析,Django,fastapi

poj2406(连续重复子串)

题意:判断串s是不是str^n,求str的最大长度。 解题思路:kmp可解,后缀数组的倍增算法超时。next[i]表示在第i位匹配失败后,自动跳转到next[i],所以1到next[n]这个串 等于 n-next[n]+1到n这个串。 代码如下; #include<iostream>#include<algorithm>#include<stdio.h>#include<math.

活用c4d官方开发文档查询代码

当你问AI助手比如豆包,如何用python禁止掉xpresso标签时候,它会提示到 这时候要用到两个东西。https://developers.maxon.net/论坛搜索和开发文档 比如这里我就在官方找到正确的id描述 然后我就把参数标签换过来