Android Patterns: A Spinner where every item launches a different Activity
I'm trying to build a complex form where almost all of the elements are optional. It starts with just a single field and an "add element" button. When you click add, the form shows a Spinner
of the types of elements you can add to the form (location, photo, detailed note, timestamp other than "now", etc). When you select an item, it will launch an Activity
, and each item has a different associated Activity
.
In addition, each choice will have several bits of data, which it would be nice to store "with" the Activity
somehow:
- An icon and the displayed name in the
Spinner
- A key for storing the data in the db (as well as passing to a webservice)
- A layout for how to display the result on the original form (i.e. a thumbnail for the photo, the lat/lon for the location, etc)
I was considering a set of classes that all extend an abstract FormElement
class, and would have static elements for each of the above extra pieces of data. (An additional bump for this solution is how much of a pain Resources
are in a static context.)
How can I make this as clean and maintainable as possible? I'd really not enjoy editing five different files to add a new type of element to this form. (Mostly bec开发者_Go百科ause I can guarantee I'll miss one and spend hours chasing down unbugs.)
A few tips...
Unit tests will prevent "unbugs" :)
When each
Activity
has obtained the information it needs from the user, call Activity#setResult() with anIntent
that contains your per-type data.Intent
supports all theBundle
methods, so you can set different types of data as needed.To support #2, make sure you're using
Activity#startActivityForResult(Intent,int)
to launch it, and listen for the result inActivity#onActivityResult(int,Intent)
I would probably maintain the list of available "element" types for use with the
SpinnerAdapter
(e.g.,ArrayList<Class<? extends AbstractFormElement>>
, and invoke static methods like.getDisplayName()
,.getActivityClass()
, etc, in the Adapter'sgetView()
method, in order to determine what to display and what Activity to launch.In this way, your list would actually contain things like
{ MyPhotoElement.class, MyTextElement.class, MyDateElement.class, ...}
).As each element is added to the form, add it to an
ArrayList<AbstractFormElement>
, which will be used to back another Adapter for aListView
. That adapter will dispatch the inflation of a custom view layout, as well as the creation of a ViewHolder, based on what type of object it is -- that will require that each distinctAbstractFormElement
will have its own "view type", according to the Adapter. See BaseAdapter#getItemViewType(int) and relatedgetViewTypeCount()
.It's worth noting that these will need distinct view types only if one cannot be converted to the other... For example, if you have two "Elements" that only need to display a string of text in the list, those can both share a "text-only" view type. Likewise, two elements that only display a photo, or can easily convert one to the other (e.g., an icon with a caption, vs a photo thumbnail with no caption), can share a single "image-plus-caption" view type.
With the above in mind, you actually would end up having to modify different files to add a new type (well, I guess technically you could have them all in one file, as inner classes, but there's really no good argument for doing that), but if you've done your interface API correctly, and follow good OO practices, and implement good unit tests, you'll considerably reduce the amount of effort required to find bugs -- simply because most of the things involved in adding a new type would actually force a compiler error if you do it incorrectly. Add to that the fact that a proper unit test suite will be able to programmatically add all possible types, and ensure that everything displays properly, and you should have a pretty streamlined process for easy extensibility :)
It sounds like a lot of work, and it might seem tedious and verbose at first... But the end result is actually much more maintainable, especially if your list of element types is going to be fairly extensive.
精彩评论