Android Camera Develop: add settings to camera app

概述

继上一篇实现了一个最简单的相机APP后,本篇主要介绍实现相机的各种偏好设置,比如分辨率、闪光灯、对焦等。添加这些设置看起来很容易,但实际实现时还是有很多需要注意的。

添加设置菜单

我们使用Android推荐的PreferenceFragment作为相机偏好设置的菜单。PreferenceFragment继承自Fragment,而Fragment从简单来说可以理解成Activity里的Activity,但可以让我们不再非常关心其UI布局,因为Android已经帮我们搞定了;PreferenceFragment则可以理解为专门为设置量身定做的Fragment,我们只用向其添加设置的内容,而不用关心具体设置参数用户交互设计等繁琐的内容。

添加SettingsFragment类

就像上篇介绍的CameraPreview继承自SurfaceView实现了一个自定义的View,现在我们需要创建SettingsFragment继承自PreferenceFragment实现一个自定义的Fragment。

新建SettingsFragment类

内容如下:

addPreferencesFromResource(R.xml.preferences);是加载来自preferences.xml文件中的菜单条目,对于PreferenceFragment,其菜单条目都是存储在xml文件中,实例化时从xml文件加载的,别着急,我们马上创建preferences.xml文件。

添加菜单条目

对于PreferenceFragment来说,需要从xml文件加载菜单条目等内容的。在res下新建xml文件夹,在其内新建preferences.xml文件

内容如下:

菜单的每一个条目都是一个Preference。这里的ListPreference继承自Preference,就是条目选项是列表的菜单条目。其名字是照片品质,对应的key(条目以key-value对形式存储)是jpeg_quality;其对用户可见的列表选项由entries定义,而对代码而言是entryValues,提供可供选择的value,其内容相同,都是一个叫做pref_jpegQuality的array,这个稍后解释;最后为这个条目设定一个默认值即defaultValue,设为100。

添加array值

在res->values下新建arrays.xml文件

内容如下:

这样就创建了一个名字为pref_jpegQuality的array,供上一步的jpeg_quality使用,现在回去看preferences.xml已经没有红色错误提示啦。

主窗口中加入按钮,调用菜单

刚才创建了一个非常简单的设置菜单,接下来让这个菜单通过点击主窗口中的“设置”按钮就能显示出来。

修改activity_main.xml

修改activity_main.xml,在FrameLayout后加入Button,修改后内容如下:

修改MainActivity

修改MainActivity类,在onCreate()方法最后添加

即为设置按钮绑定点击监听,当点击设置按钮时,就显示设置菜单。

是调用Fragment的比较通用的写法,关于具体细节还请自行查找。

修改后的MainActivity内容如下:

运行一下看看

现在就可以运行你的APP啦,现在APP的最右边会有“设置”按钮,点击此按钮后就会在相机预览上显示“照片品质”这个菜单条目,而点击这个条目,就会弹出从50到100共6个选项,其中100已经默认选中。效果如下:

但是现在这个设置菜单很简单,而且没有实际功能,我们接下来进行完善。

动态添加设置条目

像相机支持的分辨率,支持的对焦方式等都因设备而异,我们不大可能在preferences.xml就写死相机支持的分辨率的值;而为了能够让我们的APP能够运行在不同的设备上(如果只需要运行在特定的设备上,会容易许多),我们尝试动态加载设置条目的列表选项。

完善菜单条目

菜单条目名称我们是能够直接写在文件中的,因为不同相机基本都支持这些设置,只是value不同而已。

修改preferences.xml,修改后内容如下:

上面的这些选项基本覆盖到了所有常用的选项(在你读完这篇文章后你还可以自己添加想要的选项)。注意到除了“照片品质”外的所有ListPreference都没有entries和entryValues,这些值就是我们需要在代码中动态添加的;上面的“地理位置”是一个SwitchPreference,就是开关那种样式的,只有两个值。注意到有些条目有defaultValue默认值,是因为这些默认值对于不同相机都是通用的。

这样修改后,由于条目内容不完整,APP暂时就不能运行了

动态添加

获取相机

