MTK Camera学习第三篇(设置相关)

关于设置这块儿大概分为两部分来说,一是视图UI,二是数据处理。第一篇所说的UI只是整体的一个概述,而setting的UI部分相对复杂,因此这里单独来说一下。首先看一下总的布局文件setting_container.xml:

<?xml version="1.0" encoding="utf-8"?>
<com.android.camera.ui.RotateLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:camera="http://schemas.android.com/apk/res/com.mediatek.camera"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center"
    android:splitMotionEvents="false" >
    <com.android.camera.ui.MaxLinearLayout
        android:id="@+id/container"
        style="@style/SettingContainer"
        camera:maxHeight="@dimen/setting_container_height_max"
        android:splitMotionEvents="false">
        <TabHost android:id="@+id/tab_title"
            style="@style/SettingTabTitle">
            <TabWidget 
                android:id="@android:id/tabs"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:background="@drawable/bg_pressed" 
                android:splitMotionEvents="false">
            </TabWidget>
            <FrameLayout
                android:id="@android:id/tabcontent"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:visibility="gone"
                android:splitMotionEvents="false">
            </FrameLayout>
        </TabHost>
        <android.support.v4.view.ViewPager
            android:id="@+id/pager"
            android:layout_width="match_parent"
            android:layout_height="match_parent"/>
    </com.android.camera.ui.MaxLinearLayout>
</com.android.camera.ui.RotateLayout>

MaxLinearLayout重写了LinearLayout,主要是对传统的LinearLayout进行一个最大宽高的限制。内层是我们常见的TabHost+ViewPager的一个页面切换效果。UI显示如下:


TabHost_ViewPager.png

代码怎样处理呢?回到我们说过的SettingManager:

    private void initializeSettings() {
        if (mSettingLayout == null && mSettingController.getPreferenceGroup() != null) {
            mSettingLayout = (ViewGroup) getContext().inflate(R.layout.setting_container,
                    SETTING_PAGE_LAYER);
            mTabHost = (TabHost) mSettingLayout.findViewById(R.id.tab_title);
            mTabHost.setup();

            // For tablet
            int settingKeys[] = SettingConstants.SETTING_GROUP_COMMON_FOR_TAB;
            if (FeatureSwitcher.isSubSettingEnabled()) {
                settingKeys = SettingConstants.SETTING_GROUP_MAIN_COMMON_FOR_TAB;
            } else if (FeatureSwitcher.isLomoEffectEnabled() && getContext().isNonePickIntent()) {
                settingKeys = SettingConstants.SETTING_GROUP_COMMON_FOR_LOMOEFFECT;
            }
            List<Holder> list = new ArrayList<Holder>();
            if (getContext().isNonePickIntent() || getContext().isStereoMode()) {
                ...
                    list.add(new Holder(TAB_INDICATOR_KEY_COMMON, R.drawable.ic_tab_common_setting,
                            settingKeys));
                    list.add(new Holder(TAB_INDICATOR_KEY_CAMERA, R.drawable.ic_tab_camera_setting,
                            SettingConstants.SETTING_GROUP_CAMERA_FOR_TAB));
                    list.add(new Holder(TAB_INDICATOR_KEY_VIDEO, R.drawable.ic_tab_video_setting,
                            SettingConstants.SETTING_GROUP_VIDEO_FOR_TAB));
                ...
            }
            ...

            int size = list.size();
            List<SettingListLayout> pageViews = new ArrayList<SettingListLayout>();
            for (int i = 0; i < size; i++) {
                Holder holder = list.get(i);
                // new page view
                SettingListLayout pageView = (SettingListLayout) getContext().inflate(
                        R.layout.setting_list_layout, SETTING_PAGE_LAYER);
                ArrayList<ListPreference> listItems = new ArrayList<ListPreference>();
                pageView.initialize(getListPreferences(holder.mSettingKeys, i == 0));
                //这里将ArrayList<ListPreference>对象传递到了每一个Item,即SettingListLayout,而这个holder.mSettingKeys是一个int数组,每一个值表示一种状态
                pageViews.add(pageView);
                // new indicator view
                ImageView indicatorView = new ImageView(getContext());
                if (indicatorView != null) {
                    indicatorView.setBackgroundResource(R.drawable.bg_tab_title);
                    indicatorView.setImageResource(holder.mIndicatorIconRes);
                    indicatorView.setScaleType(ScaleType.CENTER);
                }
                mTabHost.addTab(mTabHost.newTabSpec(holder.mIndicatorKey)
                        .setIndicator(indicatorView).setContent(android.R.id.tabcontent));
            }

            mAdapter = new MyPagerAdapter(pageViews);
            mPager = (ViewPager) mSettingLayout.findViewById(R.id.pager);
            mPager.setAdapter(mAdapter);
            mPager.setOnPageChangeListener(mAdapter);
            mTabHost.setOnTabChangedListener(this);
        }
        Util.setOrientation(mSettingLayout, getOrientation(), false);
    }

