Android Form Generator

I’ve seen quite a few discussions pop up in various forums like Stack Overflow and the Android Developer Google Group about dynamically creating U.I. in Android applications. The official Android documentation seems to be very keen on the xml for layouts approach. This approach allows you to easily separate views from logic, which is a good thing. However, writing xml files for U.I. is not always flexible when working with dynamic data.

I’m working on a project right now that involves dynamically generating form-based user interfaces. The interfaces are assembled based on JSON schema files generated by a web service. I’ve written a small framework for handling this kind of task. In this post I’ll walk through the basic usage of the framework. I won’t go into a much detail about how the internal code is put together, but I will cover the basic API and the schema structure.

Core Features

  • auto-creation of form-based U.I. from json schema files
  • auto-population of form-based U.I. from existing json data ( must match schema format )
  • auto-layout based on priority indicated in schema ( also handles creating scroll bars for forms taller than the screen )
  • conditional visibility of form elements based on user input
  • functionality for saving user input into compatible json format

Download the source code here.

Basic API Usage

This section will explain the methods for creating, populating and saving the state of a form-based u.i.

Form Activity
The FormActivity class is an abstract class that takes care of most of the heavy lifting for you. It is intended to be sub-classed in your application. In the example project the sub-class is called FormGeneratorExample. It will work just like other Activity classes in the Android framework. Below we will look at its public methods.

generateForm( String data )

This method is responsible for parsing the JSON schema and creating the form. It should be supplied a string of valid JSON data. Note that the method does not require a JSONObject. There is a method in the FormActivity class for converting a file to a String. To generate a form-based U.I. we use the following method which produces the image below:

generateForm( FormActivity.parseFileToString( this, "schemas.json" ) );

Created by above snippet



populate( String data )

When supplied a String of JSON data that matches the current schema, this method will populate each field of the form. The syntax is nearly identical to generating a form, again parsing a file with JSON data to a String.

populate( FormActivity.parseFileToString( this, "data.json" ) );

Form populated with pre-existing values

save()

This method zips through each field of the form and caches the current attribute/value pairs into a JSONObject. From the image above it produces this JSON data.

{
    "genre":"2",
    "imported_from":"1",
    "release_year":"1999",
    "import":"1",
    "label":"1",
    "artist":"Boards Of Canada ", 
    "album_title":"Music Has The Right To Children",
    "formats":"1"
}

The Form Schema

As mentioned, each form is created from a JSON schema file. Below is a snippet from the schema included in the example project. It represents a form for audio recordings. The complete schema file is located in the assets folder of the example project. Each field in the schema represents a widget in the form-based U.I.

{
    "artist": {
        "type": "string",
        "id": "0",
        "default": "",
        "priority": "0"
    },
    "genre": {
        "type": "integer",
        "id": "5",
        "default": "0",
        "priority": "1",
        "options": {
            "0": "None",
            "1": "Rock",
            "2": "Electronic",
            "3": "Country",
            "4": "Hip-Hop",
            "5": "Jazz",
            "6": "Pop",
            "7": "World"
        }
    }
}

The first field in the snippet above is “artist”. The name of each field is used as a label for the input widget representing the field. This is important to note because it means that the names you give each field in the schema are converted to displayable names. You can use multiple word names by separating words with underscores. As an example, a field with the name “album_title” would be converted to Album Title when displayed as a label for the widget.

Required Fields

Each field in the schema has a few required attributes. These attributes are required because the code that parses a schema uses them to build the widgets in the form-based U.I. The required attributes are:

type – the data type of the field; this value ultimately informs the generator which kind of widget to create

default - a default value for the field; this value should match the data type specified by the type attribute; in the case of Spinner widgets the default should be an integer corresponding to an item in the options list ( not the actual string value )

priority – priority is used to visually order the widgets; by default all widgets are stacked vertically; if you do not need the widgets in a form to be in a specific order you can just set the value to zero; however do not omit this field from the schema

The Type Attribute

The type attribute of each field indicates which type of widget to use to represent the field.

string:

