I saw some articles when Data Binding was announced have titles like this "Time To Leave findViewById", for some reason I thought it's going to be a replacement for libs like butterknife but when I looked at it and it seemed to me that it's only links model classes to view (title -> R.id.title) so when change title in view or in code it will reflect on the other end.
Is that all to it or am I missing something else?
That is one aspect, but it isn't the biggest. You can add expressions to do XML and have the data assigned directly to the View. For example, if you have a TextView showing the first name, you might have an expression like:
<TextView android:text="#{user.firstName}" .../>
This helps eliminate the boilerplate code linking data to the UI. It is another reason not to need findViewById.
Another convenience is that you can assign event listeners to your views in the XML. It appears similar to android:onClick, though it doesn't have the performance overhead of reflection.
There are other conveniences, but the major one is the data linking to the UI.
Related
I'm very new to data binding and am just beginning to have a look at the Android data binding library, and one thing in the documentation bugs me.
Using ViewModel components with the Data Binding Library allows you to move UI logic out of the layouts and into the components, which are easier to test.
Right after that, there's this - in the layout XML:
<CheckBox
android:id="#+id/rememberMeCheckBox"
android:checked="#{viewmodel.rememberMe}"
android:onCheckedChanged="#{() -> viewmodel.rememberMeChanged()}" />
Maybe it's just me, but doesn't that onCheckedChanged property contain a lot more "logic" in the layout than the old fashioned way of calling setOnCheckedChangeListener() on a "dumb" layout from an activity or fragment? It all seems a bit contradictory. Can someone explain to me how data binding moves "UI logic out of the layouts" ?
Your ViewModel now holds the logic, which you can test independent of the UI and the UI can be tested with mocked ViewModels. Its not always less code, its more structured code following a pattern.
I am actually developing an Android application on which I should display dynamic forms based on metadata contained inside JSON documents. Basically the way it works (without the details) is that a JSON document represent the structure of a form:
{
"fields": [
{
"name": "fieldA",
"type": "STRING",
"minCharacters": 10,
"maxCharacters": 100
},
{
"name": "fieldB",
"type": "INTEGER",
"min": 10,
"max": 100
},
{
"name": "fieldC",
"type": "BOOLEAN_CHECKBOX",
"defaultValue": true
}
...
],
"name": "Form A"
}
What I'm doing actually when the application receive one of those JSON documents is that it loop through each fields and parse it to the appropriate view (EditText, Checkbox, custom view, etc.), adding a tag to the view (to be able to retrieve it easily) and add the view to a LinearLayout. Here is a pseudo-code of how it is working actually:
//Add form title
linearLayout.addView(new TextView(form.name));
//Add form fields
for(Field field: form.fields) {
View view;
switch(field.type){
case STRING: view = new EditText();
...
}
view.setTag(field.id);
linearLayout.addView(view);
}
The issue with this is that with large forms (like >20 fields), it need to inflate lot of views and the UI thread suffer a lot. Another point to take into account is that a single screen may have multiple forms (one after another vertically sorted).
To avoid overloading the UI thread I thought of 2 possible solutions:
Using a RecyclerView.
Using Litho by Facebook.
But multiple questions comes to me when considering these 2 solutions:
Is it a good use case to use Litho? Or using a RecyclerView is enough?
What about the state of my views? If I use a Recycling pattern, would I be able to keep the state of each of my fields (even those off-screen) and so being able to save the form without losing data?
If I use a Recycling pattern to display one form, how would I handle multiple forms? Can we have nested RecyclerView? Forms need to be displayed one after another like inside a vertical RV but if forms themselves are RV, how should I handle this?
This is more a "good practice" question and giving the right way or one of the right way of achieving my goal than a need of a specific answer with code example, etc.
Thank's in advance for your time.
When architecting for the mobile application I would like to address the following questions:
Is it a good use case to use Litho? Or using a RecyclerView is enough?
Are the views are being recycled properly:
What does it mean to us is consider, creating 40-50 view per screen and as user moves out of the view, system should not mark all views for GC rather it should be inside some kind archived list and as we require it again we should be able to fetch from it.
Why do we need to that: GC is the costliest operation which would cause app rendering to be jitter, we try to minimize the GC to called at this point by not clearing the views
For this I would like to go with litho, justification is here as your requirement seems to have more of variable count of viewtypesreference
Conclusion: Litho +1, RecyclerView +0
What about the state of my views? If I use a Recycling pattern, would I be able to keep the state of each of my fields (even those off-screen) and so being able to save the form without losing data?
Saving EditText content in RecyclerView This is one the component but same logic should be appliced to checkbox or radiobutton as well. or as in state-maintenance for litho is here
Conclusion: Litho +1, RecyclerView +1 both has specific API's to achieve state maintenance
If I use a Recycling pattern to display one form, how would I handle multiple forms? Can we have nested RecyclerView? Forms need to be displayed one after another like inside a vertical RV but if forms themselves are RV, how should I handle this?
This has to be addressed with user experience plus technical capability: As per user behaviour IMHO,I discourage the nested vertical scroll however others were able to achieve it you can easily find on how to in SO. Optimal solution would be to have horizontal scroll within either Andriod's or litho's recycler view as here
NOTE: If you need to know implementation details, please raise it as separate question, I would be happy to help there
UPDATE #1:
The issue with this is that with large forms (like >20 fields), it need to inflate lot of views and the UI thread suffer a lot.
UI creation/layout has to be performed at the backend only adding to the view has to be done on UI thread. And litho does it in-built. However same can be achieved native recycler view as well but you have to move off the UI thread and post periodically to UI.
Ok, you have two separate problems here. One is overwork the UI thread, and the other is to keep the state of your anonymous views. About the first part:
1.-Litho could help you with this. But you have to move all your logic towards litho components instead of android widgets. Since I don't know your codebase, I don't know how hard this can be. A recyclerview would help with view recycling, but that only matters if you are well, using a list.
2.-It could, as long as you have a way to keep a representation of the widget's state that you can pass to the adapter and then back to the view (I'm assuming you generate all the windows by code and then have zero reference to them) and so. It sounds messy, and it is messy, so I won't try it.
3.-You can, but is messy. Best approach in this case would be having horizontal recyclerviews inside a vertical recyclerview. Nesting recyclerviews inside another recyclerview with the same direction creates funny problems, like "Why this cell is not scrolling". I would avoid using the recyclerview as a parent if the view does not need it.
Now, to the solutions:
A) UI Overloading: According to your pseudocode, you aren't inflating stuff. You are creating java objects which happens to be subclasses of View. That's good, because creating objects in a background thread is far easier than inflating (Parsing XML and use it as arguments to generate identical copies of a given resource by invoking constructors) stuff in a background thread. While a LinearLayout context constructor requires an UI thread to be executed, other things, like textviews, don't. So you can create the latter ones inside an asynctask and after you are done generating your whole hierarchy, execute the methods that need the UI thread and add the generated layout to the window. For the view classes that don't support being created as java objects asynchronously, you can have an XML file with just that component, like the linearLayout, and create then asynchronously with the support package asyncLayoutInflater. this solution can be implemented in any codebase and would allow you to make your UI generation completely asynchronous.
B)Keeping track of the view state: Again, I'm assuming your view hierarchy is anonymous. If so, what you need is to generate an interface you can use as a contract to invoke both state saving and state loading from a lifecycle aware component, like the activity. After creating such interface, subclass the widgets and create a subscription/event bus system in each widget that saves/loads the state from the widget every time is triggered. That way, each of of the components on the screen would be able to remember their state while remaining anonymous.
Just use the RecyclerView and create views on runtime (you are not inflating, you are instantiating)
Dynamically creating and adding views should not slow the UI thread considerably on mid-range devices. If it does, do investigate for bottlenecks elsewhere
You can perform a simple benchmark by adding/removing/setting text with lots of views dynamically inside a RecyclerView or even a LinearLayout hosted by a ScrollView, and you'll see it goes smooth
Use jetpack composer provided by android
https://developer.android.com/jetpack/compose
Most popular MVVM-frameworks allow some kind of loops for list type properties. For example Knockout has the foreach-binding. It allows you to loop through the elements of the list property and for each element the markup contained in the foreach-loop is duplicated. Inside the copy, the current element is used as the context for data-binding.
I was looking for something similar in Android, but I only saw the possibility to bind a list to a specific ui-element. But this is bad with respect to declarativeness because I need to create a UI element in code.
Is there a way to simulate something similar using the data binding features from Android? If not, is there a workaround? Or a way to extend the binding syntax?
As #tynn answered, this kind of api is not available.
To achieve the same result, you can setup an adapter for RecyclerView in XML itself. I have implemented this feature in my MVVM library. See Setup RecyclerView from XML itself.
Your XML will look as follows:
<android.support.v7.widget.RecyclerView
bind:items="#{vm.itemVms}"
bind:layout_vertical="#{true}"
bind:view_provider="#{#layout/row_item}" />
<!-- Same arguments for ViewPager-->
<android.support.v4.widget.ViewPager
bind:items="#{vm.itemVms}"
bind:view_provider="#{#layout/row_item}" />
Additionally, there is support for displaying different kinds of child views based on your ViewModel. See Using different types of child views
It's not possible as is. The android databinding library uses static XML layout and binds values to attributes. Creating static layout dynamically is not supported and I assume won't ever be. If you want to create a view hierarchy dynamically you still should use views with adapters. That's something you can do with databindings. Just create your own databinding adapter for binding a list to a RecyclerVier. You could bind to a pair of binding:iterator and binding:layout attributes for example and set the RecyclerVier.Adapter inside the databinding adapter.
If you have a layout that has a bunch of views that won't be changed during runtime, for example a TextView used as a label, is it proper to remove the ID from it, or to label it like a view that you would use?
What about layouts? If I have a bunch or table rows in a table, should each have a unique ID or should I clear the field?
It seems to me like it removes a lot of clutter if I clear the IDs if views that I won't be changing but I don't want to do that if it's bad practice.
Thanks.
You only need to define an id for an UI component if you want to reference this component later from your program code (e.g. findViewById(R.id.my_textview) ).
Because you said the views won't change during runtime you don't have to define an id for every view.
It can even help you if you don't define an id, because it keeps the auto complete function of your IDE clean. If you have a lot of layouts (which contain lots of ui elements) and you define an id for each component then you will have a nearly endless list of component ids at the end.
#matt: if your are using canvas to draw the views and set layouts then id is not necessary from my knowledge and I never used id fro and I think its not bad practise.
The page layout is like:
An header image first
a table or list with one record each line
for each record, it has an image and 2 text view
how can I design this layout and populate the data dynamically, e.g. via web download or an data file(xml/text format)?
I can't figure out how the binding works and should I use a table layout with table row or just list view with some customizations?
Actually, this is going to be a broad question because you are asking about to fetch data from web and also need to customize the listview to display data.
To fetch data from Web: You haven't mentioned whether it is SOAP or RESTful webservice? Also what you will receive in response i.e. XML or JSON? For best, i would suggest RESTful with JSON.
To customize ListView: I can give you answer for customizing ListView, for that you can create a custom adapter class by extending BaseAdapter, implement the onView() method with inflation of custom row layout file (1 ImageView + 2 TextViews), check this: Android custom adapter for ListView
The page layout is... it has an image and 2 text view.
Elaborating on jitendra's answer: Yes, a ListView seems ideal for this set up. A TableView is doable, but is really more ideal for a rows/columns structure and that does not sound like what you need.
How can I design this layout ... ?
You would use the android XML to name and style the elements. The best practice is to make a layout file for the screen containing the list itself. Make a second layout file to house a generic record. In other words, you typically design the record separately from the list and its containers. There are several great tutorial on Layouts on the Android Developer's website.
How can I... populate the data dynamically?
There are two parts to this question. First, in code you will need an Adapter of some kind to convert the Data to the actual Views. The ListView will be linked to the Adapter. The Adapter will inflate the Layout XML that you create for the Record. Different Adapters have different guidelines for customizing the Views. As mentioned by Paresh, a BaseAdapter is a great class, but you really should look at the different Adapters provided and see if they already meet your need.
Second, to get the data...1) from the web depends a lot on your web server and how it distributes data. Without more information, you are unlikely to get any solid answers. We can only make recommendations. 2) If you are going via a data file, things change considerably. You can make an Array of objects from a custom Class designed around your data. If you need Sorting, a Tree might be better. If each record has a unique key, a Set or List might do you well. Based on your question, I find myself assuming that you are unfamiliar with Android development, so I would recommend a datafile loaded into an Array with an ArrayAdapter. This will at least help you get a little more familiar with your own project and how it relates to the Android environment.
I can't figure out how the binding works
Unfortunately, it depends on your View and its Adapter. A CursorAdapter does most of its data binding in the bindView() method of the Adapter, but inflates the View in the getView() method. A BaseAdapter does this in the onView() method. If you need to get more familiar with Adapters and their capabilities, read the Android Developer Resources. They have great samples and code. Also, do a search there for "custom adapter" and you will be linked to a ton of other tutorials.
At this point, I recommend strongly, two actions for you. 1) Make a few decisions. Either you get the data from the web or a data file. If you want to do the other later, great. Get a handle on one, that way we can help you more effectively. 2) Use both Google and Android Developer Resources search functionality with the phrases that define your project. If you add the word tutorial or example, you'll be sure to find plenty to help you make those decisions so that we can tailor a solution that meets your needs.
Hope this helps!
FuzzicalLogic
According to your requirements ListView would be good, and you would need to use adapter to bind data with view.
Please read about listview and adapter to implement these.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:background="#drawable/img_list_background_repeater"
android:layout_height="wrap_content"
android:orientation="vertical" >
<TextView
android:id="#id/android:empty"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/empty_category"/>
<ListView
android:id="#id/android:list"
android:layout_width="match_parent"
android:layout_height="match_parent" />
No one will give you all java class and xml both.
You frame a xml. Then using adapter try to implent data to the list view.
If then you have any problem, provide your code. Then that can be solved.