添加条目的列表选项当然是在SettingsFragment中了。想要知道相机支持的参数,首先需要获得一个打开的相机,这个先不追究,在SettingsFragment中添加

passCamera()用来将相机传输给SettingsFragment,SettingsFragment将相机保存到静态成员变量mCamera中,getParameters()则用来获取相机参数,将相机参数保存到静态成员变量mParameters中。

动态加载预览分辨率

首先以动态加载预览分辨率为例,介绍动态加载过程。预览分辨率即相机预览时屏幕显示的分辨率大小。在SettingsFragment中添加

静态成员变量KEY_PREF_PREV_SIZE存储预览分辨率的key(在preferences.xml定义)。

先看stringListToListPreference(),其首先将List转换为CharSequence[];由getPreferenceScreen().findPreference()获取由key指定的菜单条目;由setEntries()和setEntryValues向这个菜单条目指定用户可见的所有value和代码可见的所有value,就完成了对这个key条目的列表内容的动态加载。

mParameters.getSupportedPreviewSizes()获取相机支持的所有预览分辨率,保存在List中,再由cameraSizeListToListPreference()将List转换为List。

整个过程逻辑挺清晰的,理解起来应该没太大问题。

动态加载曝光补偿

再举个例子,在SettingsFragment中添加

由mParameters.getMinExposureCompensation()获取相机支持的最低曝光补偿,mParameters.getMaxExposureCompensation()获取相机支持的最高曝光补偿,由最低到最高形成一个List,指定key后交给stringListToListPreference()就好了。

全部的动态加载项

下面贴上全部的load,嫌乱可以到DEMO中去看完整代码,注意这里的load一个都不能少。

设置菜单创建时即加载

在onCreate()触发时就调用这些load进行动态加载,在onCreate()尾部添加

向SettingsFragment传入相机

所有上面的代码要执行必须首先获取到一个打开的相机,而CameraPreview正好有一个打开的相机,我们就可以通过MainActivity进行相机的传递。

首先需要修改一下CameraPreview的getCameraInstance()方法,使其返回正在使用的相机,修改getCameraInstance()为

然后在MainActivity中按钮监听之前添加

向SettingsFragment传递来自mPreview的相机。

声明GPS权限

注意到菜单中有个GPS的选项,想要拍到的照片中包含GPS信息,就要在AndroidManifest.xml中声明需要GPS权限。在AndroidManifest.xml中添加

运行一下看看

现在就可以运行你的APP啦,点击“设置”按钮,就会出现很多的设置选项,而点击这些选项就是显示出动态加载得到的列表项目。效果如下:

为菜单条目设定默认值

现在我们来谈谈偏好设置,即Preference的问题。通常来说,在Android中,每个APP的每个设置条目都是一个Preference,这些Preference以键值对(key-value)的形式,存储在每个APP的指定文件中。当APP首次运行时,则只有key没有value;不过可以通过Android提供的方法,加载xml文件中的默认值(defaultValue)到对应的value,Android推荐给每个key都指定默认值,否则默认值为空。记住这些key-value都是写在文件中的,一旦value发生变化,对应文件中value值也发生变化,而且以后启动APP这些值都不会被重置(不过可以通过代码重置)。所以通常的做法就是在APP首次启动时给所有的key都生成默认值即value,然后加载默认值实现不同的偏好;在以后APP启动时,就会读取这些key-value实现不同偏好,即初始化。

给xml中有默认值的添加默认值

虽然在xml文件中指定了默认值,但还是要用代码让其加载。在MainActivity的onCreate()适当位置添加

其中false代表在执行这个方法时,如果key已经有value则不进行任何操作(即不覆盖),否则设置value为xml文件中的指定值。因为APP每次运行都会执行onCreate(),设为false很有必要。

动态添加默认值

仔细点看你会发现,如果没有进行手动设置,设置菜单的“相机预览分辨率”、“照片分辨率”、“对焦模式”是没有默认值的,现在我们就为其添加默认值。因为从“正规”来说,Android只提供了从xml文件添加默认值的方法(就像其他有默认值的条目一样),想要用代码实现默认值就得花点功夫了。

修改SettingsFragment

