现在的位置: 首页 > 综合 > 正文

Camera系统中设置picture大小菜单的流程分析

2013年06月23日 ⁄ 综合 ⁄ 共 17150字 ⁄ 字号 评论关闭

转自:http://blog.csdn.net/wxzking/archive/2011/03/05/6225702.aspx

 

Android平台上,设置菜单有两种方式:通过XML布局来实现和通过Menu.add方法实现。Camera系统中的菜单可以说是通过XML布局来实现的,但它的实现过程并非像一般文章里介绍的那样简单。所以,在此我就以设置capturepicture大小为例,将Camera系统中菜单的设置流程作以简单介绍。

1、资源文件分析

packages/apps/Camera/res/xml/有两个xml文件,分别为Camera_preferences.xmlVideo_preferences.xml。这两个文件中列举了CameraVideo Camera两种模式下的不同菜单信息。以Camera_preferences.xml为例,其中关于picture大小的菜单项信息为:

<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" />

 

pref_camera_picturesize_title的定义在packages/apps/Camera/res/values/strings.xml中,其中有如下的定义:

<!--

 Settings screen, Picture size title

  -->

  <item>@string/pref_camera_picturesize_entry_2592x1936</item>

  <item>@string/pref_camera_picturesize_entry_2592x1936</item>

  <item>@string/pref_camera_picturesize_entry_2048x1536</item>

  <item>@string/pref_camera_picturesize_entry_1600x1200</item>

  <item>@string/pref_camera_picturesize_entry_1024x768</item>

  <item>@string/pref_camera_picturesize_entry_320x240</item>

  </string-array>

pref_camera_picturesize_entryvalues的定义也在

packages/apps/Camera/res/values/arrays.xml中,其中有如下的定义:

-<string-array name="pref_camera_picturesize_entryvalues" translatable="false">

  <item>2592x1944</item>

  <item>2592x1936</item>

  <item>2048x1536</item>

  <item>1600x1200</item>

  <item>1024x768</item>

  <item>320x240</item>

  </string-array>

由这些定义,我们可以看出,AndroidCamera应用程序支持五种图片大小。但是硬件并非都支持这些大小。所以最终的菜单中只会显示这五种大小当中底层硬件所支持的,如果硬件支持的大小与其中任何一种都不匹配,则不会显示出“Picture size”菜单。

 

2、菜单的创建

在文件Packages/apps/camera/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模式下mIsImageCaptureIntenttrue,此处将不做任何处理。当mIsImageCaptureIntentfalse时,即Video Camera模式下,将调用函数addBaseMenuItems()来创建Video Camera的菜单。Picture大小的菜单不会出现在Video Camera模式下,所以就不对addBaseMenuItems()函数分析了。

既然这里没有创建Camera模式下的菜单,那Camera模式下的菜单在哪里创建的了?我们接着分析。

Camera.java文件中定义了类MainHandler,它里面只有一个成员函数handleMessage(),它用来处理Camera应用程序中的message

MainHandler的定义如下:

    /**

     * This Handler is used to post message back onto the main thread of the

     * application

     */

    private class MainHandler extends Handler {

        @Override

        public void handleMessage(Message msg) {

            switch (msg.what) {

                case RESTART_PREVIEW: {

                    restartPreview();

                    if (mJpegPictureCallbackTime != 0) {

                        long now = System.currentTimeMillis();

                        mJpegCallbackFinishTime = now - mJpegPictureCallbackTime;

                        Log.v(TAG, "mJpegCallbackFinishTime = "

                                + mJpegCallbackFinishTime + "ms");

                        mJpegPictureCallbackTime = 0;

                    }

                    break;

                }

 

                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;

                }

            }

        }

 

Message FIRST_TIME_INIT应该在类Camera初始化时就会被处理,我是这样猜的,只是还没有找到具体的codeinitializeFirstTime()的实现如下所示:

    // Snapshots can only be taken after this is called. It should be called

    // once only. We could have done these things in onCreate() but we want to

    // make preview screen appear as soon as possible.

    private void initializeFirstTime() {

    ……

        mFirstTimeInitialized = true;

        changeHeadUpDisplayState();

        addIdleHandler();

    }