在这个UI初始化的过程中有两个需要关注的对象:一是SettingListLayout(即每一页PageView,共三页),二是ListPreference(用于保存每一个item条目上的可选内容)。
我们看到每一页PageView中的ListView都有多种item的布局,而这也是我们在做开发中常常会遇到的问题,来看一下Camera是怎样处理的:

      private int getSettingLayoutId(ListPreference pref) {
            // If the preference is null, it will be the only item , i.e.
            // 'Restore setting' in the popup window.
            //将所有item分成以下五种布局类型即可载入五种不同的item layout,注意这里有五个return语句
            if (pref == null) {
                return R.layout.in_line_setting_restore;
            }

            // Currently, the RecordLocationPreference is the only setting
            // which applies the on/off switch.
            if (isSwitchSettingItem(pref)) {
                return R.layout.in_line_setting_switch;
            } else if (isVirtualSettingItem(pref)) {
                return R.layout.in_line_setting_virtual;
            } else if (isSwitchVirtualItem(pref)) {
                return R.layout.in_line_setting_switch_virtual;
            }
            return R.layout.in_line_setting_sublist;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            ListPreference pref = mListItem.get(position);
            //五个Layout布局文件对应了五种自定义viewgroup
            if (convertView != null) {
                if (pref == null) {
                    if (!(convertView instanceof InLineSettingRestore)) {
                        convertView = null;
                    }
                } else if (isSwitchSettingItem(pref)) {
                    if (!(convertView instanceof InLineSettingSwitch)) {
                        convertView = null;
                    }
                } else if (isVirtualSettingItem(pref)) {
                    if (!(convertView instanceof InLineSettingVirtual)) {
                        convertView = null;
                    }
                } else if (isSwitchVirtualItem(pref)) {
                    if (!(convertView instanceof InLineSettingSwitchVirtual)) {
                        convertView = null;
                    }
                } else {
                    if (!(convertView instanceof InLineSettingSublist)) {
                        convertView = null;
                    }
                }
                if (convertView != null) {
                    ((InLineSettingItem) convertView).initialize(pref);
                    SettingUtils.setEnabledState(convertView,
                            (pref == null ? true : pref.isEnabled()));
                    return convertView;
                }
            }
            //在这里并没有直接进行数据填充,而是交给下一层的自定义viewgroup
            int viewLayoutId = getSettingLayoutId(pref);
            InLineSettingItem view = (InLineSettingItem) mInflater.inflate(viewLayoutId, parent,
                    false);
            if (viewLayoutId == R.layout.in_line_setting_restore) {
                view.setId(R.id.restore_default);
            }

            view.initialize(pref); // no init for restore one
            view.setSettingChangedListener(SettingListLayout.this);
            SettingUtils.setEnabledState(convertView, (pref == null ? true : pref.isEnabled()));
            return view;
        }
      //可以使用下面的方法判断是否是一个带有switch开关的item,其它同理
      private boolean isSwitchSettingItem(ListPreference pref) {
        return SettingConstants.KEY_RECORD_LOCATION.equals(pref.getKey())
                || SettingConstants.KEY_VIDEO_RECORD_AUDIO.equals(pref.getKey())
                || SettingConstants.KEY_VIDEO_EIS.equals(pref.getKey())
                || SettingConstants.KEY_VIDEO_3DNR.equals(pref.getKey())
                || SettingConstants.KEY_CAMERA_ZSD.equals(pref.getKey())
                || SettingConstants.KEY_VOICE.equals(pref.getKey())
                || SettingConstants.KEY_CAMERA_FACE_DETECT.equals(pref.getKey())
                || SettingConstants.KEY_HDR.equals(pref.getKey())
                || SettingConstants.KEY_GESTURE_SHOT.equals(pref.getKey())
                || SettingConstants.KEY_SMILE_SHOT.equals(pref.getKey())
                || SettingConstants.KEY_SLOW_MOTION.equals(pref.getKey())
                || SettingConstants.KEY_CAMERA_AIS.equals(pref.getKey())
                || SettingConstants.KEY_ASD.equals(pref.getKey())
                || SettingConstants.KEY_DNG.equals(pref.getKey());
    }

