Easier way to set values in a class from UI - android

Often there's a scenario where there's a number of UI fields that have to be copied into Model objects. For instance, I'm currently writing a cart page that accepts credit card information in an Activity and I need to write all the inputted values to a CreditCard object.
I end up with blocks of code that look like the following:
CreditCard card = new CreditCard();
card.setFullName(txtFullName.getText().toString());
card.setAddress(txtStreetAddress.getText().toString());
card.setCity(txtCity.getText().toString());
card.setState((String) spinnerState.getSelectedItem());
card.setZip(txtZip.getText().toString());
card.setPhone(txtPhone.getText().toString());
card.setMonth(txtMonth.getText().toString());
card.setYear(txtYear.getText().toString());
card.setNumber(txtNum.getText().toString());
card.setCvv(txtcvv.getText().toString());
I've tried to come up with a way via UI tags in XML or other means to simplify/automate this process but I can't come up with an efficient means. Am I missing out on something?

Unfortunately, there isn't anything hard-wired into Android that will let you bind large chunks of data (as shown in your example) from UI in an elegant way, yet.
One thing you can do to make your code cleaner/more testable is "injecting" the fields i.e. "txtFullName.getText().toString()" into the CreditCard constructor. That way, you do not have superfluous "setter" code like you do in your example. However, you would still have a block of code when instantiating the Credit Card object:
CreditCard card = new CreditCard(arg1, arg2, arg3, arg4..etc)
Another thing you can do is use a dependency injection (DI) framework like Dagger. Dagger does the "injecting" of fields for you in Module(s) that you specify for your class(es), "behind the scene".
http://square.github.io/dagger/
EDIT: check out the new data binding feature released in Android Studio 1.3 (currently Beta)

Related

MutableLiveData<ArrayList<T>> vs MutableLiveData<List<T>>. Why should we use List here?

I created a library here: https://github.com/chanjungkim/ALiveData
This library is made because of MutableLiveData<ArrayList<T>>. Many people who learns about LiveData complains or they are confused with this type when they need to manipulate(add, remove, etc) the MutableLiveData. That's because ArrayList is easy to manipulate and _arrayList.value!!.add(item) or _arrayList.value!!.remove(0) seems to notify. But they don't.
At the end, when we want to notify, we must assign a value like _arrayList.value!! = mList. ArrayList and List both need to set the data like _arrayList.value!! = mArrayList or _arrayList.value!! = mList.
My question is List doesn't have add(), remove(), etc. On the other hand, ArrayList already has those functions and helps us manipulate the list much easier.
some people suggested like this
_list.value = list.value.toMutableList().doWhatever().toList()
So, what's the point of using List over ArrayList? Could you give me example with the explanation of using it?
LiveData can be used in different ways, and of course there is no one correct way, but a very common way of using it is within the Android MVVM architecture recommended by Google for use in Android apps.
In this architecture, the Activity (or Fragment) observe the LiveData of the ViewModel. When doing this, the goal would be to make the UI as 'dumb' as possible, where you try to handle as much of the app logic and behaviour in the ViewModel, and the Activity simply observes and reflects it on the UI.
In a case like this, it is often preferable for the values of the LiveData being observed to be immutable.
By doing this, it limits the Activity from being able to manipulate the data it is observing, such as add()ing or remove()ing anything from it. As just described, the goal should be to limit the UI's ability to make exactly these type of changes. If the Activity wants to add() an item to an ArrayList that it is observing, it should instead do this by calling a method on the ViewModel, which will in turn update it's own LiveData.value to the new, updated list, which will in turn be observed by the Activity and updated on the UI.
By only allowing the Activity to observe the immutable values, it helps enforce this separation of concerns, and limits any accidental 'leak' of logic into the Activity itself.
This idea can be extended further by ensuring that the observed values are of type LiveData, and not MutableLiveData. Using the latter can allow the Activity to manipulate the live data on its own, and break the MVVM pattern.
A List is an interface, and defines the methods that must be implemented by any classes that would like to behave like a list. It can be considered the 'most basic' version of a list, and only defines the minimum requirements for an implementing class to behave like a list.
In the same way, the List interface itself extends the Collections interface, which in turn extends the Iterable interface. Each one adds more functionality to the one before it... kind of like lego blocks stacked on top of each other, creating more complex shapes.
An ArrayList is a class, which implements MutableList (which itself implements List). This means that an ArrayList can be instantiated, and passed around as an actual object. Because of this object oriented design, and according to the Liskov substitution principle, any class (or interface) can be replaced by a subclass (or class implementing the interface) interchangeably.
This is a fundamental principle to object oriented design. It helps break parts of the application down into smaller, more basic and more manageable pieces, and then grow as required.
To answer your question more specifically, if the class that is observing the LiveData only cares about the methods defined in the List interface, then that is the all it requires to know about the value. The actual value could in fact be an ArrayList, a MutableList or even a custom class MyOwnFancyList<E>: List<E>, it does not matter to the observer, just as long as it implements the List interface.

