1

我有一个使用NumberPickers 的应用程序。由于该应用程序支持旧版本的 Android,NumberPicker还没有可用的小部件,因此我必须使用外部库

在我的 XML 布局numberpicker_dialog.xml中,我定义NumberPicker如下:

<net.simonvt.numberpicker.NumberPicker
    android:id="@+id/interval_picker_1"
    ... />

然后在我的活动代码中,我有:

import net.simonvt.numberpicker.NumberPicker;

private View intervalPickerDialogBody;
private NumberPicker intervalPicker1;
private android.widget.NumberPicker api11_intervalPicker1;

protected void onCreate(Bundle savedInstanceState) {

    intervalPickerDialogBody = getLayoutInflater()
            .inflate(R.layout.numberpicker_dialog, null);

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
        // the casting in question happens here
        api11_intervalPicker1 = (android.widget.NumberPicker)
                 intervalPickerDialogBody.findViewById(R.id.interval_picker_1);
    } else {
        intervalPicker1 = (NumberPicker)
                 intervalPickerDialogBody.findViewById(R.id.interval_picker_1);
    }

}

它有效,并且确实在 Honeycomb 和更新版本android.widget.NumberPicker上使用,在旧版本net.simonvt.numberpicker.NumberPicker上使用。

但是现在我正在写一篇关于使用这个应用程序的论文,我想知道:

  1. 当它们实际上是不同的类时,为什么net.simonvt.numberpicker.NumberPicker从 XML 布局进行转换?android.widget.NumberPicker
  2. 当XML 布局中的那个实际上是来自外部库的那个时,为什么只有这种转换才能使设备android.widget.NumberPicker在 Honeycomb 和更新版本上使用?NumberPicker什么时候创建的实例android.widget.NumberPicker

在 Honeycomb 和更新版本上使用的事实android.widget.NumberPicker得到了前面代码片段的第二行等于true我的 4.0.3 手机上的事实的支持。

View tmp = intervalPickerDialogBody.findViewById(R.id.interval_picker_1);
tmp instanceof android.widget.NumberPicker // pseudocode

(谢谢你,朱尔斯,这比用箭头击中的证据要好得多。:))

如果有人想知道我是如何知道它android.widget.NumberPicker实际上在 Honeycomb 和更新版本上使用的,那是因为它的net.simonvt.numberpicker.NumberPicker上方和下方没有显示箭头,NumberPicker因为它是从 4.​​2 向后移植的,其中箭头已被删除。当我在运行 4.0.3 的设备上尝试它时,箭头在那里,即使它应该显示net.simonvt.numberpicker.NumberPicker,但它们不会。

我希望这个问题是可以理解的。感谢您详细说明该主题。

4

1 回答 1

1

有趣的问题。这似乎是因为在我看来是 LayoutInflater 中的一个错误。下面是用于决定特定 XML 标记使用什么 View 类的代码:

public final View createView(String name, String prefix, AttributeSet attrs)
        throws ClassNotFoundException, InflateException {
    Constructor constructor = sConstructorMap.get(name);
    Class clazz = null;

    try {
        if (constructor == null) {
            // Class not found in the cache, see if it's real, and try to add it
            clazz = mContext.getClassLoader().loadClass(
                    prefix != null ? (prefix + name) : name);

            if (mFilter != null && clazz != null) {
                boolean allowed = mFilter.onLoadClass(clazz);
                if (!allowed) {
                    failNotAllowed(name, prefix, attrs);
                }
            }
            constructor = clazz.getConstructor(mConstructorSignature);
            sConstructorMap.put(name, constructor);
        }
        ...

在调用此之前,前缀(即标记名称中最后一个点之前的所有内容)被分离出来并放入前缀参数中,因此对于您的 XML,调用是:

createView ("NumberPicker", "net.simonvt.numberpicker.", ...);

这意味着当 createView 方法在缓存中检查一个以前使用过的构造函数时,只要一个 NumberPicker 已经被使用过,它就会返回系统 NumberPicker。

作为我认为这是一个错误的必然结果,我将修改您上面的代码,以防止将来的 Android 变体通过使用 instanceof 运算符而不是测试 findViewById 返回的对象类型来修复错误的可能性您当前的版本号测试,即:

View tmp = intervalPickerDialogBody.findViewById(R.id.interval_picker_1);
if (tmp instanceof android.widget.NumberPicker) { // Note 1 below
    api11_intervalPicker1 = (android.widget.NumberPicker)
             intervalPickerDialogBody.findViewById(R.id.interval_picker_1);
} else {
    intervalPicker1 = (NumberPicker)
             intervalPickerDialogBody.findViewById(R.id.interval_picker_1);
}

注意 1 是我假设如果您要检查的类不存在,则 instanceof 检查不会在运行时崩溃。如果是这样,您将不得不颠倒检查的顺序。

于 2013-05-13T13:46:54.983 回答