根据ListPreference获取对应的Key,然后将固定的key值对应到固定的layout id上,我们以上图中的人脸美化这个条目为例进一步深入,所以接下来进入的是InLineSettingSublist:

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        mEntry = (TextView) findViewById(R.id.current_setting);
        mImage = (ImageView) findViewById(R.id.image);
        //PageView内部的ListView显示的当前item layout
        setOnClickListener(mOnClickListener);
    }
    protected OnClickListener mOnClickListener = new OnClickListener() {

        @Override
        public void onClick(View view) {
            Log.d(TAG, "onClick() mShowingChildList=" + mShowingChildList + ", mPreference="
                    + mPreference);
            if (!mShowingChildList && mPreference != null && mPreference.isClickable()) {
                expendChild();
            } else {
                collapseChild();
            }
        }
    };
    public boolean expendChild() {
        boolean expend = false;
        if (!mShowingChildList) {
            mShowingChildList = true;
            if (mListener != null) {
                mListener.onShow(this);
            }
            //载入下一个子视图(ListView)
            mSettingLayout = (SettingSublistLayout) mContext.inflate(
                    R.layout.setting_sublist_layout, ViewManager.VIEW_LAYER_SETTING);
            mSettingContainer = mSettingLayout.findViewById(R.id.container);
            //还是那个ListPreference
            mSettingLayout.initialize(mPreference);
            mContext.addView(mSettingLayout, ViewManager.VIEW_LAYER_SETTING);
            mContext.addOnOrientationListener(this);
            mSettingLayout.setSettingChangedListener(this);
            setOrientation(mContext.getOrientationCompensation(), false);
            fadeIn(mSettingLayout);
            highlight();
            expend = true;
        }
        Log.d(TAG, "expendChild() return " + expend);
        return expend;
    }

来看这个子ListView的布局处理:

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            ViewHolder holder = null;
            if (convertView == null) {
                //该子listview的item上有一个ImageView、一个TextView和一个RadioButton
                convertView = mInflater.inflate(R.layout.setting_sublist_item, null);
                holder = new ViewHolder();
                holder.mImageView = (ImageView) convertView.findViewById(R.id.image);
                holder.mTextView = (TextView) convertView.findViewById(R.id.title);
                holder.mRadioButton = (RadioButton) convertView.findViewById(R.id.radio);
                convertView.setTag(holder);
            } else {
                holder = (ViewHolder) convertView.getTag();
            }
            int iconId = mPreference.getIconId(position);
            //像我们当前的人脸美化所用的listview是没有icon,所以这里设为GONE即可
            if (mPreference.getIconId(position) == ListPreference.UNKNOWN) {
                holder.mImageView.setVisibility(View.GONE);
            } else {
                holder.mImageView.setVisibility(View.VISIBLE);
                holder.mImageView.setImageResource(iconId);
            }
            holder.mTextView.setText(mPreference.getEntries()[position]);
            holder.mRadioButton.setChecked(position == mSelectedIndex);
            // SettingUtils.setEnabledState(convertView,
            // mPreference.isEnabled(position));
            return convertView;
        }

那么事件如何层层传递到Activity呢?看下各层视图类的定义:

public class SettingSublistLayout extends RotateLayout implements AdapterView.OnItemClickListener {...}