Update 20160504: 本块内容分割线以下为旧方法,不再采用

我们把目光放到SettingsFragment上,可以创建一个方法setDefault(),就像上面添加静态默认值那样,通过调用setDefault(),来添加动态默认值。但setDefault()只在最初“相机预览分辨率”等没有默认值时才为其指定默认值,否则不进行任何操作。所以setDefault()首先找到“相机预览分辨率”的value值,如果值为空则指定默认值,否则返回。我们还应该注意到,相机本身是有默认值的,只是我们的偏好设置中还没有设置这个默认值,因此我们可以获取到相机的默认值,然后构造成为偏好设置中的格式,将这个值指定为value就可以了。

这样setDefault()代码就出来了

SharedPreferences由MainActivity提供,是操作Preference的接口,既可以读取也可以写入。SharedPreferences.Editor就是编辑Preference,其putString()将key-value对写入到APP中,注意最后需要apply()保存这些更改(也可以用commit(),但效率会低一些)。具体怎么找到value代码很简单,就是通过mParameters获取此时相机预览的参数,然后转换为特定形式的String,就作为value返回了。

这样就完成动态添加默认值,可以发现我们只需要用到SharedPreferences和mParameters,所以只需要在MainActivity中传递了相机之后调用就好了;因为方法是静态方法,调用时甚至都不需要实例化。

我们把目光放到SettingsFragment上,可以创建一个方法setDefault(),在onCreate()中调用setDefault()。但setDefault()只在最初“相机预览分辨率”等没有默认值时才为其指定默认值,否则不进行任何操作。所以setDefault()首先找到“相机预览分辨率”的value值,如果值为空则指定默认值,否则返回;而指定默认值可以直接从其动态加载的value列表中选择第一个就好了。

这样setDefault()代码就出来了

getPreferenceScreen().findPreference()获取菜单条目,setValueIndex(0)则将其value设置为value列表中的第一个。还记得之前APP相机预览一片模糊吗?现在就解决这个问题啦!首先查找对焦方式中是否存在continuous-picture,若存在则设置,否则设置为continuous-video(设备一般都支持这两种对焦模式)。这两种对焦模式都会在镜头移动时重新对焦,就像其他的相机APP那样。

注意setDefault()需要在条目已经动态生成后运行,所以在onCreate()中应当在所有load之后调用这个方法

修改MainActivity

Update 20160504: 本块内容分割线以下为旧方法,不再采用

上面的setDefault()每次调用时都会判断“相机预览分辨率”有没有默认值,因此在MainActivity中就可以放心大胆调用了,我们可以把这个方法的调用和onCreate中的setDefaultValues()放在一起;虽然onCreate()每次都会调用这个方法,但只有在APP第一次运行时才会真正进行动态添加默认值。

onCreate()中涉及到SettingsFragment的代码就是

很简单不必过多解释。

这里要注意一个问题,动态添加默认值会在SettingsFragment的onCreate()方法执行时执行,而SettingsFragment的onCreate()会在主窗口点击了“设置”后才会触发。那么如果APP从来没有点击“设置”就永远不会动态加载默认值了?APP每次运行都会读取设置菜单条目并进行偏好设置,所以在APP第一次运行时必须在不点击“设置”时就触发SettingsFragment的onCreate()。

我想到的一个很“笨”的方法是,在MainActivity的onCreate()中,也对“相机预览分辨率”是否有value进行判断,如果没有value则强行调用SettingsFragment,触发其onCreate();如果有value则不进行任何操作。这样,在MainActivity的onCreate()中,passCamera()后加入如下代码:

即可像静态加载默认值那样,动态添加默认值了。效果如下:

需要注意的地方

Update 20160504: 本块内容分割线以下为旧内容

之前的方法在APP第一次运行时会出现设置菜单需要手动退出,虽然对用户体验影响不太大,但终究是不符合常理。更新后的代码不再存在这个问题,且逻辑更为清晰。

这样实现动态添加默认值后,造成的负面影响就是APP在首次运行时,会自动出现设置菜单,需要手动退出。但这种情况只会在APP第一次运行时产生,以后都不会再出现,所以还是可以忍受的。其实有更复杂一些的方法解决这个问题,但为了这个介绍的简介,就不考虑那些方法了。

