Saturday, October 20, 2012

How the JSON Android.Dialog project works - and how WP.Dialog might work

Here's a very rough description of what I'm thinking - https://speakerdeck.com/u/cirrious/p/cross-plat ...

WP.Dialog is (I think) just the 'Form' part of that - just like Android.Dialog and MonoTouch.Dialog - although later it might grow in scope to the entire project - baby steps first:)

Also to be clear... I think WP.Dialog can exist and run without any dependency on MvvmCross - I don't see why it couldn't be useful for code-behind projects, for MvvmLight, etc

The projects
There are 5 projects:

  • Core.Dialog - a PCL containing:
    • Descriptions - the structs which describe a UI
    • Elements - the basic interfaces which UI components have to obey
    • Builder - the abstract code which turns Descriptions into Elements
  • Android
    • Android.Dialog - an Android Library containing:
        • Attributes - not currently used but provide a *very* nice way to turn C# models into UIs - see MonoTouch.Dialog for more
        • Builder - inherits from Core.Dialog.Builder - provides the *very small* Android specific building part
          • Elements - the default elements... the ones to start with are:
            • RootElement (which is a mess because it is a RootElement and also has RadioGroup functionality built in)
            • StringElement - displays a Caption and a Value
            • ValueElement - displays a Caption and an Editable Value
              • EntryElement
              • FloatElement
              • CheckboxElement
            • DO NOT START with RadioElement, WebElement, HtmlElement, etc - it's confusing!
        • Enums - mainly ignored... most seem to be there for MT.Dialog API compatability
        • Root-Level classes - DialogListView and DialogAdapter - together these 2 things provide the 'UI Control'
      • DialogSampleApp - the Android Demo project
        • MainDialogActivity - is the only activity that has been updated to the new JSON
  • Touch - ****nowhere near working****
    • Cirrious.MvvmCross.Dialog.Touch - my branch of MonoTouch.Dialog
    • Sample - the MonoTouch Sample

Within this layout... I guess WP should work in a new folder WindowsPhone and have two projects - a Dialog and a Sample :)

Naming: I'd like to put these projects in some namespace - for Mvx I used Cirrious (which is my company name) but that's mainly because of the way the project started - it was a company job! No idea what namespace to use, but I'd like to use Something.Dialog.Core rather than just Dialog.Core...


The MainDialogActivity code
For understanding Android.Dialog:
  • a good place to start might be MainDialogActivity.
  • This contains:
    • a big block of JSON
    • some code at the start of OnCreate to load the JSON
    • a big block of commented out C# which is equivalent to the JSON
Putting the big block of comment out code side-by-side with the JSON should help work out what is happening - and maybe looking at Dialog.Core.Descriptions will help with working out what the structure of the API is.

A quick walkthrough the code at the start of OnCreate:
            DroidResources.Initialise(typeof(Resource.Layout));
            var parser = new DroidElementBuilder();
 
            var setter = new ExampleActionPropertySetter();
            setter.Actions["ShowDialogActivity"] = (sender, args) => StartNew();
            setter.Actions["ShowDialogListViewActivity"] = (sender, args) => ClickList();
            setter.Actions["ElementTest"] = (sender, args) => ClickElementTest();
            parser.CustomPropertySetters["Action"] = setter;
            var description = Newtonsoft.Json.JsonConvert.DeserializeObject<ElementDescription>(JsonText);
            Root = parser.Build(description) as RootElement;
Breaking this down:

            DroidResources.Initialise(typeof(Resource.Layout));

The first line initialise the DroidResources - what this does is it tells the DroidResources where to look for numeric Android ResourceIds - e.g. when you load a StringElement then the framework might look for some AXML called dialog_string_layout (or similar) - these resources are in the resources folder.

            var parser = new DroidElementBuilder();
This creates a builder (not a parser - sorry!).

The Builder stores a lookup list of string names to Element Types

There is a default parameter to DroidElementBuilder which tells the Builder to reflect the Android.Dialog namespace and to look for the default set of Element Types.

Users can also register custom Element Types if they want to - so if you invent a RuneElement, then you can add it.

            var setter = new ExampleActionPropertySetter();
            setter.Actions["ShowDialogActivity"] = (sender, args) => StartNew();
            setter.Actions["ShowDialogListViewActivity"] = (sender, args) => ClickList();
            setter.Actions["ElementTest"] = (sender, args) => ClickElementTest();
            parser.CustomPropertySetters["Action"] = setter;