public class InLineSettingSublist extends InLineSettingItem implements
        SettingSublistLayout.Listener, CameraActivity.OnOrientationListener {...}

public abstract class InLineSettingItem extends RelativeLayout {...}

public class SettingListLayout extends FrameLayout implements InLineSettingItem.Listener,
        AdapterView.OnItemClickListener, OnScrollListener {...}

public class SettingManager extends ViewManager implements View.OnClickListener,
        SettingListLayout.Listener, CameraActivity.OnPreferenceReadyListener, OnTabChangeListener {...}

基本上是一个通过接口层层向外传递的过程,其中InLineSettingItem作为一个抽象类,其定义的接口由其子类实现(子类同时将继承其接口对象),然后就进入了SettingListLayout,进而转发到SettingManager,最终到达CameraActivity。
我们来看一下这个抽象类的子类都有哪些:


settingitem.png

这五个子类就是前面说的五个布局文件中的自定义viewgroup。
视图关系与事件逻辑就讲到这里,接下来看一下上面提到的ListPreference,回到SettingManager的以下代码:

 int settingKeys[] = SettingConstants.SETTING_GROUP_COMMON_FOR_TAB;
 ...
 pageView.initialize(getListPreferences(holder.mSettingKeys, i == 0));

这个mSettingKeys整形数组初始化为如下,每一个数都表示一条item(即一个Camera属性)

    public static final int[] SETTING_GROUP_COMMON_FOR_TAB = new int[]{
        ROW_SETTING_DUAL_CAMERA_MODE,
        ROW_SETTING_RECORD_LOCATION,//common
        ROW_SETTING_MULTI_FACE_MODE,
        ROW_SETTING_EXPOSURE,//common
        ROW_SETTING_COLOR_EFFECT,//common
        ROW_SETTING_SCENCE_MODE,//common
        ROW_SETTING_WHITE_BALANCE,//common
        ROW_SETTING_IMAGE_PROPERTIES,
        ROW_SETTING_ANTI_FLICKER,//common
    };

除了上面的通用设置属性,还有针对camera和video的针对属性,同理定义的数组如下:

    public static final int[] SETTING_GROUP_CAMERA_FOR_TAB = new int[]{
        ROW_SETTING_ZSD,//camera
        ROW_SETTING_AIS,//camera
        ROW_SETTING_VOICE,//camera
        ROW_SETTING_CAMERA_FACE_DETECT,//camera
        ROW_SETTING_GESTURE_SHOT,
        ROW_SETTING_SMILE_SHOT,//camera
        ROW_SETTING_ASD,//camera
        ROW_SETTING_DNG,
        ROW_SETTING_SELF_TIMER,//camera
        ROW_SETTING_CONTINUOUS_NUM,//camera
        ROW_SETTING_PICTURE_SIZE,//camera
        ROW_SETTING_PICTURE_RATIO,//camera
        ROW_SETTING_ISO,//camera
        ROW_SETTING_FACEBEAUTY_PROPERTIES,//camera:Cfb
    };
    public static final int[] SETTING_GROUP_VIDEO_FOR_TAB = new int[]{
        ROW_SETTING_3DNR,
        ROW_SETTING_VIDEO_STABLE,//video
        ROW_SETTING_MICROPHONE,//video
        ROW_SETTING_AUDIO_MODE,//video
        ROW_SETTING_TIME_LAPSE,//video
        ROW_SETTING_VIDEO_QUALITY,//video
        ROW_SETTING_SLOW_MOTION_VIDEO_QUALITY,
    };

