android 原生camera——设置模块修改
Posted _solary
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了android 原生camera——设置模块修改相关的知识,希望对你有一定的参考价值。
,
此篇博客是记一次客户需求修改,从上周五到现在正好一周时间,期间的各种酸爽,就不说了,还是来看大家关注的技术问题吧。
首先看下以前效果和修改后的效果:
修改前:修改后:
不知道有没有看明白,我在简单说下,没修改前Camera 设置中是有两个选项的一个负责预览大小(显示的宽高比如16:9),一个负责照片大小(如1600x1200),修改后 只要一个Picture Size 即负责切换预览大小也负责照片大小。
看到这是不是觉的好简单只要在Picture Size添加几个选项,然后在点击不同像素的选项时同样也能切换比例,。。。。。。。。。。。。。。其实呢就是这么简单!!!
但是前提是需要对系统的Camera有一定的了解,不然就和我一样苦逼的搞了一周最后发现只要不到50行代码就可以实现这个功能。所以改这个需求也是我对Camera的一次探索之旅,如果你的工作中也要涉及到camera模块,但对它又比较陌生就和我一起开始学习camera模块吧。
说明一下这里主要围绕文章开头的那个需求来说的,也就是camera设置模块,毕竟我只研究了一周,对它的整体设计
还有待深入研究,这里就不误人子弟了 O(∩_∩)O哈哈~
还是看那个需求 这里分为三步去解决
1、在Picture Size中添加需要的所以像素;
2、隐藏Preview Size;
3、添加各个像素选中后切换比例的功能
但是 首先呢 我们还是要对Camera有个基本的认识看看系统Camera的整体构架(虽然今天只是了解一小部分但是框架还是要了解一点点滴)
Camera根据android 架构从上至下可分为
1)Applications: 最上层的应用,编译后生成Camera APK;
2)Application Framework: 主要为Applications提供API;
3)JNI: 使Application Framework和Libraries可交互;
4)Libraries: 包括Camera Framework和Camera Service(camera service和camera client);
5)HAL: 硬件抽象层, 用来链接driver和 Camera Service;
6)Kernel: image sensor driver的实作.
在来一张图 是山寨之王MTK提供的:
这个是MTK对camera进行了一下定制 他们主要对HAL和Kernel层着两层进行了比较大的改动
好了就大致提一下,如果全部都搞通了你就很niubility了。
我们还是看应用层的东东吧
这里就是整个界面的上有用到的控件了,camera的设计只有一个activity叫CameraActivity当然看到CameraLauncher的话也是CameraActivity,CameraLauncher只是CameraActivity的别名。
好了看了上面这么多高大上的东东,我们要研究的呢只是 这一小块,而且还不会全部都看
开始我们也需要对Setting的流程有一个了解 看上图可以知道SettingManager主要就是设置模块的管理类具体的话大家有时间可以点进去看下,这里从点击这个按钮开始
public void onClick(View view)
if (view == mIndicator)
if (!mShowingContainer)
showSetting();
else
collapse(true);
它会去调用showSetting() 然后一路调用到initializeSettings 开始布局设置的界面,至于其中每项显示的内容是从xml文件的camera_preferences.xml中获取,而camera_preferences.xml的解析需要从CameraDeviceCtrl中开始跟 ,最后解析的数据会保存到SettingItem类中,具体流程的话需要大家去跟一下,我其实也没太怎么跟,领导天天在屁股后面问搞好了没,这感觉你懂的。
如果你跟过了上面的显示流程(不跟也没什么关系),我们来看下关于Picture Size 和preview Size选项中的数据来源,如果跟进camera_preferences.xml看的话 你会说 这里面不是有数据么
<ListPreference
camera:key="pref_camera_picturesize_key"
camera:title="@string/pref_camera_picturesize_title"
camera:entries="@array/pref_camera_picturesize_entries"
camera:entryValues="@array/pref_camera_picturesize_entryvalues" />
但是有没有发现这里的数据并没有都显示在列表里面,这是为什么呢,
带着这个疑问我们通过打log发现刚进去Camera的时候有很多
这样的信息 通过字面意思可以发现一个是支持的size 一个是可使用的list,而 buildEnableList里面的就是在设置界面显示的size ,
public static String buildEnableList(String[] list, String current)
String listStr = null;
if (list != null)
listStr = ENABLE_LIST_HEAD + current + ENABLE_LIST_SEPERATOR;
List<String> uniqueList = new ArrayList<String>();
for (int i = 0, len = list.length; i < len; i++)
if (uniqueList.contains(list[i]))
continue;
uniqueList.add(list[i]);
if (i == (len - 1))
listStr += list[i];
else
listStr += (list[i] + ENABLE_LIST_SEPERATOR);
Log.d(TAG, "buildEnableList(" + current + ") return " + listStr);
return listStr;
可以发现String[] list就是可用的列表,所以需要找到buildEnableList是谁调用的
通过跟踪buildEnableList的调用堆栈,发现是在CommonRule类中调用public void execute()来获取size的list的
overrideValue = SettingUtils.buildEnableList(
resultValuesAfterFilter.toArray(values), resultValue);
当然这个方法里面有很多事情要做,辣么多的代码,我就不拷贝了 可以看出来resultValuesAfterFilter.toArray(values)就是它,我们的list, 继续跟进去
List<String> resultValues = mResults.get(index);
List<String> resultValuesAfterFilter = filterUnsupportedValue(resultValues, mResultKey);
很明朗了filterUnsupportedValue字面意思过滤掉不匹配的 所以resultValues 包括所有的尺寸
而resultValues = mResults.get(index) 继续看看mResults 是哪儿赋值的
public void addLimitation(String condition, List<String> result, MappingFinder mappingFinder)
mConditions.add(condition);
mResults.add(result);
mMappingFinder.add(mappingFinder);
private void createRuleFromRestrictions()
Restriction[] restrictionArray = SettingDataBase.getRestrictions();
...
rule.addLimitation(conditionValues.get(k), resultValues, mappingFinder);
...
当然还是打印堆栈最后发现createRuleFromRestrictions中有调用 最后追到了SettingDataBase.getRestrictions();中
public static Restriction[] getRestrictions()
return RESTRICTIOINS;
这个是一个Restriction数组代码量好大,有条件的话最好自己去看看 我就不贴了
private static final String[] PICTURE_SIZE_4_3
private static final String[] PICTURE_SIZE_16_9
最后发现没过滤前的数据就是上面这两个数组, 我现在默认是全屏也就是16:9 所以刚进去的时候是用的PICTURE_SIZE_16_9这个数组,按我们现在的需求 只要把4:3的数据都放到这个数组里面就好了。
有没有发现对camera不了解的话这一步其实也要跟很久呢。
至此第一步合并数据算是完成了,
第二部需要把preview size给隐藏了 这个应该比较简单了方法有很多 我这里是通过
public void initialize(ArrayList<ListPreference> listItems)
//by linyu.li for mixture picturesize 20161215 start
if (listItems != null)
for (int i = 0; i < listItems.size(); i++)
if (listItems.get(i) != null)
if (listItems.get(i).getKey()
.equals("pref_camera_picturesize_ratio_key"))
continue;
mListItem.add(listItems.get(i));
// mListItem = listItems;
//by linyu.li for mixture picturesize 20161215 end
mListItemAdapter = new SettingsListAdapter();
mSettingList.setAdapter(mListItemAdapter);
mSettingList.setOnItemClickListener(this);
mSettingList.setSelector(android.R.color.transparent);
mSettingList.setOnScrollListener(this);
这种方式做的
来看最后一步 切换比例
设置中点击事件的调用堆栈如上图
当时通过跟踪发现主要在SettingCtrl的
public void onSettingChanged(String settingKey, String value)
...
onSettingChanged(parameters, currentCameraId, settingKey, value);
...
private void onSettingChanged(Parameters parameters, int currentCameraId, String key,
String value)
...
//by linyu.li for mixture picturesize 20161215 start
if (SettingConstants.KEY_PICTURE_SIZE.equals(key))
String prefName0 = mContext.getPackageName() + "_preferences_" + 0;
String prefName1 = mContext.getPackageName() + "_preferences_" + 1;
SharedPreferences sharedPreferences0 = mContext.getSharedPreferences(prefName0, Context.MODE_PRIVATE);
SharedPreferences.Editor editor0 = sharedPreferences0.edit();
SharedPreferences sharedPreferences1 = mContext.getSharedPreferences(prefName1, Context.MODE_PRIVATE);
SharedPreferences.Editor editor1 = sharedPreferences1.edit();
int index = value.indexOf('x');
float width = Integer.parseInt(value.substring(0, index));
float height = Integer.parseInt(value.substring(index + 1));
Log.i(TAG,"heightonseting === "+height+",width === "+width+"w/h = "+width/height);
if(currentCameraId==0)
if(width/height==(4f/3f))
editor0.putString(SettingConstants.KEY_PICTURE_RATIO, "1.3333");
onSettingChanged(parameters, currentCameraId, SettingConstants.KEY_PICTURE_RATIO,
"1.3333");
else if(width/height==(16f/9f)||width/height>1.7f)
editor0.putString(SettingConstants.KEY_PICTURE_RATIO, "1.7778");
onSettingChanged(parameters, currentCameraId, SettingConstants.KEY_PICTURE_RATIO,
"1.7778");
else
if(width/height==(4f/3f))
editor1.putString(SettingConstants.KEY_PICTURE_RATIO, "1.3333");
onSettingChanged(parameters, currentCameraId, SettingConstants.KEY_PICTURE_RATIO,
"1.3333");
else if(width/height==(16f/9f)||width/height>1.7f)
editor1.putString(SettingConstants.KEY_PICTURE_RATIO, "1.7778");
onSettingChanged(parameters, currentCameraId, SettingConstants.KEY_PICTURE_RATIO,
"1.7778");
editor0.apply();
editor1.apply();
//if(!SettingConstants.KEY_PICTURE_RATIO.equals(key))
//by linyu.li for mixture picturesize 20161215 end
executeRule(parameters, currentCameraId, key);
下面就是我添加的代码应该也好懂吧。
到这里我的这个需求就完美的解决了,可以发现这个需求其实很简单,但是还是需要对camera的设计流程有一定的了解,并且通过分析android原生的app,可以发现里面的设计思想真的很值得我们学习,就比如,executeRule(parameters, currentCameraId, key); 中当点击preview size时 会递归的再次调用onSettingChanged来切换 picture size 从而使picture size 和preview size 对应,可惜我的这个需求老板一直在催,就只是简单递归了一下,其实我觉得可以用更少的代码代替上面的那一堆的代码,待我闲时在分析吧。我要闪人了,连续一周加班到现在,当然今天是加班写博客 O(∩_∩)O哈哈~。
以上是关于android 原生camera——设置模块修改的主要内容,如果未能解决你的问题,请参考以下文章