indicates to the parser to create an FormEditText widget; fields of type string also support an optional attribute called “hint”; for text input widgets the hint attribute sets the default text for when the widget contains no text;  below is an example:

"release_date": {
        "type": "string",
        "id": "2",
        "default": "",
        "priority": "2",
        "hint":"Example: 10/30/1999"
    },

Text field with hint defined in schema



integer:

indicates to the parser either a FormNumericEditText widget or a FormSpinner widget if there is an options object; below is an example of an integer type with an options object

"genre": {
        "type": "integer",
        "id": "5",
        "default": "0",
        "priority": "1",
        "options": {
            "0": "None",
            "1": "Rock",
            "2": "Electronic",
            "3": "Country",
            "4": "Hip-Hop",
            "5": "Jazz",
            "6": "Pop",
            "7": "World"
        }
    }

A Spinner populated with schema options property



boolean:

indicates to the parser to create a FormCheckBox widget; notice that the default value of the boolean field is “0″ and not false; booleans are represented as one or zero in the schema

"import": {
     "type": "boolean",
     "id": "6",
     "default": "0",
     "priority": "7",
 },

Example of Checkbox defined by boolan



Optional Attributes

Toggles

The framework allows you to specify the conditional visibility of form widgets based on user selections. In the example project there is a field called “import” which is of type boolean and represented in the U.I. as a FormCheckBox. When it is selected an “Imported From” FormSpinner widget is displayed. To achieve this we add an attribute to the “import” field called “toggles”. Below is an example of the “toggles” attribute:

"import": {
        "type": "boolean",
        "id": "6",
        "default": "0",
        "priority": "7",
        "toggles": {
            "1": [ "imported_from" ]
        }
    },
    "imported_from": {
        "type": "integer",
        "id": "7",
        "default": "0",
        "priority": "8",
        "options": {
            "0": "N/A",
            "1": "Spain",
            "2": "Italy",
            "3": "Germany",
            "4": "France",
            "5": "Japan",
            "6": "Australia",
            "7": "China",
            "8": "United States"
        }
    }

The syntax of the toggles attribute works like this. The “1″ indicates that when the value of the FormCheckBox changes to “1″ or true make all of the widgets in the corresponding array visible. The field could also indicate that multiple items toggle their visibility or that when the value is set to false or “0″ that other widgets become invisible, as in the example below:

"import": {
        "type": "boolean",
        "id": "6",
        "default": "0",
        "priority": "7",
        "toggles": {
            "1": [ "imported_from", "record_label", "original_release_date", "import_release_date" ],
            "0":[ "distributor" ]
        }
    },

The toggles attribute can also be used with FormSpinner widgets. Below is an example:

"label": {
        "type": "integer",
        "id": "3",
        "default": "-1",
        "priority": "3",
        "options": {
            "0": "none",
            "1": "Warp Records",
            "2": "Touch & Go",
            "3": "Warner Bros.",
            "4": "Sony Music",
            "5": "S.Y.R.",
            "6": "The Leaf Label"
        },
        "toggles": {
          "1":["formats"],
          "2":["formats"]
        }
    },
    "formats": {
        "type": "integer",
        "id": "4",
        "default": "0",
        "priority": "4",
        "options": {
            "0": "None",
            "1": "Compact Disk",
            "2": "Record/Vinyl",
            "3": "MP3/Download",
            "4": "Cassette",
            "5": "8-Track",
            "6": "Wax Cylinder",
            "7": "Laser Disc",
            "8": "Mini-Disk"
        }
    },

In the example above the formats field will only be displayed when option 1 or 2 is selected.

Note: if you populate a form-based U.I. with existing data and the data makes use of fields that toggle the visibility of other fields this functionality should still work. If it doesn’t work then there is a bug, please leave a comment…

Stability

This is an early version of this little framework. The code has been implemented in my production code, however it still needs to be thoroughly tested. It would be great to hear back from the community on this. If you have suggestions or think certain parts could be improved, don’t hesitate to leave a comment or question.

Future

