Reputation: 1240
I'm hoping someone in the wide world of Android development has come across this problem/use case before, and has a relatively straight-forward approach for implementation.
I have a client Android application, that needs to render a list of fields that the end-user then fills out. Each field is a certain known-type, such as "Text" or "Radio", "MultiSelect", etc. When the user taps on a form, an API call is made to a backend which returns the schema for that form (ie: each field's UUID, title, description, hint text, etc), and the data for that form where some fields are likely already filled out from a prior time. Example of what data I would get over an API call:
{
"submittedBy": 8,
"updatedBy": 8,
"createdBy": 8,
"submittedDateMillis": 1489680600000,
"updatedDateMillis": 1489680600000,
"createdDateMillis": 1489680600000,
"name": "My Form",
"formTemplateId": 3,
"id": 0,
"schema": {
"6051c1e3-b4bf-4e6a-afe3-de2497dbff11": {
"units": "ft.",
"hintText": "Length of measurement",
"required": false,
"description": "Take the length of the measured item to 4 decimal places.",
"title": "Measurement",
"type": "number"
},
"fdf6ff0b-e60d-4591-a3e7-5467cd7bc67e": {
"enum": [
"Foo",
"Bar",
"Baz",
"Bat"
],
"required": true,
"hintText": "",
"description": "This is a description for a multiple choice question",
"title": "Multiple Choice (radio) title",
"type": "radio"
},
"203ef6d8-03fe-48e8-9a45-b18d12721d44": {
"enum": [
"Option 1",
"Option 2",
"Option 3",
"Option 4"
],
"required": true,
"hintText": "",
"description": "This is the description for a multiselect question",
"title": "This is the title for a multiselect question",
"type": "multiselect"
},
"751e9b8f-a59d-4e81-b3da-17ae44daa44e": {
"enum": [
"A dropdown answer",
"This is another option for a dropdown question it's limit is 130 characters"
],
"required": true,
"hintText": "",
"description": "This is the description for a dropdown question",
"title": "This is the title for a dropdown question",
"type": "select"
},
"33e13828-9171-4680-b68b-9838d4d42af8": {
"required": true,
"hintText": "This is the hint text for a text question limit 130 characters",
"description": "This is the description for a text question limit 5000 characters",
"title": "This is the title for a text question limit 130 characters",
"type": "text"
}
},
"fields": {
"6051c1e3-b4bf-4e6a-afe3-de2497dbff11": "5555.5555",
"fdf6ff0b-e60d-4591-a3e7-5467cd7bc67e": "Bar",
"751e9b8f-a59d-4e81-b3da-17ae44daa44e": "A dropdown answer",
"203ef6d8-03fe-48e8-9a45-b18d12721d44": [
"Option 1",
"Option 2",
"Option 4"
],
"33e13828-9171-4680-b68b-9838d4d42af8": "My answer for your text question."
}
}
The API call, say /api/v1/forms/0
, returns the above data. In that I have schema
which describes the field types, and fields
which give me the answers to populate (some of which could be missing). They both have UUIDs which "match up", so I know what field data to put into what form field.
Now I have to render that Form, and allow the user to tap "Submit" and POST
/PUT
the new data back to the API.
What is an approach for dealing with this? I consider myself a beginner in Android, and from what I've come up with so far, is probably not the best solution (and probably doesn't scale beyond say, 50 questions, as the "render" and "submit" portions of this activity will become slow):
schema
type, .inflate()
an XML layout that is whatever that .type
is (number
, text
, radio
, etc), and construct a Java type (FormElement
is what I'm calling it) that represents that schema JSON type. After .inflate()
, .setTag(formElement)
and "attach" that Java FormElement
to it.fields
mapping in the JSON, set the data to whatever that is. If not, leave it blank.FormElement
via .getTag()
. Get the FormElement#getType
to find the type of the View, then work backwards and knowing the View we are iterating on, cast it, get it's inner data value, build the resulting JSON data back up, and PUT
that to the API.I might assign every Widget
that represents a data entry point (Text, Radio, etc) a unique ID, based on the UUID from the schema
(UUID is v1, so one way is to get the timestamp, and hope for the best, since we would be going from 128 bits to 32 bits). Then use this ID later, when I need to pull data out after the user taps Submit.
There looks to be some promising capability in Android's Data Binding Library, but I don't think Android's data binding can handle the "dynamic" nature of laying out this UI, with different Widgets that have different data types (some of these are Pull Down menus).
PUT
request back to the server?Resources I've looked at so far, which shine some light on this overall problem:
.setTag()
& .getTag()
- What is the main purpose of setTag() getTag() methods of View? which seems to fit my use case, where I need to have the views "know" things about themselves (like where they came from in the JSON response).Thank you all ahead of time!
Upvotes: 4
Views: 2179
Reputation: 38263
You should create a representation of those fields in memory (like a Collection
of Field
's).
Then you can use RecyclerView to lay them out efficiently.
In RecyclerView, you can have view types (one for each Field type) that knows how to handle a particular field.
Inside the RecyclerView, to bind the views, you can use data binding. There is a demo on github that shows how to effectively use data binding with RecyclerView.
Last but not least, make sure your network operations are completely de-coupled from the UI. UI just reads the collection so each time you do a network request, it updates the collection, that notifies a change and RecyclerView will update itself. (you probably want to make optimistic updates on the collection since network requests may take time but that is a large topic to cover here).
Upvotes: 5