From Android developer documentation, a statement reads as under:
An ID need not be unique throughout the entire tree, but it should be unique within the part of the tree you are searching (which may often be the entire tree, so it's best to be completely unique when possible).
Please help me understand, with an example, what is meant by 'part of the tree you are searching'?
Example, given following:
<AnOutterLayout>
<Button android:id="@+id/my_button".../>
<ASuitableInnerLayout>
<Button android:id="@+id/my_button".../>
</ASuitableInnerLayout>
</AnOutterLayout>
If I have:
Button myButton = (Button) findViewById(R.id.my_button);
What will be search tree here?
Thanks!
The "part of the tree you are searching" is typically the children of the ViewGroup
you're calling findViewById
on.
In an Activity
, the findViewById
method is implemented like this (source):
public View findViewById(int id) {
return getWindow().findViewById(id);
}
Ok, so how does a Window
implement findViewById
(source)?
public View findViewById(int id) {
return getDecorView().findViewById(id);
}
getDecorView
returns a View
- and all that the implementation of View
does is return itself (if the views ID matches the one passed in), or null (source):
public final View findViewById(int id) {
if (id < 0) {
return null;
}
return findViewTraversal(id);
}
protected View findViewTraversal(int id) {
if (id == mID) {
return this;
}
return null;
}
It's much more interesting if we look at the implementation for a ViewGroup
(source):
protected View findViewTraversal(int id) {
if (id == mID) {
return this;
}
final View[] where = mChildren;
final int len = mChildrenCount;
for (int i = 0; i < len; i++) {
View v = where[i];
if ((v.mPrivateFlags & PFLAG_IS_ROOT_NAMESPACE) == 0) {
v = v.findViewById(id);
if (v != null) {
return v;
}
}
}
return null;
}
So you see a ViewGroup
traverses its children searching for the ID you pass in. I'm not certain of the order of mChildren
, but I suspect it'll be in the order you add the views to the hierarchy (just checked - addView(View child)
does add views to the end of the mChildren
list, where as addView(View child, int index)
adds the view at index
position in the list).
So, for your example, which button was returned would depend on which ViewGroup
you were calling findViewById
on.
If you called anOutterLayout.findViewById(R.id.my_button)
, you'd get the first button - as this is the first child element it comes across that contains that id.
If you called anInnerLayout.findViewById(R.id.my_button)
, you'd get the second button.
However, if your layout file looked like this:
<AnOutterLayout>
<ASuitableInnerLayout>
<Button android:id="@+id/my_button".../>
</ASuitableInnerLayout>
<Button android:id="@+id/my_button".../>
</AnOutterLayout>
Then anOutterLayout.findViewById(R.id.my_button)
would actually return the button inside the inner layout - as this view was added to the hierarchy earlier, and is therefore earlier in the list of children for that view.
This assumes that views are added in the order they're present in the XML view hierarchy.