This block of code sets up a custom property setter for 'Action'. This means that if ever a property is marked in the Json with a value "@Action:foobar" then this property and foobar will be passed to the custom setter.

In a 'coming soon' step I expect to add an MvxBind custom property setter...

            var description = Newtonsoft.Json.JsonConvert.DeserializeObject<ElementDescription>(JsonText);

This uses PCL JSON.Net to deserialize the ElementDescription JSON. Note that the framework is decoupled from JSON - so users could use XML, ProtoBuf, C#, etc instead!


            Root = parser.Build(description) as RootElement;
Finally this line passes the description to the builder :)


How an Android Element is built
If some JSON comes in like:

                {
                    'Key':'String',
                    'Properties':{
                        'Caption':'Click for EntryElement Test',
                        'Click':'@Action:ElementTest'
                    }
                }
then this maps to an ElementDescription.

The builder uses this:
  • The Key 'String' is mapped to 'StringElement' - so it uses Reflection to call the parameterless constructor of StringElement (or the constructor with default parameters)
  • The builder then iterates the Properties 
    • uses reflection to set the Caption property on the StringElement to 'Click for EntryElement Test'
    • notices the '@Action:' prefix - so passes the Click PropertyInfo and 'ElementTest' to the custom property setter
That's it! You can set as many properties as there are available - it's up to the StringElement to turn those properties into something on the display.

Note that property values can be strings, numerics or booleans - anything else has to go through a special handler (e.g. a string lookup or a JSON serialised object)

Note that there is no real data binding right now - but that will come with '@MvxBind;' :)

Note that later there may be some chances to add platform specific properties - e.g. something like:

                    'Properties':{
                        'Caption':'Click for EntryElement Test',
                        'Click':'@Action:ElementTest',
                        '@Droid:WarpSpeed':4,
                        '@WindowsPhone:Pinned':false,
                    }


How an entire Dialog is displayed
In Android and in Touch, the Dialog is displayed using a ListView

The reason for this is because it allows UI virtualisation... and also because MonoTouch doesn't really have a StackPanel!

This use of a ListView makes the UI a bit more complicated than is really needed... e.g. for small forms it would actually be a lot easier to not have any cell reuse.

For WindowsPhone, it might be easier to just use a StackPanel with fixed members - but you could use a List if you wanted to....

In Android, the basic approach is:

- the UI View (Activity) is a List (including Groupings)
- the UI View gets help from an Adapter
- the Adapter stores the list of Elements
- when it comes to time to render the UI, then the Adapter's GetView gets called for every visible 'index'
- the Adapter passes the GetView call through to the GetView on the individual Element - this is passed through to GetViewImpl
- currently the GetViewImpl passes everything through eventually to DroidResources - which loads 'axml' for the element.
- calls like UpdateDetailDisplay and UpdateCaptionDisplay are used in the Element to actually fill in values in the Element

Note that because it uses a list, then there is an opportunity for cell reuse. In Mt.Dialog this works quite well. In Android.Dialog I think it's currently broken - but I will fix it.

So for WP.Dialog
I would expect for WP:
  • the same JSON will be used
  • the JSON will be parsed by JSON.Net
  • a WindowsPhoneBuilder will be used
  • this WindowsPhoneBuilder will (by default) know about a set of WindowsPhone Elements - which it will construct using reflection
  • these Elements will contain some C# and will somehow map to XAML...
  • the XAML will be parented either in a StackPanel (inside a ScrollViewer) or inside a ListView
  • if you use a ListView it's just a bit more complicated to route the bindings and to reuse controls (it's a standard 'ListItemSelector' type thing)
  • it would be really nice if the XAML is very easily styleable - like custom controls are
To get started...
  • maybe just try some JSON with some StringElements - each has a Caption and Value
  • then try adding an EntryElement - Caption and Value
  • then try adding a CheckboxElement - Caption and Boolean Value
The idea would be to creative native looking form UIs from the same JSON:

And then (if we're lucky) people can add platform specific styling to those UIs.

No comments:

Post a Comment