APP启动时加载偏好

我们自然希望相机APP每次启动时都能自动加载好之前偏好设置,思路也很简单,在SettingsFragment中创建init()方法,负责设置相机;在MainActivity的onCreate()中,在设置完静态和动态默认值后调用init()方法,完成相机设置。

修改SettingsFragment

在SettingsFragment中添加

SharedPreferences即APP存储的key-value对;getString()即获取指定key的value值,对于ListPreference是getString(),对于SwitchPreference是getBoolean()。这些set就是分别修改相机参数mParameters的不同属性,很简单不详细解释了。最后,首先停止相机预览,然后应用修改后的mParameters到相机,最后再开始相机预览。这样就完成了相机的偏好设置。

修改MainActivity

在MainActivity的onCreate()设置完静态和动态默认值后添加

其中PreferenceManager.getDefaultSharedPreferences(this)就是得到此APP的SharedPreferences。

运行一下看看

打开APP,修改偏好设置后关闭APP(需要退出后台,下一篇文章会介绍一个不需要这么麻烦的方法),再次打开APP就能看到效果了。

条目value变化时立即应用设置

我们肯定不希望修改设置后,需要重启APP才能看到效果。现在来实现修改设置后,相机立即应用新的设置。

我们只需要监听条目value变化就好了,一旦出现变化,就根据其key立即应用新的value到相机。

给SettingsFragment添加监听接口

修改

OnSharedPreferenceChangeListener为监听Preference变化的接口

给SettingsFragment添加监听事件

在SettingsFragment中添加

onSharedPreferenceChanged()为监听事件回调,干的事情就像之前说的,代码也很简单。onResume()和onPause()是Android推荐写法,防止由于Fragment的不断调用导致事件监听失效。

运行一下看看

现在在APP中修改设置马上就能看到效果了!

设置菜单中显示ListPreference的当前值

目前功能已经全部实现了,现在做一点美化。设置菜单中的ListPreference只显示了其条目的标题,而当前用户可见的value只有在点击条目后才会显示,现在就来实现让当前值直接显示在条目上。

添加方法

让ListPreference显示当前值,就是为其summary赋值,即将当前值赋给其summary。

在SettingsFragment中添加

initSummary()处理全部的Preference,主要是处理含有PreferenceGroup的情况,这个APP目前没有这个情况,但还是保留这个功能。updatePrefSummary()则处理具体的ListPreference,将其用户可见的值getEntry()通过setSummary()赋给summary。

调用方法

有两个调用上述方法的地方。

首先是SettingsFragment的onCreate()中,给所有的Preference都设置summary。在onCreate最后添加

其次是在onSharedPreferenceChanged()中,每次条目value发生变化时,summary也随机变化。在onSharedPreferenceChanged()最头上添加

运行一下看看

现在设置菜单的ListPreference都显示其当前值了。效果如下:

一点唠叨

现在看来实现偏好设置还是有些麻烦的,其实主要难点在动态加载和默认值上,为了能够自适应不同设备就是要这么折腾。像系统自带的相机可能就可以根据设备本身直接将分辨率等都写到xml文件中,这样工作量小了不少。本篇还是没有实现基本的拍照功能,不过已经是一步之遥了呢。

DEMO

参考