只要Hal层做了相应的支持,上述数组中表示的所有属性调节都将出现在Camera的Setting中
然后使用该数组封装得到ListPreference对象:

    private ArrayList<ListPreference> getListPreferences(int[] keys, boolean addrestore) {
        ArrayList<ListPreference> listItems = new ArrayList<ListPreference>();
        for (int i = 0; i < keys.length; i++) {
            //keys数组的值对应的是KEYS_FOR_SETTING这个数组的index下标,这里将得到类似pref_camera_id_key这样的值
            String key = SettingConstants.getSettingKey(keys[i]);
            /*
            getListPreference(key)这个方法的实现在SettingCtrl中,然后将调用SettingGenerator的getListPreference(int row)方法,
            传到SettingCtrl的是数组值,然后又转换为了数组下标
            */
            ListPreference pref = mSettingController.getListPreference(key);
            if (pref != null && pref.isShowInSetting()) {
                if (SettingConstants.KEY_VIDEO_QUALITY.equals(key)) {
                    if (!("on".equals(mSettingController
                            .getSettingValue(SettingConstants.KEY_SLOW_MOTION)))) {
                        listItems.add(pref);
                    }
                } else {
                    listItems.add(pref);
                }

            }
        }

        if (addrestore) {
            listItems.add(null);
        }
        return listItems;
    }

看一下SettingGenerator的getListPreference方法:

    public ListPreference getListPreference(int row) {
        int currentCameraId = mICameraDeviceManager.getCurrentCameraId();
        //根据CameraId得到当前使用的ListPreference
        ArrayList<ListPreference> preferences = mPreferencesMap.get(currentCameraId);
        if (preferences == null) {
            Log.e(TAG, "Call setting before setting updated, return null");
            return null;
        }
        return preferences.get(row);
    }

mPreferencesMap定义如下:

private HashMap<Integer, ArrayList<ListPreference>> mPreferencesMap;
//一般来说我们都有两个camera,所以我们需要定义两个ListPreference集合分别来表示前置后置两个摄像头的参数配置信息
int cameraCounts = mICameraDeviceManager.getNumberOfCameras();
mPreferencesMap = new HashMap<Integer, ArrayList<ListPreference>>(cameraCounts);

到这里我们得到了当前摄像头所使用的ListPreference,那它与UI视图怎样建立联系呢?上面说过每一个item都对应了一个int值,而这个int值就分别对应了不同的ListPreference对象。
接下来我们看下ListPreference的创建过程,回到第二篇中讲的CameraDeviceCtrl,在Camera成功open之后,将执行如下方法:

    private void initializeSettingController() {
        Log.d(TAG, "[initializeSettingController]");
        if (!mISettingCtrl.isSettingsInitialized()) {
            //该xml文件定义了setting的所有条目  
            mISettingCtrl.initializeSettings(R.xml.camera_preferences, mPreferences.getGlobal(),
                    mPreferences.getLocal());
        }
        //mPreferences最初是在CameraActivity中new出来的ComboPreferences对象
        mISettingCtrl.updateSetting(mPreferences.getLocal());
        mMainHandler.sendEmptyMessage(MSG_CAMERA_PREFERENCE_READY);
    }

而后进入SettingCtrl:

    @Override
    public void initializeSettings(int preferenceRes, SharedPreferences globalPref,
            SharedPreferences localPref) {
        Log.i(TAG, "[initializeSettings]...");
        mLocalPrefs.put(mICameraDeviceManager.getCurrentCameraId(), localPref);
        mPrefTransfer = new SharedPreferencesTransfer(globalPref, localPref);
        mSettingGenerator = new SettingGenerator(mICameraContext, mPrefTransfer);
        mSettingGenerator.createSettings(preferenceRes);
        createRules();
        mIsInitializedSettings = true;
    }

