,


此篇博客是记一次客户需求修改,从上周五到现在正好一周时间,期间的各种酸爽,就不说了,还是来看大家关注的技术问题吧。


首先看下以前效果和修改后的效果:

修改前:修改后:

不知道有没有看明白,我在简单说下,没修改前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——设置模块修改的更多相关文章

  1. 修改Android 4.2.2的原生Camera引出的java.lang.UnsatisfiedLinkError: Native method not found,及解决方法

    修改Android 4.2.2的原生Camera应用,做一些定制,将Camera的包名从之前的 package com.android.* 修改成了com.zhao3546.*. 调整后,应用可以正常 ...

  2. React Native Android原生模块开发实战|教程|心得|怎样创建React Native Android原生模块

    尊重版权,未经授权不得转载 本文出自:贾鹏辉的技术博客(http://blog.csdn.net/fengyuzhengfan/article/details/54691503) 告诉大家一个好消息. ...

  3. 【转】eclipse android 设置及修改生成apk的签名文件 -- custom debug keystore

    原文网址:http://hold-on.iteye.com/blog/2064642 android eclipse 设置及修改生成apk的签名文件 1. 问题: 平时在使用eclipse进行andr ...

  4. Android 系统默认参数的修改

    转自: http://www.th7.cn/Program/Android/201505/447097.shtml 写在前面的话 一般在新项目开始之初,我们需要针对客户需求进行各种系统默认属性的配置, ...

  5. 【转】Android ROM研究---Android build system增加模块

    原文网址:http://hualang.iteye.com/blog/1141315 Android build system就是编译系统的意思 在我们需要向自己编译的源代码中增加模块的时候,需要一些 ...

  6. Android 访问权限设置

    Android开发应用程序时,有时我们要用到很多权限, 今天我就收集了一些开发时可能用到的开启权限设置. 这些权限都是在 AndroidManifest.xml设置. 设置方法 <uses-pe ...

  7. MTK6577+Android之Camera驱动

    MTK6577+Android之Camera驱动 <MTK安卓平台的Camera效果在线调试> 1.     Camera拍照相关概念 1.1  ISP isp--(Image Signa ...

  8. Android 开发权限设置中英对照说明详解

    android.permission.ACCESS_CHECKIN_PROPERTIES 允许读写访问 "properties"表在checkin数据库中,改值可以修改上传( Al ...

  9. Android原生编解码接口 MediaCodec 之——踩坑

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明.本文链接:https://blog.csdn.net/gb702250823/article/d ...

随机推荐

  1. flask开发表单

    from flask import Flask from flask import render_template from flask import request from flask impor ...

  2. Array方面Js底层代码学习记录

    一..clear() →Array function clear() { this.length = 0; return this; } 返回清除item的空数组. 例子: var fruits = ...

  3. 深入浅出理解 TCP/IP 协议 (一)

    文章转自:https://www.cnblogs.com/onepixel/p/7092302.html TCP/IP 协议栈是一系列网络协议的总和,是构成网络通信的核心骨架,它定义了电子设备如何连入 ...

  4. JavaScript 随机数相关算法

    // Math.ceil() 返回大于等于数字参数的最小整数(取整函数),对数字进行上舍入 // Math.floor() 返回小于等于数字参数的最大整数,对数字进行下舍入 // Math.round ...

  5. JS 语言核心(JavaScript权威指南第六版)(阅读笔记)

    前言: 对于程序员,学习是无止境的,知识淘换非常快,能够快速稳固掌握一门新技术,是一个程序员应该具备的素质.这里将分享本人一点点不成熟的心得. 了解一门语言,了解它的概念非常重要,但是一些优秀的设计思 ...

  6. [BZOJ 4589]Hard Nim

    Description 题库链接 两人玩 \(nim\) 游戏,\(n\) 堆石子,每堆石子初始数量是不超过 \(m\) 的质数,那么后手必胜的方案有多少种.对 \(10^9+7\) 取模. \(1\ ...

  7. [WC 2010]重建计划

    Description Input 第一行包含一个正整数N,表示X国的城市个数. 第二行包含两个正整数L和U,表示政策要求的第一期重建方案中修建道路数的上下限 接下来的N-1行描述重建小组的原有方案, ...

  8. 计蒜客 NOIP模拟赛(3) D1T1火山喷发

    火山喷发对所有附近的生物具有毁灭性的影响.在本题中,我们希望用数值来模拟这一过程. 在环境里有 nnn 个生物分别具有 A1,A2,⋯,An​​点生命值,一次火山喷发总计 M轮,每轮造成 1点伤害,等 ...

  9. 洛谷P3233 [HNOI2014]世界树

    虚树= = #include<cstdio> #include<cstdlib> #include<algorithm> #include<cstring&g ...

  10. 【HNOI2017】大佬

    题目描述 人们总是难免会碰到大佬.他们趾高气昂地谈论凡人不能理解的算法和数据结构,走到任何一个地方,大佬的气场就能让周围的人吓得瑟瑟发抖,不敢言语. 你作为一个 OIER,面对这样的事情非常不开心,于 ...