Android添加拍照功能,Android相机开发(二): 给相机加上偏好设置相关推荐

  1. Android添加拍照功能,Android开发实现拍照功能的方法实例解析

    本文实例讲述了Android开发实现拍照功能的方法.分享给大家供大家参考,具体如下: 解析: 1)判断是否有摄像头checkCameraHardware(this) 2)获得相机camera = Ca ...

  2. Android添加拍照功能,Android自定义相机,添加水印

    很多app都要求自定义一个相机,类似违章查询拍照,美图相机之类的应用都要求自定义相机,网上的例子大多数我也看过,很多例子是有坑的,存在各种各样的问题,常见的就是问题就是适配性很差,这里我分享一下我做的 ...

  3. android 静默拍照功能,Android实现静默拍照功能

    本文实例为大家分享了Android实现静默拍照功能的具体代码,供大家参考,具体内容如下 1.申请权限(6.0以后要动态申请) 2.创建相机工具类 CameraPreview: public class ...

  4. android 添加附件功能,Android实现带附件的邮件发送功能

    本文实例讲解了基于基于jmail实现android邮件发送功能,分享给大家供大家参考,具体内容如下 在android上发送邮件方式: 第一种:借助gmail app客户端,缺点是必须使用gmail帐号 ...

  5. android添加截图功能,Android应用开发之Android 5.0及以上编程实现屏幕截图功能的方法...

    本文将带你了解Android应用开发Android 5.0及以上编程实现屏幕截图功能的方法,希望本文对大家学Android有所帮助. 本文实例讲述了Android   5.0及以上编程实现屏幕截图功能 ...

  6. icordova拍照_ionic2/3实战-添加拍照功能cordova-plugin-camera

    效果演示 源代码已上传到github 由于ionic版本更新较快,有些写法可能改变来不及更新简书,请以github代码为准 ionic2.0插件的使用方式和ionic3.0已不一样 ionic2实战- ...

  7. android开启照相功能,Android打开系统相机并拍照的2种显示方法

    本文实例为大家分享了Android打开系统相机并拍照的具体实现代码,供大家参考,具体内容如下 目标效果: 第二张为点击第一个按钮拍照后显示的,比较模糊,第三章为点击第二个按钮拍照后显示的,比较清楚. ...

  8. android裁剪图片功能,Android实现拍照、选择图片并裁剪图片功能

    一. 实现拍照.选择图片并裁剪图片效果 按照之前博客的风格,首先看下实现效果. 二. uCrop项目应用 想起之前看到的Yalantis/uCrop效果比较绚,但是研究源码之后发现在定制界面方面还是有 ...

  9. android 微信相册功能,Android仿微信选择图片和拍照功能

    本文实例为大家分享了 Android微信选择图片的具体代码,和微信拍照功能,供大家参考,具体内容如下 1.Android6.0系统,对于权限的使用都是需要申请,选择图片和拍照需要申请Manifest. ...

最新文章

  1. 人脸分割 人脸解析 源码推荐
  2. 深度学习的seq2seq模型——本质是LSTM,训练过程是使得所有样本的p(y1,...,yT‘|x1,...,xT)概率之和最大...
  3. Codeforces 1336E Chiori and Doll Picking (子集和变换、线性基、阈值算法、状压 DP、组合计数)...
  4. mysql show slave_Mysql复制 show slave status
  5. Bash脚本教程之脚本入门
  6. python map zip_Python学习笔记(九) map、zip和filter函数
  7. 又一个查询,哥整了一天,NND有点受不鸟了
  8. ​最高要价 8888元,小米 11 邀请函现身闲鱼;荣耀与微软签署全球 PC 合作协议;Xfce 4.16 发布|极客头条...
  9. 路由器故障排除的思路与理论
  10. (11) python 使用baostock获取历史A股K线数据
  11. 安装tensorflow出现的问题
  12. 【一键激活win8.1系统】
  13. 关于深度优先遍历和广度优先遍历的一些深入思考
  14. 出海推荐 (出海服务器盘点)
  15. Win10系统不兼容驱动怎么办?
  16. 2022广东省安全员B证第四批(项目负责人)上岗证题目及在线模拟考试
  17. dedecms如何自定义分页样式
  18. exe反编译为pyc,再反编译为py
  19. 因果效应与uplift模型
  20. 好久没来这里了,冒个泡

热门文章

  1. offsetLeft,Left,clientLeft的区别
  2. ural 1066 uva 1555
  3. python繁体中文到简体中文的转换
  4. pytrhon画图matplolib
  5. LinkedList 源码小解
  6. grafana-----Time Range Controls
  7. ant copy 复制文件用法
  8. Eclipse导入maven项目报Resources文件夹红叉问题解决方案
  9. CentOS网络设置 couldn‘t resolve host ‘mirrorlist.centos.org问题解决
  10. flex 布局,flex-grow 宽度未等比放大问题解决办法