Is setting fields line after line from a Firestore document read an anti-pattern?

I have a document on Firestore, from which I read its fields in a fragment. Since it has many fields, I set variables in the Activity that hosts this fragment so that I can pass the data between other fragments. In order to achieve that, I realize that I have to write similar lines of codes over and over again, which got me to thinking if there is a better way.
Two possible solutions that come to my mind:
Structure all these fields in JSON format -> something that wouldn't be suitable in Firestore's document system imo
Put all these fields into a serializable data class which I keep in the activity then pass it around the bundles of fragments -> Seemed to complicated and I would still have to write it.get(foo) as bar for each of the field's of this class' constructor.
Given all these, what is the best approach? Thanks in advance.
You have a several options on how to approach this. There is none that's necessarily better than another. Ultimately, you will pick the one that best suits your needs and preferences.
You can do what you're doing now.
You can go a step further an actually check the types of the values instead of just blindly casting them (which would cause a crash at runtime if they didn't match).
You can provide a Class object to get(String, Class<T>) that can automatically map the fields to properties in a new object of type T, as long as the types also match.
You can call a variety of type-specific versions of get, such as getString()
Ultimately you will have to decide if you are going to trust what you get in the snapshot and allow errors to happen, or trust nothing and check everything. It's up to you.

Android Spinner - System view VS User view

I have been creating Spinner controls (Combo boxes/Drop downs) in one of my apps, and was surprised to find out how difficult it was to achieve all of the following features:
User facing Strings are externalized, taking advantage of strings.xml internationalisation (I18N) feature of Android.
Spinner selections operate using a System view, which facilitates not having to work with or map Strings to meaningful values (yuck).
User view to System view mapping should be easy, automated and minimal (i.e not hand rolled for every component).
Others have attempted solutions to this, but universally as far as I could see they suffer from one or many of the following problems:
UI code is creeping into their enum class which doesn’t belong there (messy), nearly all existing solutions suffered from this.
Hardcoded User facing Strings in their enum classes. Because these are not externalized you cannot do I18N using the stock Android features.
Authors typically make the Fragment or Activity an OnItemSelectedListener which perpetuates a common problem of inheritance for convenience, where composition is more appropriate.
I have developed my own solution which does this: http://www.androidanalyse.com/android-spinner-externalize-user-strings-mapped-to-system-enum/
My question is, have I missed something? This seems like something that should not have been this hard (which makes me feel like I'm possibly reinventing the wheel).
Below is some example code showing my solution in-use (which is available Apache 2 license from the link above).
String none = getString(R.string.none);
String light = getString(R.string.light);
String medium = getString(R.string.medium);
String strong = getString(R.string.strong);
SpinnerUtil.createNewSpinner(view, R.id.wind, Arrays.asList(none, light, medium, strong), WindLevel.values(),
new SpinnerItemSelectedListener<WindLevel>() {
public void onItemSelected(Spinner item, WindLevel value) {
// Take whatever action you wish to here.
}});
I would just use ArrayAdapter<WindLevel>. Yes, you created a custom typed listener, but the regular event listener gets the position and can call getItem() on the ArrayAdapter<WindLevel> to get a WindLevel properly typed.
IMHO, the vast majority of Spinner widgets will be populated with material read in from a database, the Internet, or some other dynamic data source, rather than populated by some sort of enum with display values coming from static strings that can be internationalized ahead of time.
This is not to say that your code is useless: if you find it useful, then it was worth writing. And I am sure that there are apps out there that contain your targeted pattern (i.e., a Spinner backed by an enum or equivalent where the display values are known in advance and can be internationalized) who might find your solution useful as well. Every developer who writes enough code cooks up these sorts of helper classes and the like that help map an OS or framework model into something that better fits the developer's own mental model. So long as you are not perceiving any performance issues, it's all good.
Also, note that OnItemSelectedListener is an interface; implementing that interface on an existing class is not inheritance.
I believe the reason nobody answered you is :
What problem are you trying to solve ? Spinners existed prior to your well designed attempt.
Why reinvent them in exactly the same way they exist in Android ?
http://developer.android.com/guide/topics/ui/controls/spinner.html
It is a beautiful wheel indeed you designed, but still, it is just a wheel :)
UPDATE :
I think I begin to understand what you did. This is interesting. I'm not sure why you did not go to the pattern implemented by the ListPreference with its entries and entryvalues.
In fact, I'm not sure I understand why the Android team did not go that route either.
In any case, it is worth proposing your idea to the Android framework. It is after all open source.

Where should this code live - in my Activity or my Adapter?

I'm looking for guidance as to how to modularize my code. I have an activity and a listAdapter and they are getting pretty complex. I'm not sure what code should live where and how much knowledge each of these 2 classes should have of each other. How do you decide whether to put code in an activity or its adapter? And what patterns do you use to keep these classes as lean as possible?
Your description is too generic, so I cannot give you an exact answer (would be useful to explain why they are getting bigger and bigger, what is the extra code good for).
But generically speaking, just think about what each class supposed to do. The "Activity" (as I see it), is a main controller, it "knows everybody", and it connects the other components together (the ListView with the list adapter). The list adapter's purpose is simply to map data to views. If they are getting bigger, extract new (utility) classes.
For example assume a big part of the code in ListAdapter formats timestamps (eg. takes timestamp as long value, and based on current time creates a string like "2 hours ago"). Then it makes sense to create a new utility class called TimeFormat (with a constructor which takes a context, you'll need it later to fetch string resources). Then the ListAdapter will create an instance of this class.
Another example would be data saving. In that case you could create a class called "Model" or "Document" (again with a constructor taking a "Context" instance). It would be responsible (for example) to load the data by parsin XML files, and to save the data by generating XML files. In this case this class would be instantiated by the activity.
Also note that the ListAdapter should really do what it supposed to do: create/setup views based on data. It should never depend on other views (in other views it should work with any ListView in any layout file). So if you have "findViewById" call, which access a view outside of the ListView (or the ListView itself), then that code should be moved to the activity.
Also, when in doubt you can try to find an open source application, which is relatively mature, and does something similarn (and see how that is solving the problem).
Per the adapater documentation in android
An Adapter object acts as a bridge between an AdapterView and the underlying data for that view. The Adapter provides access to the data items. The Adapter is also responsible for making a View for each item in the data set.
So if your code has to do with getting the data to display or creating the views, then it goes in the adapter. Everything else goes in the Activity or else where. If you're spending a lot of code retrieving the information you want to display, consider using some sort of AsyncTaskLoader class. Note that loader classes can be accessed from API Levels less than 3.0 using the android compatibility package.

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 because 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 an Intent that contains your per-type data. Intent supports all the Bundle 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 in Activity#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's getView() 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 a ListView. 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 distinct AbstractFormElement will have its own "view type", according to the Adapter. See BaseAdapter#getItemViewType(int) and related getViewTypeCount().
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.

Categories

Resources