-
Notifications
You must be signed in to change notification settings - Fork 550
Code-behind fragment and widget access fixes #1378
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Missing: Documentation update. ;-) There's also a philosophical question which we've debated on (private?) Slack and Issue #1302: What API are we actually providing, and should this be consistent? For example, if a <TextView
android:id="@+id/first_text_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:editable="false"
/> What API are we exporting? There are three options, one of which must be documented: Option 1: "Direct" binding- We bind as it is in the internal TextView first_text_view {get;} This affords the "obvious" usage: protected override void OnCreate (Bundle bundle) {
first_text_view.Click += (o, e) => { /* ... */ };
} However, this option won't -- can't! -- support Option 2: "Indirect" binding: we bind as an indirect type in the internal __first_text_view_Views first_text_view {get;}
internal class __first_text_view_Views {
public TextView Value {get;} // name?
} This doesn't support the obvious usage of Option 1, but means that if the Usage is...weird: protected override void OnCreate (Bundle bundle) {
first_text_view.Value.Click += (o, e) => { /* ... */ };
// WTF is `Value`? What do we call the "other" `//@android:id` "overloads"? Value2, Value3, etc.?
} Option 3: Do both! Use Option 1 when there's only one This seems "obvious", but means there is no consistent API; it's dependent on how many This could be acceptable -- maybe even preferable?! -- but will complicate the documentation process. |
@@ -121,7 +122,7 @@ public override bool Execute () | |||
|
|||
void GetLineInfo (IXmlLineInfo linfo, out int line, out int column) | |||
{ | |||
if (linfo == null || linfo.HasLineInfo ()) { | |||
if (linfo == null || !linfo.HasLineInfo ()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I half wonder if this should be a separate PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nah. It's a one-liner and really doesn't deserve special treatment.
This option also doesn't take into account the hierarchy. I realize we can flatten it, but then you lose the contextual information from the axml and I think having this hierarchical context is, in fact, an advantage of our implementation over Android.
I don't know about "weird" here. This is used in all kinds of OOP GUI libraries where there's the notion of a container and its children or properties. As for the "other" overloads - the problem doesn't exist here because the other ones are nested in other parents and there's always unique and uniform path to access them: protected override void OnCreate (Bundle bundle) {
// MyTextView is the .axml widget id
first_layout.MyTextView.Widget.Click += (o, e) => { };
second_layout.MyTextView.Widget.Click += (o, e) => { };
} As for sibling widgets with the same id, they aren't directly accessible in Android either - the first one is picked and we bind that one. The other dupes are reachable by enumerating children of the layout/parent/container widget.
You seem to focus only on the duplicate IDs, but they are just part of the entire picture. I really think that the biggest advantage of my approach is the reflection of the hierarchical nature of the layout and it blends in nicely within the overall OOP paradigm of XA. |
48f2a7a
to
9c93623
Compare
|
||
<a name="" class="injected"/></a> | ||
|
||
# Preparing to use Code Behind | ||
|
||
In order to make use of this new feature there are a few changes which are required. | ||
An axml/xml file that you want to associate with an activity needs to be modified to | ||
include a few extra xml attributes on the root layout element. | ||
An ``axml/xml`` file that you want to associate with an activity needs to be modified to |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why the double backticks here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm, I didn't notice it. Emacs markdown
mode...
An ``axml/xml`` file that you want to associate with an activity needs to be modified to | ||
include a few extra xml attributes on the root layout element. | ||
|
||
Additionally, **only*** elements which have the `android:id` attribute will be accessible via |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Too many -- or not enough -- *
s. Presumably you want ***only***
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ditto here
|
||
class myScrollView_Class | ||
{ | ||
public ScrollView Widget { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In terms of keeping code short for clarity reasons, we should go with:
public ScrollView Widget {get;}
for the following: | ||
|
||
1. Android allows duplicate `android:id` values for **sibling** elements | ||
2. Android allows duplicate `android:id` values anywhere withn the layout tree |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Spelling error: you want within
.
3. Many layouts reuse XML in the form of fragments | ||
4. Many layouts reuse XML in the form of includes (using the `<include>` element) | ||
|
||
`1.` means that there's direct access (via `FindViewById` or with code-behind) to the **first** element with that `id` **only**. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of enclosing numbers in backticks, please surround with parens, e.g. (1), (2), etc.
// The actual view is set/replaced in `TestSuiteActivity.OnCreate()` | ||
SetContentView (Resource.Layout.Main); | ||
|
||
first_text_view.Widget.Click += delegate { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Question: Main.axml
contains no <include/>
s -- which aren't supported yet, anyway -- and only one element with android:id="@ +id/first_text_view"
.
Why isn't this a leaf node? Why do we need the extra .Widget
here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's a bug. first_text_view
should be a leaf element, but not because it's a single one with this particular id
but because it doesn't have any child elements with android:id
fad217c
to
6700103
Compare
build |
continue; | ||
} | ||
|
||
Log.LogDebugMessage ($"Inner subtree: element == {subtree.LocalName}"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nice :D probably want to remote the debugging info
568099e
to
254627a
Compare
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.
254627a
to
2229509
Compare
) Fragment handling code partially taken from: #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.
Fragment handling code partially taken from #1302
Fragments are treated as normal widget, with the difference that they have to be
found using
FragmentManager.FindFragmentById
instead of the usualActivity.FindViewById
method.Each generated class also gets a property named
Widget
which returns theactual 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 usedto 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
onit, we would end up using the activity's type for the root element's property in
the generated code, instead of its actual type.