I am hoping to add a couple of new features to this code as needed. It would be nice if there were a few additional parameters on a per widget basis and I’d like to implement more of the component set. That said, I don’t see a need for this project to go into a massive coding effort. I would like any additional features to be completely optional so that it can still be used as simply as it currently is.

Collaborators

If you have something you would like to contribute to this project don’t hesitate to let me know. It would be great to work with some other coders to optimize and extend what is already written.

Posted in Android | Tagged , , , , , , , | 35 Comments

35 Responses to Android Form Generator

  1. Rashi says:

    Hi,

    This is really nice and helped me a lot.
    I wanted to know where is the data which is taken from the user actually stored, i know it uses Json Object but what is the location and how to retrieve it for further use.

    Thanks
    Rashi

  2. Hi Rashi,

    Good question!

    In one project I worked on I just stored the entire json object as a string in one column of a sqlite table. You could create a sqlite database on the device based on the schema files with columns for each property too. Perhaps that would make a nice utility for the Forms framework ( a sqlite table generator ).

    Hope this is helpful.
    ~Jeremy

  3. Dias Nurul says:

    wow, it’s cool :D

  4. Gilles says:

    really nice!

  5. Otávio says:

    Wow Jeremy, awesome! I was really looking after a way to render fields in a more dynamic way. Thanks, I will definitelly take a look at your work. I’ve already built a Java web project doing just this and I was wondering the much effort it will take port it to the android plataform.

    regards, otávio

  6. Jeremy says:

    Otávio,

    Great, look forward to hearing your thoughts!

    Thanks,
    ~Jeremy

  7. Chris says:

    Sorry but I am confused about how to download the code from the Google repository – am I supposed to open each file and copy&paste the code from the repository to my Android project?

  8. Jeremy says:

    Hi Chris,

    Google Code hosts the files in a Subervsion (SVN) repository. You need to download and install Subversion in order to “check out” all of the files at once. Doing so will allow your version of the code to stay current in the event that updates are made.

    If however, you don’t want to bother with SVN, though I recommend you at least check it out, I’ve added a zipped version of the forms example and lib. You can download it here: https://github.com/jeremynealbrown/makemachine/

    Hope that helps you out,
    ~Jeremy

  9. Chris says:

    Thanks, I was able to get the files.

    Are you purposely outputting the items listed in a the spinners in a different order than what they appear in the JSON?

  10. Jeremy says:

    Hi Chris,

    Good question. As far as I understand it, when a JSON object is parsed, the sequence of it’s objects is not maintained. However, the sequence of a JSON array will be. Your best bet is to implement a sorting function on anything that needs to maintain a sequence. Perhaps wrapping all objects in a JSON array would be an improvement.

    ~Jeremy

  11. Chris says:

    Just wanted to let you know I’ve got this runnig on a test app, reading the JSON from a php file, which is pulling the JSON data from a MySQL database.

    I modified the code so that it “reorders” the Spinner options based on the order they appear in the JSON.

  12. Jeremy says:

    @Chris

    Cool, glad to know that it is working out for you! I wouldn’t mind taking a look at your changes if you want to share. If so, feel free to send the modified source to: jeremynealbrown at gmail.

  13. Jeremy,

    It appears we may be working on similar solutions. Perhaps we could collaborate? Could I ask you to take a look at Metawidget (http://metawidget.org)?

    We may find some nice points of integration. For example, Metawidget has a ‘pluggable inspection layer’, so perhaps you could plug your JSON parser in there?

    Regards,

    Richard.

  14. Vikas Patidar says:

    Hi Jeremy thank you for writing such a nice article and this API is very useful. If I use this API in my application then does it will affect the performance(loading and rendering time of form layout and elements) of the application?.

  15. Jeremy says:

    @Vikas Patidar

    The project I built this for used forms with up to 70 elements. In general, the view creation is very fast and the scrolling was smooth. I haven’t experienced any performance issues. That said, if you are debugging on an actual device that is logging to the console, you will most likely see a significant delay in the view creation.

    Thanks,
    ~Jeremy

  16. N03 says:

    Hi Jeremy, nice example, i new on android and i want to know how i can to put a button in the form with jason, i already run your example but i nned to do the submit but a can´t see the button any where.

    Thanks

    N03

  17. Jeremy says:

    @N03
    The best way to put a submit button is to use the contextual menu buttons. Or, you could change the height of the scroll view that the form resides in and insert a container that with a submit button in it.

  18. Smin Rana says:

    Very nice Jeremy, I need to try !!!

    Thanks again for such important post.

  19. Guy Fomi says:

    Very nice Jeremy…exactely what im looking for…please tell me, i would like to implement a schema editor that allow me to generate the json data from my android project instead of gettint it from a web service

    do you have any idea ?

    thx

    Guy

  20. Jeremy says:

    Hi Guy,

    Glad that the form generator is working out for you. As for generating schemas from within the app, you could build a database directly on the phone using sqlite and then use that as a model for your schema/forms.

  21. Brill Pappin says:

    We could use this occasionally.
    Do you have a SVN repo setup for it so we don’t have to keep checking for updates?

  22. Jeremy says:

    Hi Brill,

    Here is a link to the repo: http://bit.ly/lBoIbO. I have not been working with the Android platform lately. This code base will likely remain the way it is.

    Thanks for your interest.

  23. Andrew says:

    Is this form generator can be used for commercial projects? I see your google code license marked as GPLv3, it looks like close source apps can not adopt that.

  24. Jeremy says:

    This is a great question. Thanks for asking. I have changed to the license to a more liberal MIT license so that the code can be used in commercial closed source projects.

  25. Bamby says:

    Hi Jeremy,

    this is very great, and helped me out so much.
    but I’m confused to change the default size of the textview, how to do that ?

    Regards,

    Bamby

  26. naga bhushan says:

    i am new to android environment
    i have set the environment and learning android by books

    i have downloaded code for dynamic form generatior but how to execute it

    is 2010_08_26_android_forms_src_and_example.zip example for windows os or not
    please help me

    bhushan
    gvvit

  27. naga bhushan says:

    as i knew to this technology , in less time
    finally i got executed the code
    it is nice

    thank you

    but i want information about webservice to create json object and to validate the created form

    bhushan
    gvvit
    bhimvaram
    ap

  28. Kumaravel Sambandam says:

    Hi Jeremy, Excellent. I will also contribute. Thank you
    very much.

  29. Elijah Mwangi says:

    Could you please let me know if this project is on going?

  30. Jeremy says:

    Hi Elijah,

    I have no plans to develop this project any further. You are welcome to take the source code and do what you like with it.

  31. Andre says:

    Hey Jeremy..

    I know this is an old post, but I only just found it last night. I’m currently in the planning stages of a similar project, designing multi-page forms online to be used for data capture in an Android app.

    I’m quite new to Android dev, so just to check.. The screenshots look like they’re from the Eclair/FroYo era, so would your code still work with more recent versions of Android (specifically the 4.x range)?

  32. Matthew Wood says:

    Hi Jeremy,
    I love the work you have done. It’s fantastic and is what I have been looking for in regards to using dynamic forms.

    I only have one question. Have you thought about pulling the data from the form into an sqlite or even a mysql database?

  33. Jeremy says:

    Hi Andre,

    It has been quite some time since I’ve dabbled with the Android platform, so I do not know if this library is compatible with the current version of Android. It would be great to know though, so if you end up giving it a shot, please let me know what you discover.

    Thanks,
    ~Jeremy

  34. Jeremy says:

    Hi Mathew,

    That would be a great addition. I could see it working especially well if each database “adapter” conformed to an Interface. Please let me know if you explore this and perhaps we can add it to the project.

    ~Jeremy

  35. Andre says:

    Thanks Jeremy, from the reading I’ve done so far it doesn’t appear to be too different from current Android apps, so in theory I shouldn’t need to tweak too much.

    Honestly can’t say when this will actually happen (got quite a lot going on at the same time here), but I’ll definitely let you know when I have something to share.. :)

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>