最终进入SettingGenerator:

    public void createSettings(int preferenceRes) {
        mPreferenceRes = preferenceRes;
        mInflater = new PreferenceInflater(mContext, mPrefTransfer);
        int currentCameraId = mICameraDeviceManager.getCurrentCameraId();
        //自定义了一个Inflater用来解析上面提到的xml文件(并且在解析过程中封装得到多个ListPreference对象),
        //一个摄像头所对应的所有可用setting属性构成了一个PreferenceGroup  
        PreferenceGroup group = (PreferenceGroup) mInflater.inflate(preferenceRes);
        mPreferencesGroupMap.put(currentCameraId, group);
        createSettingItems();
        createPreferences(group, currentCameraId);
    }
    /*
    一个SettingItem记录了当前item条目的id信息、当前的value值、上次选择的value值以及
    默认的value值,另外SettingItem内部持有当前item的ListPreference对象
    */
    private void createSettingItems() {
        int cameraCounts = mICameraDeviceManager.getNumberOfCameras();
        for (int i = 0; i < cameraCounts; i++) {
            ArrayList<SettingItem> settingItems = new ArrayList<SettingItem>();
            for (int settingId = 0; settingId < SettingConstants.SETTING_COUNT; settingId++) {
                SettingItem settingItem = new SettingItem(settingId);
                String key = SettingConstants.getSettingKey(settingId);
                int type = SettingConstants.getSettingType(settingId);
                settingItem.setKey(key);
                settingItem.setType(type);
                settingItems.add(settingItem);
            }
            mSettingItemsMap.put(i, settingItems);
        }

    }
    /*
    根据CameraId和上面得到的group将ListPreference保存到HashMap
    */
    private void createPreferences(PreferenceGroup group, int cameraId) {
        Log.i(TAG, "[createPreferences], cameraId:" + cameraId + ", group:" + group);
        ArrayList<ListPreference> preferences = mPreferencesMap.get(cameraId);
        mSupportedImageProperties = new ArrayList<String>();
        mSupportedFaceBeautyProperties = new ArrayList<String>();
        if (preferences == null) {
            preferences = new ArrayList<ListPreference>();
            ArrayList<SettingItem> settingItems = mSettingItemsMap.get(cameraId);
            for (int settingId = 0; settingId < SettingConstants.SETTING_COUNT; settingId++) {
                String key = SettingConstants.getSettingKey(settingId);
                ListPreference preference = group.findPreference(key);

                preferences.add(preference);

                SettingItem settingItem = settingItems.get(settingId);
                //上面说过SettingItem持有一个ListPreference的对象
                settingItem.setListPreference(preference);
            }
            mPreferencesMap.put(cameraId, preferences);
        }
        // every camera maintain one setting item list.
        //从ICameraDevice得到Parameters过滤掉当前Camera不支持的一些属性
        filterPreferences(preferences, cameraId);
    }

综上得到如下类图关系:


camerasetting.png

接着前面说过的,当事件通过接口层层传递到CameraActivity时,将通过ISettingCtrl的对象调整item条目的value值,再通过CameraDeviceCtrl将最新的Camera属性适用到设备。如下所求:

    private SettingManager.SettingListener mSettingListener = new SettingManager.SettingListener() {
        @Override
        public void onSharedPreferenceChanged(ListPreference preference) {
            Log.d(TAG, "[onSharedPreferenceChanged]");
            if (!isCameraOpened()) {
                return;
            }
            if (preference != null) {
                String settingKey = preference.getKey();
                String value = preference.getValue();
                mISettingCtrl.onSettingChanged(settingKey, value);
            }
            mCameraDeviceCtrl.applyParameters(false);
        }
        ...
    }

而SettingCtrl将对item进行重新赋值(不操作UI),如下:

    private void onSettingChanged(Parameters parameters, int currentCameraId, String key,
            String value) {
        int settingId = SettingConstants.getSettingId(key);
        SettingItem setting = getSetting(settingId);
        String lastValue = setting.getLastValue();
        Log.i(TAG, "[onSettingChanged], key: " + key + ", value:" + value + ", lastValue:"
                + lastValue);
        if (value == null || value.equals(lastValue) && ! SettingConstants.KEY_PICTURE_SIZE.equals(key)){
            Log.w(TAG, "[onSettingChanged], do not need to change, return.");
            return;
        }
        setting.setValue(value);
        setting.setLastValue(value);
        ...
     }

流程相对复杂但也相对清楚,但我们发现一个问题,说了这么多,item上的value值的变化即UI操作到底在哪里进行的?总得有一个setText()或者setImage()之类的方法才对吧,我们找到SettingSublistLayout的onItemClick事件:

    @Override
    public void onItemClick(AdapterView<?> parent, View view, int index, long id) {
        Log.d(TAG,
                "onItemClick(" + index + ", " + id + ") and oldIndex = "
                        + mAdapter.getSelectedIndex());
        boolean realChanged = index != mAdapter.getSelectedIndex();
        if (realChanged) {
            //这里最后将数据保存在SharedPreferences中
            mPreference.setValueIndex(index);
        }
        if (mListener != null) {
            //这里最终调用到SettingManager的onSettingChanged()方法
            mListener.onSettingChanged(realChanged);
        }
    }