在函数的结束部位有一个函数changeHeadUpDisplayState(),它的定义如下:

    private void changeHeadUpDisplayState() {

        // If the camera resumes behind the lock screen, the orientation

        // will be portrait. That causes OOM when we try to allocation GPU

        // memory for the GLSurfaceView again when the orientation changes. So,

        // we delayed initialization of HeadUpDisplay until the orientation

        // becomes landscape.

        Configuration config = getResources().getConfiguration();

        if (config.orientation == Configuration.ORIENTATION_LANDSCAPE

                && !mPausing && mFirstTimeInitialized) {

            if (mGLRootView == null) initializeHeadUpDisplay();

        } else if (mGLRootView != null) {

            finalizeHeadUpDisplay();

        }

    }

此时mFirstTimeInitialized已经为true,当其他条件也满足时将进入函数initializeHeadUpDisplay(),它的具体定义为:

 

    private void initializeHeadUpDisplay() {

        FrameLayout frame = (FrameLayout) findViewById(R.id.frame);

        mGLRootView = new GLRootView(this);

        frame.addView(mGLRootView);

 

        mHeadUpDisplay = new CameraHeadUpDisplay(this);

        CameraSettings settings = new CameraSettings(this, mInitialParams);

        mHeadUpDisplay.initialize(this,

                settings.getPreferenceGroup(R.xml.camera_preferences));

        mHeadUpDisplay.setListener(new MyHeadUpDisplayListener());

        mHeadUpDisplay.setOrientation(mLastOrientation);

 

        if (mParameters.isZoomSupported()) {

            ……

        }

 

        updateSceneModeInHud();

 

        mGLRootView.setContentPane(mHeadUpDisplay);

    }

 

语句mHeadUpDisplay.initialize(this,                settings.getPreferenceGroup(R.xml.camera_preferences));中的R.xml.camera_preferences指的就是camera_preference.xml资源文件。

接下来我们将对settings.getPreferenceGroup()mHeadUpDisplay.initialize()MyHeadUpDisplayListener做以介绍。

函数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);

        initPreference(group);

        return group;

    }

函数inflater.inflate(preferenceRes)camera_preference.xml文件中的菜单信息存储到PreferenceGroup中。接着函数initPreference()对这些信息做了处理,其定义为:

    private void initPreference(PreferenceGroup group) {

        ListPreference videoQuality = group.findPreference(KEY_VIDEO_QUALITY);

        ListPreference pictureSize = group.findPreference(KEY_PICTURE_SIZE);

        ListPreference whiteBalance =  group.findPreference(KEY_WHITE_BALANCE);

……

        // Since the screen could be loaded from different resources, we need

        // to check if the preference is available here

        if (videoQuality != null) {

            // Modify video duration settings.

            // The first entry is for MMS video duration, and we need to fill

            // in the device-dependent value (in seconds).

            CharSequence[] entries = videoQuality.getEntries();

            CharSequence[] values = videoQuality.getEntryValues();

            for (int i = 0; i < entries.length; ++i) {

                if (VIDEO_QUALITY_MMS.equals(values[i])) {

                    entries[i] = entries[i].toString().replace(

                            "30", Integer.toString(MMS_VIDEO_DURATION));

                    break;

                }

            }

        }

 

        // Filter out unsupported settings / options

        if (pictureSize != null) {

            filterUnsupportedOptions(group, pictureSize, sizeListToStringList(

                    mParameters.getSupportedPictureSizes()));

        }

        if (whiteBalance != null) {

            filterUnsupportedOptions(group,

                    whiteBalance, mParameters.getSupportedWhiteBalance());

        }

        ……

    }

 

其中,宏定义KEY_PICTURE_SIZE定义在文件

Packages/apps/camera/src/com/android/camera/CameraSetting.java中,具体定义为:

public static final String KEY_PICTURE_SIZE = "pref_camera_picturesize_key";

该值与camera_preference.xml中的KEY值匹配。语句ListPreference pictureSize = group.findPreference(KEY_PICTURE_SIZE);picture的大小信息存储在了结构体ListPreference pictureSize中。

语句

        if (pictureSize != null) {

            filterUnsupportedOptions(group, pictureSize, sizeListToStringList(

                    mParameters.getSupportedPictureSizes()));

        }

mParameters.getSupportedPictureSizes()获取了硬件Camera所支持的picture大小信息,我在Camera HAL层的实现中,在函数initDefaultParameters()中设置了硬件所支持的picture大小。CODE如下:

CameraParameters p;

p.set(picture-size-values1600x1200,1024x768,640x480);

函数filterUnsupportedOptions()会将Camera应用和硬件支持的picture大小中两者匹配的大小存储在group中。

函数mHeadUpDisplay.initialize()定义在文件

