Introduction
While we are working in any android app, the most important task is create a UI and set the data value to view in activities, fragments. There are various methods to set data to UI Initially, we started with findViewById() then started using ButterKnife later Kotlin Synthetics, etc.
As time increase we have go through multiple ways of finding and using views from layouts in activities, fragments. But, there was always some boilerplate code in place for setting data to a view. In this post we will learn data binding which is the quickest way to set data to view without any code inside activity or fragments.
What is data binding in android?
Android DataBinding is android jetpack architecture components which provides a way to bind the UI with data and update the UI automatically without manual intervention. DataBinding avoid to write lot of boilerplate code for update UI when new data is available.
In data binding we don’t need the id of view lets understand with an example having a property userName in ViewModel and in this property we need to set the data. To set the data to a view we usually do findViewById approach inside activity as following.
findViewById<TextView>(R.id.txt_name).apply {
text = viewModel.userName
}
But while working with data binding, we don’t need findViewById any more. All we need to do just use the basic syntax @{} in assignment expression
<TextView
android:text="@{viewmodel.userName}" />
Advantages of data binding
- You don’t need to use findViewById any more.
- It avoid run pointer exception because binding happens at compile time not run time
- It also avoid memory leak.
- It improve performance of application.
Working with data binding
Step:-1 Enable data binding in Gradle file
dataBinding {
enabled true
}
}
If you are using Kotlin DSL
dataBinding {
android.buildFeatures.dataBinding = true
}
Step 2: Converting your layout files to Data Binding layouts
To convert layout to data binding layout Firstly, wrap your layout containers with a <layout> tag. You Just have to change the root element as <layout> tag and do the as usual stuff of designing
If you want to append data in XML just add a tag<data> and assign a variable to be used. You can also set data from your activity also this is not compulsory to set data from xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:bind="http://schemas.android.com/tools">
<data>
<variable
name="viewModel"
type="com.codinglance.viewmodel.YourViewModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/grey"
android:paddingTop="46dp">
<ImageView
android:id="@+id/cloudImg"
android:layout_width="90dp"
android:layout_height="90dp"
android:contentDescription="@string/image"
app:layout_constraintEnd_toStartOf="@id/tempSection"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
bind:setViewModel="@{viewModel}"
bind:setContext="@{viewModel.mContext}"
bind:setSrc="@{viewModel.weatherIcon}" />
<androidx.cardview.widget.CardView
android:id="@+id/card_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="12dp"
android:visibility="gone"
app:cardCornerRadius="4dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/constraint_layout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:minWidth="150dp"
android:padding="16dp">
<TextView
android:id="@+id/dayLabel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Wednesday, Jan 15"
android:textSize="10sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/rowCloudIc"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="6dp"
android:contentDescription="@string/image"
android:src="@mipmap/ic_cloud_sm"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/dayLabel" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
You can also convert it form android studio
to convert from android studio
goto your layout file>Right click on parent layout>show context actions>convert data binding layout
here is image after convert layout into data binding layout
before convert into data binding
after convert into data binding
Step 3: Use data tag to set variables and link dynamic data
<data> <variable
name="viewModel"
type="com.codinglance.viewmodel.YourViewModel" />
</data>
Step 4: Binding at the Activity level
Now we have completed with the XML part but we have to do binding at the activity level and need to set the defined variables to the binding object. For this, we have to change setContentView method in activity with
val binding: ActivityMainBinding =DataBindingUtil.setContentView(this,R.layout.activity_main)
DataBindingUtil is a Utility class which is use to create ViewDataBinding from layouts. for each layout file a binding class is generated. Name of the autogenerated class file is based on the default naming convention. It will convert the layout name to Pascal case notation and add a Binding suffix to that. If your layout filename is activity_user.xml so the corresponding class generated name must be ActivityUserBinding. This class contains all the bindings info. If you click on that it will take you to the XML layout file
Binding adapter
Binding adapters are used for creating the suitable framework calls to set values. We can say that one example is setting a property value like calling the setText() method. Another example is setting a click listener like calling the setOnClickListener() method.
Let’s see an example
<variable
name="message"
type="com.codinglance.database.entity.MessageEntity" />
In this example we are going to set text to textview based on current time and other time you can change condition asa per your convenient.
@BindingAdapter("bind:setReplyTime")
fun TextView.setReplyTime(repliedAt: Long) {
if (repliedAt != 0L) {
val repliedAtStr =
SimpleDateFormat(Constants.TIME_FORMAT_HMA_v2, Locale.getDefault()).format(repliedAt)
val currentTime = Constants.getCurrentDateTime(Constants.TIME_FORMAT_HMA_v2)
val timeEquality = Constants.checkTimeEquality(repliedAtStr,currentTime)
if (timeEquality){
this.text = resources.getString(R.string.justReplied)
}else{
this.text = resources.getString(R.string.youReplied) + " " + repliedAtStr
}
this.visibility= View.VISIBLE
}else this.visibility= View.GONE
}
<TextView
android:id="@+id/youRepliedTxt"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:fontFamily="sans-serif"
android:gravity="start"
android:padding="4dp"
android:textSize="12sp"
app:layout_constraintBottom_toTopOf="@+id/replyButton"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/time"
app:layout_constraintVertical_bias=".01"
bind:setReplyTime="@{message.repliedAt}" />
Image binding
You can also bind a URL to ImageView with data binding to load the image. For this we can use @BindingAdapter annotation to object property.
Here, userImage variable is bound to android:userImage attribute. We can use any Glide or Picasso image library to Imageview
@BindingAdapter({"android:userImage"})
public static void loadImage(ImageView view, String imageUrl) {
//with glide library
Glide.with(view.getContext())
.load(imageUrl)
.into(view);
// If you consider Picasso, follow the below
// Picasso.with(view.getContext()).load(imageUrl).placeholder(R.drawable.placeholder).into(view);
}
To load the image into ImageView, add the android:userImage=”@{user.profileImage}” attribute.
<variable
name="user"
type="com.codinglance.database.entity.MessageEntity" />
<ImageView
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_marginTop="@dimen/fab_margin"
android:profileImage="@{user.userImage}" />
Click events using data binding
With data binding we not just the data, we can also bind the click events and other events on UI elements. To bind a click listener, you have to create necessary callback methods. Let’s see an example
ublic void onClicked(View view) {
Toast.makeText(getApplicationContext(), "clicked!", Toast.LENGTH_SHORT).show();
}
To bind the click event, Below android:onClick=”@{(v)->viewModel.onClicked(v)}”binds the view click to onClicked() method.
<TextView
android:id="@+id/userName"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@{message.receiverName}"
android:textColor="@color/black"
android:textSize="16sp"
android:textStyle="bold"
android:onClick="@{(v)->viewModel.onClicked(v)}
/>
Let’s see an example
1:-enable data binding into your project
buildFeatures{
dataBinding true
}
2:-MainActivity
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = DataBindingUtil.setContentView(this,R.layout.activity_main)
binding.student = getStudent()
}
private fun getStudent():Student{
return Student(1,"Codinglance","codinglance@gmail.com")
}
}
3:-XML
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="student"
type="com.codinglance.demo.Student" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:id="@+id/name_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="30sp"
android:text="@{student.name}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.415" />
<TextView
android:id="@+id/email_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="30sp"
android:text="@{student.email}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.595" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
4:-pojo class
data class Student(
var id: Int,
var name: String,
var email: String
)