Camera中的设置选项,主要的是控制Camera的显示效果和动作的。下面简单从代码的角度分析一下Camera的设置。
6.1 简单介绍
此部分主要介绍的是如何快速地定位设置项的位置,找到对应key和value的值。
首先根据字符串找到相应的菜单的位置,一般都会在如图的两个文件里面,分别对应拍照和录像模式的菜单;
就拿取景模式举例:根据“取景模式”这四个字对应的name值,找到xml中的位置;
<string name="pref_camera_scenemode_title"
msgid="5709932164781367066">"取景模式"</string>
<IconListPreference
camera:key=*"pref_camera_scenemode_key"*
camera:defaultValue=*"@string/pref_camera_scenemode_default"*
camera:title=*"@string/pref_camera_scenemode_title"*
camera:icons
=*"@array/camera_scenemode_icon"*
camera:singleIcon=*"@drawable/ic_settings_scenemode"*
camera:largeIcons
=*"@array/camera_scenemode_icon"*
camera:thumbnails=*"@array/scenemode_thumbnails"*
camera:entries=*"@array/pref_camera_scenemode_entries"*
camera:entryValues=*"@array/pref_camera_scenemode_entryvalues"*/>
再根据其key值找到相应的value值。首先根据字符串找到在CameraSettings.java中的相应的位置,
public static final StringKEY_SCENE_MODE =
"pref_camera_scenemode_key";
然后根据其变量KEY_SCENE_MODE在代码中查找所有相关的代码,查看相应的功能逻辑,根据相应的功能逻辑做处理;
6.2 xml文件的解析器
首先设置菜单的显示是通过xml文件布局实现的实现方式,以设置取景模式为例:
Camera界面资源文件在xml/camera_preferences.xml文件中:
<IconListPreference
camera:key=*"pref_camera_scenemode_key"*
camera:defaultValue=*"@string/pref_camera_scenemode_default"*
camera:title=*"@string/pref_camera_scenemode_title"*
camera:icons
=*"@array/camera_scenemode_icon"*
camera:singleIcon=*"@drawable/ic_settings_scenemode"*
camera:largeIcons
=*"@array/camera_scenemode_icon"*
camera:thumbnails=*"@array/scenemode_thumbnails"*
camera:entries=*"@array/pref_camera_scenemode_entries"*
camera:entryValues=*"@array/pref_camera_scenemode_entryvalues"*/>
PreferenceInflater.java(src/com/android/camera/PreferenceInflater.java)是个解析器,用来解析xml中所有的设置项,包括照相机和摄像机的,具体看解析哪个xml文件,然后保存在自己的成员变量list中,list就是ArrayList类型。
inflate(mContext.getResources().getXml(resId))就是得到的就是XmlPullParser对象,这个就是该xml文件的解析器。
6.3 自定义属性的添加
declare-styleable是给自定义组件添加自定义属性用的,自定义属性在res/values/attrs.xml
<resources>
<declare-styleable name=*"Theme.GalleryBase"*>
<attr name=*"listPreferredItemHeightSmall"*
format=*"dimension"* />
<attr name=*"switchStyle"*
format=*"reference"* />
</declare-styleable>
<!--
Camera resources below -->
<declare-styleable name=*"CameraPreference"*>
<attr name=*"title"*
format=*"string"* />
</declare-styleable>
<declare-styleable name=*"ListPreference"*>
<attr name=*"key"* format=*"string"* />
<attr name=*"defaultValue"* format=*"string|reference"*
/>
<attr name=*"entryValues"* format=*"reference"*
/>
<attr name=*"entries"*
format=*"reference"* />
<attr name=*"labelList"*
format=*"reference"* />
</declare-styleable>
<declare-styleable name=*"IconIndicator"*>
<attr name=*"icons"*
format=*"reference"* />
<attr name=*"modes"*
format=*"reference"* />
</declare-styleable>
<declare-styleable name=*"IconListPreference"*>
<!-- If a preference does not have individual icons for each entry, it
can has a single icon to represent it. -->
<attr name=*"singleIcon"*
format=*"reference"* />
<attr name=*"icons"*
/>
<attr name=*"largeIcons"*
format=*"reference"* />
<attr name=*"thumbnails"*
format=*"reference"* />
<attr name=*"images"*
format=*"reference"* />
</declare-styleable>
</resources>
6.4 自定义属性的使用
camera_preferences.xml及video_preferences.xml的菜单选项包括ListPreference和IconListPreference两种。ListPreference是列表型设置项,是负责存储数据的,它决定了显示什么数据,IconListPreference继承自ListPreference,是带图标的列表菜单。
下面看ListPreference如何使用自定义属性:
public ListPreference(Context context, AttributeSet
attrs) {
super (context,attrs);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ListPreference,0, 0);
mKey = CameraUtil.checkNotNull(a.getString(R.styleable.ListPreference_key));
// We allow the defaultValue attribute to be a string or an array of
// strings. The reason we need multiple default values is that some
// of them may be unsupported on a specific platform (for example,
// continuous auto-focus). In that case the first supported value
// in the array will be used.
翻译如下:
我们允许“默认值”属性是一个字符串或数组串.
我们需要多个默认值的原因是:它们可能在特定平台上不受支持(例如,连续、自动对焦)。
在这种情况下,第一个支持的值将在数组中使用。
int attrDefaultValue = R.styleable.ListPreference_defaultValue;
TypedValue tv = a.peekValue(attrDefaultValue);
if (tv != null && tv.type == TypedValue.TYPE_REFERENCE) {
mDefaultValues = a.getTextArray(attrDefaultValue);
} else {
mDefaultValues = **new** CharSequence[1];
mDefaultValues[0] = a.getString(attrDefaultValue);
}
setEntries(a.getTextArray(R.styleable.ListPreference_entries));
setEntryValues(a.getTextArray(R.styleable.ListPreference_entryValues));
setLabels(a.getTextArray(R.styleable.ListPreference_labelList));
a.recycle();
}
在camera_preferences.xml菜单项里包含了下面信息:
packages/apps/Camera2/res/values/arrays.xml定义了菜单项成员及其键值
<!--
Camera Preferences Scene Mode dialog box entries -->
<string-array name=*"pref_camera_scenemode_entries"* translatable=*"false"*>
<item>@string/pref_camera_scenemode_entry_night</item>
<item>@string/pref_camera_scenemode_entry_auto</item>
</string-array>
<string-array name=*"pref_camera_scenemode_labels"*>
<item>@string/pref_camera_scenemode_label_night</item>
<item>@string/pref_camera_scenemode_label_auto</item>
</string-array>
<array name=*"pref_camera_scenemode_icons"*>
<item>@drawable/ic_sce_night</item>
<item>@drawable/ic_sce_off</item>
</array>
<string-array name=*"pref_camera_scenemode_entryvalues"* translatable=*"false"*>
<item>night</item>
<item>auto</item>
</string-array>
由这些定义,我们可以看出,Android的Camera应用程序支持几种情景模式。但是硬件并非都支持。所以最终的菜单中只会显示这几种当中底层硬件所支持的,如果硬件支持的与其中任何一种都不匹配,则不会显示出“取景模式”菜单。
注意:进入相机菜单丢失问题:硬件不支持导致菜单不可见;
6.5 加载菜单项
Xml文件的内容以及解析都讲完了,下面看下是怎么把菜单项加载进去的。
Packages/apps/Camera2/src/com/android/camera/CameraSettings.java
CameraSettings是相机设置项的总的接口,里面定义了菜单设置项的KEY值,
public static final String *KEY_SCENE_MODE = "pref_camera_scenemode_key";
这些KEY值是跟camera_preferences.xml里的菜单项对应的,接着在private voidinitPreference(PreferenceGroupgroup)中添加
ListPreference sceneMode = group.findPreference(*KEY_SCENE_MODE*);
来获取键值为KEY_SCENE_MODE的菜单项,接着
if (sceneMode != null) {
filterUnsupportedOptions(group, sceneMode, mParameters.getSupportedSceneModes());
}
函数mParameters.getSupportedSceneModes()将获取camera 硬件支持的SceneMode的项,其定义在文件Framework/base/core/java/android/hardware/camera.java中。
sceneMode相当于从camera_preferences.xml中获取的sceneMode的项,它俩之间需要做个匹配,如果camera硬件支持的项不存在或者只有一项,则就会从group中去掉sceneMode的菜单。
6.6 效果设置:
我们通过下面代码获取当前菜单项所选中的值,如果是改变值则是改变值,否则就是默认值:
mSceneMode= mPreferences.getString(CameraSettings.*KEY_SCENE_MODE*, mActivity.getString(R.string.pref_camera_scenemode_default));
如果改变值把选中的值设置到底层:
if(CameraUtil.isSupported(mSceneMode,mParameters.getSupportedSceneModes())) {
if (!mParameters.getSceneMode().equals(mSceneMode)) {
mCameraDevice.setSceneDetectionCallback(null,null);
mParameters.setSceneMode(mSceneMode);
sceneChanged = true;
}
}
因为KEY值都存放在SharedPreferences 里,保存Camera设置的所有值,每次启动Camera都会重新读取保存的值。
6.7 界面菜单Camera 切换模式功能:
CameraControls是相机界面下面的控制栏,在其xml文件里显示自定义view项
<com.android.camera.ui.ModuleSwitcher
android:id="@+id/camera_switcher"
style="@style/SwitcherButton"
android:layout_gravity="bottom|left"
android:layout_marginBottom="-11dip"
android:contentDescription="@string/accessibility_mode_picker"
/>
Packages/apps/Camera2/src/com/android/camera/ui/ModuleSwitcher.java
此类定义了ModuleSwitchListener接口
public interface ModuleSwitchListener {
public void onModuleSelected(int i);
public void onShowSwitcherPopup();
}
ModuleSwitchListener是模式改变监听器当模式改变时通知调用者。
initializeDrawables()初始化了所有模式的图片,只调用一次,主要是给
int[] drawids = new int[numDrawIds];
int[] moduleids = new int[numDrawIds];
两个变量赋值,这两个变量主要是用于模式图标的显示。
网友评论