Packages/apps/camera/src/com/android/camera/ui/HeadUpDisplay.java中,其具体定义为:

    public void initialize(Context context, PreferenceGroup preferenceGroup) {

        mPreferenceGroup = preferenceGroup;

        mSharedPrefs = PreferenceManager.getDefaultSharedPreferences(context);

        mSharedPrefs.registerOnSharedPreferenceChangeListener(

                mSharedPreferenceChangeListener);

        initializeIndicatorBar(context, preferenceGroup);

    }

其中函数initializeIndicatorBar()定义如下:

    protected void initializeIndicatorBar(

            Context context, PreferenceGroup group) {

        mIndicatorBar = new IndicatorBar();

 

        mIndicatorBar.setBackground(new NinePatchTexture(

                context, R.drawable.ic_viewfinder_iconbar));

        mIndicatorBar.setHighlight(new NinePatchTexture(

                context, R.drawable.ic_viewfinder_iconbar_highlight));

        addComponent(mIndicatorBar);

        mIndicatorBar.setOnItemSelectedListener(new IndicatorBarListener());

    }

函数addComponent()group中的picture大小添加到了菜单中。至此,菜单的创建就告一段落。下面我们介绍一下菜单的事件。

 

3、菜单的监听事件

当菜单被点击时,菜单的监听事件就会监听到该事件,并作出相应的处理。监听事件定义在文件Packages/apps/camera/src/com/android/camera/Camera.java中。其具体定义为:

    private class MyHeadUpDisplayListener implements HeadUpDisplay.Listener {

        // The callback functions here will be called from the GLThread. So,

        // we need to post these runnables to the main thread

        public void onSharedPreferencesChanged() {

            mHandler.post(new Runnable() {

                public void run() {

                    Camera.this.onSharedPreferenceChanged();

                }

            });

        }

 

        public void onRestorePreferencesClicked() {

            mHandler.post(new Runnable() {

                public void run() {

                    Camera.this.onRestorePreferencesClicked();

                }

            });

        }

 

        public void onPopupWindowVisibilityChanged(int visibility) {

        }

    }

函数onSharedPreferenceChanged()定义为:

    private void onSharedPreferenceChanged() {

        // ignore the events after "onPause()"

        if (mPausing) return;

 

        boolean recordLocation;

 

        synchronized (mPreferences) {

            recordLocation = RecordLocationPreference.get(

                    mPreferences, getContentResolver());

            mQuickCapture = getQuickCaptureSettings();

        }

 

        if (mRecordLocation != recordLocation) {

            mRecordLocation = recordLocation;

            if (mRecordLocation) {

                startReceivingLocationUpdates();

            } else {

                stopReceivingLocationUpdates();

            }

        }

 

        setCameraParametersWhenIdle(UPDATE_PARAM_PREFERENCE);

    }

其中函数setCameraParametersWhenIdle()定义为:

    // If the Camera is idle, update the parameters immediately, otherwise

    // accumulate them in mUpdateSet and update later.

    private void setCameraParametersWhenIdle(int additionalUpdateSet) {

        mUpdateSet |= additionalUpdateSet;

        if (mCameraDevice == null) {

            // We will update all the parameters when we open the device, so

            // we don't need to do anything now.

            mUpdateSet = 0;

            return;

        } else if (isCameraIdle()) {

            setCameraParameters(mUpdateSet);

            mUpdateSet = 0;

        } else {

            if (!mHandler.hasMessages(SET_CAMERA_PARAMETERS_WHEN_IDLE)) {

                mHandler.sendEmptyMessageDelayed(

                        SET_CAMERA_PARAMETERS_WHEN_IDLE, 1000);

            }

        }

    }

函数setCameraParameters()定义为:

    // We separate the parameters into several subsets, so we can update only

    // the subsets actually need updating. The PREFERENCE set needs extra

    // locking because the preference can be changed from GLThread as well.

    private void setCameraParameters(int updateSet) {

        mParameters = mCameraDevice.getParameters();

 

        if ((updateSet & UPDATE_PARAM_INITIALIZE) != 0) {

            updateCameraParametersInitialize();

        }

 

        if ((updateSet & UPDATE_PARAM_ZOOM) != 0) {

            updateCameraParametersZoom();

        }

 

        if ((updateSet & UPDATE_PARAM_PREFERENCE) != 0) {

            synchronized (mPreferences) {

                updateCameraParametersPreference();

            }

        }

 

        mCameraDevice.setParameters(mParameters);

    }

