You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Fragment handling code partially taken from dotnet#1302
Fragments are treated as normal widget, with the difference that they have to be
found using `FragmentManager.FindFragmentById` instead of the usual
`Activity.FindViewById` method.
Each generated class also gets a property named `Widget` which returns the
actual Android widget as found in the layout file. This allows us to keep
hierarchical nature of the XML code (thus handling nested widgets with duplicate
ids gracefully) while being able to access the parent widget itself.
The commit introduces a new attribute called `tools:managedType` which is used
to specify the element associated property's type. We cannot use `tools:class`
for this purpose since the layout root element already uses it to specify the
name of the generated code-behind class and if the element had `android:id` on
it, we would end up using the activity's type for the root element's property in
the generated code, instead of its actual type.
The code-behind will have this rough structure (class names are different to keep the documentation clear):
141
+
142
+
```csharp
143
+
myScrollView_ClassmyScrollView {
144
+
get { returnnewmyScrollView_Class (this); }
145
+
}
146
+
147
+
classmyScrollView_Class
148
+
{
149
+
publicScrollViewWidget {
150
+
get { /* ... */ }
151
+
}
152
+
153
+
publicTextViewmyTextView {
154
+
get { /* ... */ }
155
+
}
156
+
157
+
publicmyScrollView_Class (MyActivityparent) {}
158
+
}
159
+
```
160
+
161
+
So in order to access the widgets you'd use code similar to:
162
+
163
+
```csharp
164
+
InitializeContentView ();
165
+
myScrollView.Widget.Fling (100);
166
+
myScrollView.myTextView.AutoSizeMaxTextSize=40;
167
+
```
168
+
169
+
### Code structure rationale
170
+
171
+
It may seem that it would be simpler to generate code which would put properties returning the layout elements directly in the
172
+
Activity partial class instead of outputing a seemingly complex nested class structure. This approach would work if it wasn't
173
+
for the following:
174
+
175
+
1. Android allows duplicate `android:id` values for **sibling** elements
176
+
2. Android allows duplicate `android:id` values anywhere withn the layout tree
177
+
3. Many layouts reuse XML in the form of fragments
178
+
4. Many layouts reuse XML in the form of includes (using the `<include>` element)
179
+
180
+
`1.` means that there's direct access (via `FindViewById` or with code-behind) to the **first** element with that `id`**only**.
181
+
The rest of elements can be accessed only by enumerating the child collection. This is how it works in Android and we do not deviate
182
+
from the Android approach currently.
183
+
184
+
`2.` works in Android by walking down the element hierarchy (using `FindViewById`) until we find the direct parent of the element we seek and,
185
+
despite being tedious, this approach creates no conflicts and issues with accessing the elements with the same `id`
186
+
187
+
If we "flattened" the hierarchy, however, we would create the issue ourselves as suddenly we'd have `id` conflicts where there would have been
188
+
none before. Additionally, we wouldn't be able to generate code to directly access the farther elements, similar to `1.`. Or we could but we would
189
+
have to come up with a scheme to generate unique names for our properties for instance by appending a monotonously increasing integer to the base name,
190
+
e.g. given the base `id` of `myTextView` we would have properties named `myTextView`, `myTextView1`, `myTextView2` and so on.
191
+
192
+
It may not seem to be a big problem, after all there's a clearly defined naming convention that is predictable. But, is it? What happens if one element
193
+
with the shared `id` in the middle is removed? The elements following it are renumbered and suddenly our code works subtly differently - where `myTextView2`
194
+
was used to refer to the 3rd control, now it not only does not exist (causing a build error for the **third** instance of the element) but it is now silently
195
+
referred to by `myTextView1` which might again introduce subtle issues to the way the code works.
196
+
197
+
What happens when the layouts containing the "duplicate" `id`s are reordered? We have no compilation error as in the scenario above, it's much worse - suddenly
198
+
and quietly the code works differently, because the properties refer to **different** widgets (and thus layouts) but with the same `id`s!
199
+
200
+
`3.` and `4.` make the situation worse as they can introduce a number of "duplicate" `id` values all over the place and cause the `1.` and `2.` issues.
201
+
202
+
The hierarchical approach generates code that's inherently object-oriented, reflects the structure of the layout and in case of removing of elements will
203
+
generate a compile-time error, while in case of reordering of elements it will keep working correctly as long as the `id` "path" doesn't change (i.e. the
204
+
involved elements keep their `id` values from the root all the way to the leaf child). The only slightly awkward aspect is the necessity to introduce the
205
+
`Widget` property in each wrapper class in order to enable referring to the element itself and not just its children. However, since the usage and naming is
206
+
consistent, this is simply a matter of getting used to the convention.
207
+
208
+
## Managed types
209
+
210
+
By default each element for which we generate code-behind has its managed type set to
211
+
its local name, for instance
212
+
213
+
```xml
214
+
<TextViewandroid:id=""@+id/textView" />
215
+
```
216
+
217
+
Will generate a property named `textView` of type `TextView`. It works fine in most cases
218
+
but sometimes you might find code which either refers to a custom widget using the package
219
+
name or a fragment which uses the case-insensitive `android:name` attribute syntax, for
220
+
instance:
221
+
222
+
```xml
223
+
<fragment
224
+
android:name="commonsamplelibrary.LogFragment"
225
+
android:id="@+id/log_fragment" />
226
+
```
227
+
228
+
In this case the generated property would have the managed type `commonsamplelibrary.LogFragment`,
229
+
however the actual managed type fully qualified name is `CommonSampleLibrary.LogFragment` and thus
230
+
the generated code would fail to compile. The solution is to add the `tools:managedType` attribute
231
+
which specifies the element's (all elements support this attribute) managed type.
232
+
233
+
One may wonder why we didn't reuse the `tools:class` attribute to specify the managed type? It is
234
+
because that attribute is used to specify the code-behind partial class name on the root element of
235
+
the layout and should the element had the `android:id` attribute present we'd end up with generated
236
+
code that would use the **activity** type for the element's associated property instead of its
237
+
actual type and the code wouldn't build.
238
+
82
239
# How it works
83
240
84
241
There are a couple of new MSBuild Tasks which generate the code behind.
0 commit comments