SettingManager.java

    @Override
    public void onSettingChanged(SettingListLayout settingList, ListPreference preference) {
        Log.i(TAG, "onSettingChanged(" + settingList + ")");
        if (mListener != null) {
            mListener.onSharedPreferenceChanged(preference);
            mPreference = preference;
        }
        refresh();
    }

这个refresh()方法应该就是我们想要找的,但它本身没有,我们查找它的父类ViewManager:

    public final void refresh() {
        if (mShowing) {
            onRefresh();
        }
    }

onRefresh()方法在该父类中没有实现,我们回到它的子类SettingManager:

    @Override
    public void onRefresh() {
        Log.i(TAG, "onRefresh() isShowing()=" + isShowing() + ", mShowingContainer="
                + mShowingContainer);
        if (mShowingContainer && mAdapter != null) { // just apply checker when
                                                     // showing settings
            //通知UI进行刷新,这就是我们想要找的
            mAdapter.notifyDataSetChanged();
        }
    }

进入内部类MyPagerAdapter:

        @Override
        public void notifyDataSetChanged() {
            super.notifyDataSetChanged();
            for (SettingListLayout page : mPageViews) {
                if (page != null) {
                    page.setSettingChangedListener(SettingManager.this);
                    page.reloadPreference();
                }
            }
        }

然后是SettingListLayout:

    public void reloadPreference() {
        int count = mSettingList.getChildCount();
        for (int i = 0; i < count; i++) {
            ListPreference pref = mListItem.get(i);
            if (pref != null) {
                InLineSettingItem settingItem = (InLineSettingItem) mSettingList.getChildAt(i);
                settingItem.reloadPreference();
            }
        }
    }

接着是InLineSettingItem,由于它是一个抽象类,而其子类InLineSettingVirtual(也可能是其它子类)同时重写了reloadPreference()方法,我们直接看它的子类方法:

    @Override
    public void reloadPreference() {
        int len = mPreference.getEntries().length;
        mChildPrefs = new ListPreference[len];
        for (int i = 0; i < len; i++) {
            String key = String.valueOf(mPreference.getEntries()[i]);
            mChildPrefs[i] = mContext.getListPreference(key);
            Log.d(TAG, "reloadPreference() mChildPrefs[" + i + "|" + key + "]=" + mChildPrefs[i]);
        }
        updateView();
    }
    @Override
    protected void updateView() {
        if (mPreference == null || mChildPrefs == null) {
            return;
        }
        setOnClickListener(null);
        int len = mChildPrefs.length;
        boolean allDefault = true;
        int enableCount = 0;
        for (int i = 0; i < len; i++) {
            ListPreference pref = mChildPrefs[i];
            if (pref == null) {
                continue;
            }
            String defaultValue = String.valueOf(mPreference.getEntryValues()[i]);
            String value = pref.getOverrideValue();
            if (value == null) {
                value = pref.getValue();
            }
            if (pref.isEnabled()) {
                enableCount++;
            }
            // we assume pref and default value is not null.
            if (allDefault && !defaultValue.equals(value)) {
                allDefault = false;
            }
        }
        if (allDefault) {
            mEntry.setText(mPreference.getDefaultValue());
        } else {
            mEntry.setText("");
        }
        boolean enabled = (enableCount == len);
        mPreference.setEnabled(enabled);
        setEnabled(mPreference.isEnabled());
        setOnClickListener(mOnClickListener);
        Log.d(TAG, "updateView() enableCount=" + enableCount + ", len=" + len);
    }

至此,一个Camera Setting完整的UI操作过程就结束了。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容

  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 11,613评论 4 59
  • 啦啦啦
    康卡阅读 137评论 0 0
  • R:失败的时候,请原谅自己。《自控力》P159 遇到挫折时,自我批评不会让我们离目标更近,降低积极性和自控力。每个...
    豆妈成长阅读 192评论 0 0