Reputation: 7037
I have an app using NumberPicker
s. Since the app supports older versions of Android, where no NumberPicker
widget had been available yet, I have to use an external library.
In my XML layout numberpicker_dialog.xml
, I define the NumberPicker
like this:
<net.simonvt.numberpicker.NumberPicker
android:id="@+id/interval_picker_1"
... />
Then in the code of my activity, I have:
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);
}
}
It works, and really on Honeycomb and newer, android.widget.NumberPicker
is used, on older versions, net.simonvt.numberpicker.NumberPicker
is used.
But now that I am writing a thesis about the app using this, I wonder:
net.simonvt.numberpicker.NumberPicker
from the XML layout to android.widget.NumberPicker
work when they in reality are different classes?android.widget.NumberPicker
on Honeycomb and newer when the NumberPicker
that's in the XML layout is actually the one from the exetrnal library? When is an instance of android.widget.NumberPicker
ever created?The fact that android.widget.NumberPicker
is used on Honeycomb and newer is supported by the fact the second line of the forecoming code snippet equals true
on my 4.0.3 phone.
View tmp = intervalPickerDialogBody.findViewById(R.id.interval_picker_1);
tmp instanceof android.widget.NumberPicker // pseudocode
(Thank you, Jules, this is a much better proof than the striked one with the arrows. :))
If anyone wonders how I know that android.widget.NumberPicker
is actually used on Honeycomb and newer, it's because net.simonvt.numberpicker.NumberPicker
doesn't display arrows above and below the NumberPicker
as it was backported from 4.2 where the arrows have been removed. And when I try it on my device running 4.0.3, the arrows are there even though if it should display net.simonvt.numberpicker.NumberPicker
, they wouldn't be.
I hope the question is understandable. Thanks for elaborating on the subject.
Upvotes: 1
Views: 1141
Reputation: 15199
Interesting question. It seems to be because of what appears to me to be a bug in LayoutInflater. Here's the code used to decide what View class is used for a particular XML tag:
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);
}
...
Before this is called, the prefix (i.e. everything up to and including the last dot in the tag name) is separated out and put into the prefix parameter, so for your XML, the call is:
createView ("NumberPicker", "net.simonvt.numberpicker.", ...);
This means that when the createView method checks in the cache for a previously-used constructor, as long as a NumberPicker has been used before it will return the system NumberPicker.
As a corollary to my belief that this is a bug, I would modify your code above to safeguard against the possibility that a future variant of Android will fix the bug by testing which type of object is returned by findViewById using the instanceof operator, rather than your current version number test, i.e.:
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);
}
Note 1 is that I'm making the assumption that the instanceof check won't crash at run time if the class you're checking against doesn't exist. If it does, you'll have to reverse the order of the checks.
Upvotes: 1