Picture大小的设置属于UPDATE_PARAM_PREFERENCE,所以会调到函数updateCameraParametersPreference(),其定义为:

   private void updateCameraParametersPreference() {

        // Set picture size.

        String pictureSize = mPreferences.getString(

                CameraSettings.KEY_PICTURE_SIZE, null);

        if (pictureSize == null) {

            CameraSettings.initialCameraPictureSize(this, mParameters);

        } else {

            List<Size> supported = mParameters.getSupportedPictureSizes();

            CameraSettings.setCameraPictureSize(

                    pictureSize, supported, mParameters);

        }

 

        // Set the preview frame aspect ratio according to the picture size.

        Size size = mParameters.getPictureSize();

        PreviewFrameLayout frameLayout =

                (PreviewFrameLayout) findViewById(R.id.frame_layout);

        frameLayout.setAspectRatio((double) size.width / size.height);

 

        // Set a preview size that is closest to the viewfinder height and has

        // the right aspect ratio.

        List<Size> sizes = mParameters.getSupportedPreviewSizes();

        Size optimalSize = getOptimalPreviewSize(

                sizes, (double) size.width / size.height);

        if (optimalSize != null) {

            mParameters.setPreviewSize(optimalSize.width, optimalSize.height);

        }

……

       

    }

函数CameraSettings.initialCameraPictureSize()的定义如下:

    public static void initialCameraPictureSize(

            Context context, Parameters parameters) {

        // When launching the camera app first time, we will set the picture

        // size to the first one in the list defined in "arrays.xml" and is also

        // supported by the driver.

        List<Size> supported = parameters.getSupportedPictureSizes();

        if (supported == null) return;

        for (String candidate : context.getResources().getStringArray(

                R.array.pref_camera_picturesize_entryvalues)) {

            if (setCameraPictureSize(candidate, supported, parameters)) {

                SharedPreferences.Editor editor = PreferenceManager

                        .getDefaultSharedPreferences(context).edit();

                editor.putString(KEY_PICTURE_SIZE, candidate);

                editor.commit();

                return;

            }

        }

        Log.e(TAG, "No supported picture size found");

    }

函数setCameraPictureSize()定义如下:

    public static boolean setCameraPictureSize(

            String candidate, List<Size> supported, Parameters parameters) {

        int index = candidate.indexOf('x');

        if (index == NOT_FOUND) return false;

        int width = Integer.parseInt(candidate.substring(0, index));

        int height = Integer.parseInt(candidate.substring(index + 1));

        for (Size size: supported) {

            if (size.width == width && size.height == height) {

                parameters.setPictureSize(width, height);

                return true;

            }

        }

        return false;

    }

 

监听事件中的onRestorePreferencesClicked()定义如下:

    protected void onRestorePreferencesClicked() {

        if (mPausing) return;

        Runnable runnable = new Runnable() {

            public void run() {

                mHeadUpDisplay.restorePreferences(mParameters);

            }

        };

        MenuHelper.confirmAction(this,

                getString(R.string.confirm_restore_title),

                getString(R.string.confirm_restore_message),

                runnable);

    }

 

其中restorePreferences()函数定义如下:

    public void restorePreferences(final Parameters param) {

        getGLRootView().runInGLThread(new Runnable() {

            public void run() {

                OnSharedPreferenceChangeListener l =

                        mSharedPreferenceChangeListener;

                // Unregister the listener since "upgrade preference" will

                // change bunch of preferences. We can handle them with one

                // onSharedPreferencesChanged();

                mSharedPrefs.unregisterOnSharedPreferenceChangeListener(l);

                Context context = getGLRootView().getContext();

                synchronized (mSharedPrefs) {

                    Editor editor = mSharedPrefs.edit();

                    editor.clear();

                    editor.commit();

                }

                CameraSettings.upgradePreferences(mSharedPrefs);

                CameraSettings.initialCameraPictureSize(context, param);

                reloadPreferences();

                if (mListener != null) {

                    mListener.onSharedPreferencesChanged();

                }

                mSharedPrefs.registerOnSharedPreferenceChangeListener(l);

            }

        });

    }

函数reloadPreferences()定义如下:

    public void reloadPreferences() {

        mPreferenceGroup.reloadValue();

        mIndicatorBar.reloadPreferences();

    }

}

 

4、小结

由于Camera应用程序比较复杂其菜单的设置流程也是比较难以理解。这里我只是做了一个初步的小结,很多地方还不清楚,以备日后再做细化。

 

 

 

Revision History

修订历史记录

Version

版本

Date

日期

Author

作者

Brief Description

变更简要

0.0.1

2011-03-05

Wangxiaozhe

wxiaozhe@163.com

QQ1226062415

Init draft.

 

 

<

抱歉!评论已关闭.