参考:http://blog.youkuaiyun.com/wxzking/article/details/6225702
amera设置取景模式流程分析:
在Android平台上,设置菜单有两种方式:通过XML布局来实现和通过Menu.add方法实现。Camera系统中的菜单可以说是通过XML布局来实现的,但它的实现过程并非像一般文章里介绍的那样简单。所以,在此我就以设置取景模式的菜单为例,将Camera系统中菜单的设置流程作以简单介绍。
1、资源文件分析
在packages/apps/Camera/res/xml/有两个xml文件,分别为Camera_preferences.xml(拍照的xml文件)和Video_preferences.xml(视频录制的xml文件)。这两个文件中列举了Camera和Video Camera两种模式下的不同菜单信息。以Camera_preferences.xml为例,其中关于Scence mode的菜单项信息为:
<PreferenceGroup
。。。
<IconListPreference
camera:key="pref_camera_scenemode_key"
camera:defaultValue="@string/pref_camera_scenemode_default"
camera:title="@string/pref_camera_scenemode_title"
camera:singleIcon="@drawable/ic_scn_holo_light"
camera:entries="@array/pref_camera_scenemode_entries"
camera:entryValues="@array/pref_camera_scenemode_entryvalues" />
。。。
</PreferenceGroup>
问题:什么是PreferenceGroup和IconListPreference
pref_camera_scenemode_title的定义在packages/apps/Camera/res/values/strings.xml中,其中有如下的定义:
<!-- Settings screen, Select Scene mode -->
<string name="pref_camera_scenemode_title">Scene mode</string>
这个是相机设置上的标题,点击这个控件就会选择相应的取景模式
pref_camera_scenemode_entries定义在
packages/apps/Camera/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_auto</item>
<item>@string/pref_camera_scenemode_entry_landscape</item>
<item>@string/pref_camera_scenemode_entry_portrait</item>
<item>@string/pref_camera_scenemode_entry_night</item>
<item>@string/pref_camera_scenemode_entry_night_portrait</item>
<item>@string/pref_camera_scenemode_entry_action</item>
<item>@string/pref_camera_scenemode_entry_theatre</item>
<item>@string/pref_camera_scenemode_entry_beach</item>
<item>@string/pref_camera_scenemode_entry_snow</item>
<item>@string/pref_camera_scenemode_entry_sunset</item>
<item>@string/pref_camera_scenemode_entry_steadyphoto</item>
<item>@string/pref_camera_scenemode_entry_fireworks</item>
<item>@string/pref_camera_scenemode_entry_sports</item>
<item>@string/pref_camera_scenemode_entry_party</item>
<item>@string/pref_camera_scenemode_entry_candlelight</item>
</string-array>
由上面这些定义,我们可以看出,Android的Camera应用程序支持auto到candlelight的取景模式。但是硬件并非都支持这些模式的。所以最终的菜单中只会显示这几种模式当中中底层硬件所支持的,如果硬件支持的取景模式与其中任何一种都不匹配,则不会显示出“Scene mode”的菜单。
2、菜单的创建
在文件Packages\apps\legacycamera\src\com\android\camera\Camera.java中,函数onCreateOptionsMenu()用来创建Camera系统的菜单。其具体定义如下:
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
if (mIsImageCaptureIntent) {
// No options menu for attach mode.
return false;
} else {
addBaseMenuItems(menu);
}
return true;
}
在非Video Camera模式下,mIsImageCaptureIntent为true,此处将不做任何处理。当mIsImageCaptureIntent为false时,即Video Camera模式下,将调用函数addBaseMenuItems()来创建Video Camera的菜单。取景模式的菜单不会出现在Video Camera模式下,所以就不对addBaseMenuItems()函数分析了。
在Camera.java文件中定义了类MainHandler,它里面只有一个成员函数handleMessage(),它用来处理Camera应用程序中的message。
private class MainHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case CLEAR_SCREEN_DELAY: {
getWindow().clearFlags(
WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
break;
}
case FIRST_TIME_INIT: {
initializeFirstTime();
break;
}
case SET_CAMERA_PARAMETERS_WHEN_IDLE: {
setCameraParametersWhenIdle(0);
break;
}
case CHECK_DISPLAY_ROTATION: {
// Set the display orientation if display rotation has changed.
// Sometimes this happens when the device is held upside
// down and camera app is opened. Rotation animation will
// take some time and the rotation value we have got may be
// wrong. Framework does not have a callback for this now.
if (Util.getDisplayRotation(Camera.this) != mDisplayRotation) {
setDisplayOrientation();
}
if (SystemClock.uptimeMillis() - mOnResumeTime < 5000) {
mHandler.sendEmptyMessageDelayed(CHECK_DISPLAY_ROTATION, 100);
}
break;
}
case SHOW_TAP_TO_FOCUS_TOAST: {
showTapToFocusToast();
break;
}
case UPDATE_THUMBNAIL: {
mImageSaver.updateThumbnail();
break;
}
}
}
}
Message FIRST_TIME_INIT应该在类Camera初始化时就会被处理,我是这样猜的,只是还没有找到具体的code。
initializeFirstTime()的实现如下所示:
。。。
mHeadUpDisplay = new CameraHeadUpDisplay(this);
mHeadUpDisplay.setListener(new MyHeadUpDisplayListener());
initializeHeadUpDisplay();
。。。
initializeHeadUpDisplay
CameraSettings settings = new CameraSettings(this, mInitialParams,CameraHolder.instance().getCameraInfo());
mHeadUpDisplay.initialize(this,settings.getPreferenceGroup(R.xml.camera_preferences),getZoomRatios(), mOrientationCompensation);
语句mHeadUpDisplay.initialize(this,settings.getPreferenceGroup(R.xml.camera_preferences),getZoomRatios(), mOrientationCompensation);中的R.xml.camera_preferences指的就是camera_preference.xml资源文件。
接下来我们将对settings.getPreferenceGroup(),mHeadUpDisplay.initialize()和MyHeadUpDisplayListener做以介绍。
1.settings.getPreferenceGroup()
函数settings.getPreferenceGroup()定义在文件Packages/apps/camera/src/com/android/camera/CameraSetting.java中。其具体定义为:
public PreferenceGroup getPreferenceGroup(int preferenceRes) {
PreferenceInflater inflater = new PreferenceInflater(mContext);
PreferenceGroup group =
(PreferenceGroup) inflater.inflate(preferenceRes);
if (mParameters != null) initPreference(group);
return group;
}
函数inflater.inflate(preferenceRes)将camera_preference.xml文件中的菜单信息存储到PreferenceGroup中。接着函数initPreference()对这些信息做了处理,其定义为:
private void initPreference(PreferenceGroup group) {
。。。
ListPreference sceneMode = group.findPreference(KEY_SCENE_MODE);
。。。
if (sceneMode != null) {
filterUnsupportedOptions(group,
sceneMode, mParameters.getSupportedSceneModes());
}
。。。
}
其中,宏定义 KEY_SCENE_MODE定义在文件Packages/apps/camera/src/com/android/camera/CameraSetting.java中,具体定义为:
public static final String KEY_SCENE_MODE = "pref_camera_scenemode_key";
该值与camera_preference.xml中的KEY值匹配。
ListPreference sceneMode = group.findPreference(KEY_SCENE_MODE);将Scence mode信息存储在了结构体ListPreference sceneMode中。
语句
if (sceneMode != null) {
filterUnsupportedOptions(group,
sceneMode, mParameters.getSupportedSceneModes());
}
mParameters.getSupportedSceneModes()获取了硬件Camera所支持的场景模式信息。
该信息在我目前所拿的机器在Camera HAL层中实现。在函数initDefaultParameters()中设置了硬件所支持的picture大小。路径:broadcom\rhea_hawaii\v4l2_camerahal\brcm\CameraHAL.cpp
CODE如下:
status_t CameraHAL::initDefaultParameters()
{
...//这个需要进一步分析源码。下次分析。
str8 = pCamDev->querySceneModes(defScene);
if (str8.length() > 0) {
ALOGI("initDefaultParameters(): camera device supports %s scene modes", str8.string());
mParameters.set(CameraParameters::KEY_SUPPORTED_SCENE_MODES, str8.string());
mParameters.set(CameraParameters::KEY_SCENE_MODE, defScene.string());
}
else
ALOGI("initDefaultParameters(): camera deevice does not support any scene mode");
...
}
函数 filterUnsupportedOptions() 会将Camera应用层和硬件支持的Scence mode中两者匹配的大小存储在group中。
这样settings.getPreferenceGroup()获取的group就是最终显示到Scence mode选项上的模式。
2.mHeadUpDisplay.initialize()
函数mHeadUpDisplay.initialize()定义在文件
broadcom_test_apps\brcmcamera\src\com\broadcom\camera\ui\CameraHeadUpDisplay.java 中,其具体定义为:
public void initialize(Context context, PreferenceGroup group,
float[] initialZoomRatios, int initialOrientation) {
mInitialZoomRatios = initialZoomRatios;
mInitialOrientation = initialOrientation;
super.initialize(context, group);//调用父类的initialize
}
public void initialize(Context context, PreferenceGroup preferenceGroup) {
mPreferenceGroup = preferenceGroup;
mSharedPrefs = ComboPreferences.get(context);
mPopupWindow = null;
clearComponents();
initializeIndicatorBar(context, preferenceGroup);//将group添加到菜单中。实现如下
requestLayout();
}
protected void initializeIndicatorBar(
Context context, PreferenceGroup group) {
super.initializeIndicatorBar(context, group);
ListPreference[] prefs = getListPreferences(group,
CameraSettings.KEY_FOCUS_MODE,
CameraSettings.KEY_EXPOSURE,
CameraSettings.KEY_SCENE_MODE,
CameraSettings.KEY_PICTURE_SIZE,
CameraSettings.KEY_JPEG_QUALITY,
CameraSettings.KEY_COLOR_EFFECT);
mOtherSettings = new OtherSettingsIndicator(context, prefs);
mOtherSettings.setOnRestorePreferencesClickedRunner(new Runnable() {
public void run() {
if (mListener != null) {
mListener.onRestorePreferencesClicked();
}
}
});
mIndicatorBar.addComponent(mOtherSettings);
addIndicator(context, group, CameraSettings.KEY_WHITE_BALANCE);
addIndicator(context, group, CameraSettings.KEY_VIDEOCAMERA_FLASH_MODE);
addIndicator(context, group, CameraSettings.KEY_VIDEO_QUALITY);
addIndicator(context, group, CameraSettings.KEY_CAMERA_ID);
mIndicatorBar.setOrientation(mInitialOrientation);
}
}
函数addComponent()将group中的Scence mode添加到了菜单中。至此,菜单的创建就告一段落。下面我们介绍一下菜单的事件。
3、菜单的监听事件MyHeadUpDisplayListener
当菜单被点击时,菜单的监听事件就会监听到该事件,并作出相应的处理。监听事件定义在文件broadcom_test_apps\brcmcamera\src\com\broadcom\camera\Camera.java
private class MyHeadUpDisplayListener implements HeadUpDisplay.Listener {
public void onSharedPreferencesChanged() {
Camera.this.onSharedPreferenceChanged();
}
public void onRestorePreferencesClicked() {
Camera.this.onRestorePreferencesClicked();
}
public void onPopupWindowVisibilityChanged(int visibility) {
}
}
函数onSharedPreferenceChanged()定义为:
onSharedPreferenceChanged
setCameraParametersWhenIdle(UPDATE_PARAM_PREFERENCE);
setCameraParameters
updateCameraParametersPreference
private void updateCameraParametersPreference() {
....
// Since change scene mode may change supported values,
// Set scene mode first,
mSceneMode = mPreferences.getString(
CameraSettings.KEY_SCENE_MODE,
getString(R.string.pref_camera_scenemode_default));
if (isSupported(mSceneMode, mParameters.getSupportedSceneModes())) {
if (!mParameters.getSceneMode().equals(mSceneMode)) {
mParameters.setSceneMode(mSceneMode);
mCameraDevice.setParameters(mParameters);
// Setting scene mode will change the settings of flash mode,
// white balance, and focus mode. Here we read back the
// parameters, so we can know those settings.
mParameters = mCameraDevice.getParameters();
}
} else {
mSceneMode = mParameters.getSceneMode();
if (mSceneMode == null) {
mSceneMode = Parameters.SCENE_MODE_AUTO;
}
}
...
}