从XML中扩展对象

时间:2014-01-24 20:34:30

标签: java android xml android-inflate

我正在尝试编写一个自定义相机应用程序,所以为了获得我的相机的一些想法,我下载了谷歌相机应用程序并删除了我不需要的功能。

但是,我遇到了一个问题,即部分代码读入XML文件并将该XML扩展为自定义首选项对象以便在相机屏幕上使用。这是一个很酷的功能,我想使用,但不幸的是它是错误的,我无法弄清楚原因。

让我们首先解释他们在做什么。首先,有一个带有首选项的xml文件。每个@string对应于strings.xml中的一个字段,每个@array对应于arrays.xml中的一个数组。所有这些代码都是我从他们的存储库中删除的Google。

<PreferenceGroup
        xmlns:camera="http://schemas.android.com/apk/lib/com.android.gallery3d"
        camera:title="@string/pref_camera_settings_category">
    <IconListPreference
            camera:key="pref_camera_flashmode_key"
            camera:defaultValue="@string/pref_camera_flashmode_default"
            camera:title="@string/pref_camera_flashmode_title"
            camera:icons="@array/camera_flashmode_icons"
            camera:largeIcons="@array/camera_flashmode_largeicons"
            camera:entries="@array/pref_camera_flashmode_entries"
            camera:entryValues="@array/pref_camera_flashmode_entryvalues" />
    <IconListPreference
            camera:key="pref_camera_exposure_key"
            camera:defaultValue="@string/pref_exposure_default"
            camera:title="@string/pref_exposure_title"
            camera:singleIcon="@drawable/ic_exposure_holo_light" />
    <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" />
    <IconListPreference
            camera:key="pref_camera_whitebalance_key"
            camera:defaultValue="@string/pref_camera_whitebalance_default"
            camera:title="@string/pref_camera_whitebalance_title"
            camera:icons="@array/whitebalance_icons"
            camera:largeIcons="@array/whitebalance_largeicons"
            camera:entries="@array/pref_camera_whitebalance_entries"
            camera:entryValues="@array/pref_camera_whitebalance_entryvalues" />
    <RecordLocationPreference
            camera:key="pref_camera_recordlocation_key"
            camera:defaultValue="@string/pref_camera_recordlocation_default"
            camera:title="@string/pref_camera_recordlocation_title"
            camera:icons="@array/camera_recordlocation_icons"
            camera:largeIcons="@array/camera_recordlocation_largeicons"
            camera:entries="@array/pref_camera_recordlocation_entries"
            camera:entryValues="@array/pref_camera_recordlocation_entryvalues" />
    <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" />
    <ListPreference
            camera:key="pref_camera_focusmode_key"
            camera:defaultValue="@array/pref_camera_focusmode_default_array"
            camera:title="@string/pref_camera_focusmode_title"
            camera:entries="@array/pref_camera_focusmode_entries"
            camera:entryValues="@array/pref_camera_focusmode_entryvalues" />
    <IconListPreference
            camera:key="pref_camera_id_key"
            camera:defaultValue="@string/pref_camera_id_default"
            camera:title="@string/pref_camera_id_title"
            camera:icons="@array/camera_id_icons"
            camera:entries="@array/camera_id_entries"
            camera:largeIcons="@array/camera_id_largeicons" />
    <ListPreference
            camera:key="pref_camera_hdr_key"
            camera:defaultValue="@string/pref_camera_hdr_default"
            camera:title="@string/pref_camera_scenemode_entry_hdr"
            camera:entries="@array/pref_camera_hdr_entries"
            camera:entryValues="@array/pref_camera_hdr_entryvalues" />
    <CountDownTimerPreference
            camera:key="pref_camera_timer_key"
            camera:defaultValue="@string/pref_camera_timer_default"
            camera:title="@string/pref_camera_timer_title" />
    <ListPreference
            camera:key="pref_camera_timer_sound_key"
            camera:defaultValue="@string/pref_camera_timer_sound_default"
            camera:title="@string/pref_camera_timer_sound_title"
            camera:entries="@array/pref_camera_timer_sound_entries"
            camera:entryValues="@array/pref_camera_timer_sound_entryvalues" />
</PreferenceGroup>

每个元素(包括PreferenceGroup)对应一个类。

接下来是PreferenceInflator。这是解析xml的方法:

private CameraPreference inflate(XmlPullParser parser) {

    AttributeSet attrs = Xml.asAttributeSet(parser);
    ArrayList<CameraPreference> list = new ArrayList<CameraPreference>();
    Object args[] = new Object[]{mContext, attrs};

    try {
        for (int type = parser.next();
                type != XmlPullParser.END_DOCUMENT; type = parser.next()) {
            if (type != XmlPullParser.START_TAG) continue;
            CameraPreference pref = newPreference(parser.getName(), args);

            int depth = parser.getDepth();
            if (depth > list.size()) {
                list.add(pref);
            } else {
                list.set(depth - 1, pref);
            }
            if (depth > 1) {
                ((PreferenceGroup) list.get(depth - 2)).addChild(pref);
            }
        }

        if (list.size() == 0) {
            throw new InflateException("No root element found");
        }
        return list.get(0);
    } catch (XmlPullParserException e) {
        throw new InflateException(e);
    } catch (IOException e) {
        throw new InflateException(parser.getPositionDescription(), e);
    }
}

newPreference是我的错误发生的地方。此代码实际上适用于PreferenceGroup,但对任何子代都失败。

private CameraPreference newPreference(String tagName, Object[] args) {
    String name = PACKAGE_NAME + "." + tagName;
    Constructor<?> constructor = sConstructorMap.get(name);
    try {
        if (constructor == null) {
            // Class not found in the cache, see if it's real, and try to
            // add it
            Class<?> clazz = mContext.getClassLoader().loadClass(name);
            constructor = clazz.getConstructor(CTOR_SIGNATURE);
            sConstructorMap.put(name, constructor);
        }
        return (CameraPreference) constructor.newInstance(args);
    } catch (NoSuchMethodException e) {
        throw new InflateException("Error inflating class " + name, e);
    } catch (ClassNotFoundException e) {
        throw new InflateException("No such class: " + name, e);
    } catch (Exception e) {
        throw new InflateException("While create instance of " + name, e);
    }
}

错误是

  

android.view.InflateException:创建com.mynamespace.camera.IconListPreference的实例

IconListPreferenceListPreference延伸,这是代码在构造函数中抛出错误的地方。

public class ListPreference extends CameraPreference {
    private static final String TAG = "ListPreference";
    private final String mKey;
    private String mValue;
    private final CharSequence[] mDefaultValues;

    private CharSequence[] mEntries;
    private CharSequence[] mEntryValues;
    private boolean mLoaded = false;

    public ListPreference(Context context, AttributeSet attrs) {
        super(context, attrs);

        TypedArray a = context.obtainStyledAttributes(
                attrs, R.styleable.ListPreference, 0, 0);

        mKey = Util.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));
        a.recycle();
    }

}

问题是这一行,当找到null时抛出错误。

mKey = Util.checkNotNull(
                a.getString(R.styleable.ListPreference_key));

所以现在我们再往下走兔子洞了。 attrs.xml看起来像这样

<resources>
    <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" />
    </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="images" format="reference" />
    </declare-styleable>
</resources>

当我最初加载首选项xml文件时,命名空间看起来像

  

http://schemas.android.com/apk/res/com.android.gallery3d

我改变了以便建立

  

http://schemas.android.com/apk/ LIB /com.android.gallery3d

调试并倾注代码,我觉得有一些明显的东西我不知道为什么无法加载。

修改

在玩代码时,我发现没有正确设置attrs。必须在XML Parse的每次迭代中设置它。一旦我这样做了,我就开始在attrs中获取信息。

然后,我发现即使是attrs中的数据,TypedArray也没有填充任何内容。我仍然不确定为什么会这样。

尽管我讨厌这样做,但我可能只需要尝试使用attrs而不是TypedArray并做一些手动工作来将xml拉入对象。

0 个答案:

没有答案