Data Binding راهی برای اتصال رابط کاربری (UI) با داده ها فراهم می کند و اجازه می دهد که رابط کاربر بطور خودکار بروز شود. همچنین کار ما را راحت کرده و به ما اجازه میدهد که متغیرها و توابع را مستقیما در لایوت مقدار دهی کنیم و مقادیر به view های مربوطه متصل شوند ودیگه نیازی به استفاده از کد تکراری ()findViewById درسمت جاوا نیست.که اینکار حجم کد را کمتر و سرعت را بالاتر میبرد. با آموزش Data Binding در اندروید در کلاس های برنامه نویسی در مشهد همراه ما باشید.
Data Bindingیکی از اجزای معماری اندروید است که توسط اندروید پیشنهاد شده است.
فهرست مطالب آموزش Data Binding در اندروید
۱-فعال کردن Data Binding
۲-مثال اصلی آموزش Data Binding در اندروید
۳- کلاسهای Data Binding ایجاد نشده اند
Data Binding -4 در Layout
۵-اتصال Event Handling/ Click Listeners
۶-بروزرسانی رابط کاربر با استفاده از observables
۷-بروزرسانی رابط کاربر با استفاده از observablefield
۸-بارگذاری تصاویر از URL
۹-متصل کردن توابع جاوا
1- -فعال کردن Data Binding
برای شروع کار ابتدا باید این ویژگی را در پروژه فعال کرد.فایل build.gradle را باز کنید وبا این چند خط کد Data Binding را فعال کنید.پس از فعالسازی پروژه را sync (همگامسازی) کنید.
android { dataBinding { enabled = true } compileSdkVersion 27 defaultConfig { applicationId "info.androidhive.databinding" minSdkVersion 16 // .. } }
ما میخواهیم اطلاعات کاربر را از یک کلاس POJO User نمایش دهیم . در زیر کلاس POJO یک شی ء User با نام و ایمیل را ایجاد میکند. به منظور اتصال داده UI به کلاس model می توان از هر آبجکت ساده ی جاوا استفاده کرد که در اصطلاح POJO میگویند .
public class User { String name; String email; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } }
برای فعال کردن Data Binding در یک Layout باید با تگ <layout> به عنوان عنصر ریشه(root)شروع کرد .همراه با آن تگ های <data> و <variable> استفاده میشود.
در زیر ساختاری از لایوت data-binding وجود دارد.
<layout ...> <data> <variable name="..." type="..." /> </data> <LinearLayout ...> <!-- YOUR LAYOUT HERE --> </LinearLayout> </layout>
- داخل تگ <layout> کدهای معمول لایوت قرار میگیرد.
- تگ <data> در زیر تگ<layout> قرار دارد.همه متدها و متغیرهای اتصال باید در تگ <data > وارد شوند.
- داخل تگ <data> , یک متغیر با تگ <variable> تعریف میشود. تگ <variable> دو خصوصیت name و type دارد.خصوصیت name نام مستعار هست و خصوصیت type باید از کلاس مدل باشد.در اینجا مسیر ما کلاس User می باشد.
- برای اتصال یک مقدار , از دستور نگارشی{ }@ استفاده میشود. در لایوت زیر name و email کاربربا استفاده از {user.name} @و {user.email}@به TextView متصل شده اند.
<?xml version="1.0" encoding="utf-8"?> <layout xmlns:android="http://schemas.android.com/apk/res/android"> <data> <variable name="user" type="info.androidhive.databinding.User" /> </data> <LinearLayout xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:padding="@dimen/fab_margin" app:layout_behavior="@string/appbar_scrolling_view_behavior" tools:context=".MainActivity" tools:showIn="@layout/activity_main"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{user.name}" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{user.email}" /> </LinearLayout> </layout>
- وقتی که اتصال داده ها در فایل لایوت انجام شد به منوی Build رفته و پروژه را Clean Project وRebuild Project میکنیم. اینکار کلاسهای ضروری اتصال را ایجاد میکند.
- نامگذاری کلاسهای اتصال تولید شده براساس نام فایل لایوت اتصال فعال شده, میباشد .برای لایوت activity_main.xml نام کلاس اتصال تولید شده ActivityMainBinding خواهد بود(پسوند Binding در آخر اضافه میشود)
- برای متصل کردن داده در رابط کاربر (UI) باید ابتدا لایوت اتصال را با استفاده از کلاسهای اتصال تولید شده پر کنید. در زیر ابتدا لایوت ActivityMainBinding را پر کرده و بعد ()binding.setUser لایوت را به شیء User متصل میکند.
میتوانید ملاحظه کنید که از ()findViewByIdدر جایی استفاده نکردیم.
import android.databinding.DataBindingUtil; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import info.androidhive.databinding.databinding.ActivityMainBinding; public class MainActivity extends AppCompatActivity { private User user; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // setContentView(R.layout.activity_main); ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main); user = new User(); user.setName("Ravi Tamada"); user.setEmail("ravi@androidhive.info"); binding.setUser(user); } }
اگر این برنامه را اجرا کنید میتوانید جزيیات نمایش داده شده کاربر را در TextViews ببینید.
۳-کلاسهای DataBinding تولید نشده اند
نسخه رایج اندروید استودیو در اکثر اوقات موفق به تولید کلاسهای اتصال نمیشود. معمولا این مساله را میتوان با , Cleaning & Rebuilding کردن پروژه حل کرد.اگر مشکل همچنان ادامه داشت , به مسیر File ⇒ Invalidate Caches & Restart برویدو پروژه را دوباره راه اندازی کنید.اگر فایلهای لایوت شما خطایی نداشته باشند احتمالا این مشکل حل شود.
۴- DataBinding در لایوتهای *<include>
* چیدمانی که قرار است چند جا تکرار شود را در یک فایل می نویسند و در جاهای مختلف آن را include می کنند.
ما CoordinatorLayout و AppBarLayout و سایر عناصر را در مثال بالا نداریم . معمولا لایوت اصلی(main) و محتوا (content)را در دو لایوت مختلف activity_main.xml و content_main.xml جدا میکنیم .content_main در لایوت اصلی (main) با استفاده از تگ <include> وجود خواهد داشت . حالا خواهیم دید که چطور وقتی که لایوتها را include میکنیم امکان اتصال داده را فراهم کنیم .
در زیر activity_main.xml با CoordinatorLayout, AppBarLayout و FAB را داریم.
- تگ <layout> در لایوت activity_main.xml برای فعال کردن اتصال داده استفاده میشود.همچنین تگهای <data> <variable برای اتصال شی ء User استفاده میشوند.
- برای انتقال user به لایوت content_main در تگ <include> خط کد bind:user=”@{user}” را مینویسیم.بدون این خط کد شیء user در لایوت content_main در دسترس نخواهد بود.
<?xml version="1.0" encoding="utf-8"?> <layout xmlns:bind="http://schemas.android.com/apk/res/android"> <data> <variable name="user" type="info.androidhive.databinding.User" /> </data> <android.support.design.widget.CoordinatorLayout 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" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <android.support.design.widget.AppBarLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:theme="@style/AppTheme.AppBarOverlay"> <android.support.v7.widget.Toolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:background="?attr/colorPrimary" app:popupTheme="@style/AppTheme.PopupOverlay" /> </android.support.design.widget.AppBarLayout> <include android:id="@+id/content" layout="@layout/content_main" bind:user="@{user}" /> <android.support.design.widget.FloatingActionButton android:id="@+id/fab" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="bottom|end" android:layout_margin="@dimen/fab_margin" app:srcCompat="@android:drawable/ic_dialog_email" /> </android.support.design.widget.CoordinatorLayout> </layout>
- content_main.xml تگ <layout> را برای فعال کردن اتصال داده دوباره include میکند. تگهای <layout>, <data> , <variable> برای هردو parent و لایوتهای include شده ضروری هستند.
- ویژگیهای android:text=”@{user.name}” , android:text=”@{user.email}” برای نمایش داده ها در TextViewها استفاده میشوند.
<?xml version="1.0" encoding="utf-8"?> <layout xmlns:android="http://schemas.android.com/apk/res/android"> <data> <variable name="user" type="info.androidhive.databinding.User" /> </data> <LinearLayout xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:padding="@dimen/fab_margin" app:layout_behavior="@string/appbar_scrolling_view_behavior" tools:context=".MainActivity" tools:showIn="@layout/activity_main"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{user.name}" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{user.email}" /> </LinearLayout> </layout>
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main); setSupportActionBar(binding.toolbar); User user = new User(); user.setName("Ravi Tamada"); user.setEmail("ravi@androidhive.info"); binding.setUser(user); } }
اگر برنامه را اجرا کنید میتوانید داده های نمایش داده شده را ببینید.
۵–اتصال Event Handling/ Click Listeners
نه تنها داده ها ما میتوانیم click و سایر رویدادها را روی عناصر UI متصل کنیم.
برای اتصال رویداد کلیک ما احتیاج داریم که یک کلاس با متدهای مورد نیاز callback ایجاد کنیم.
در زیر ما یک کلاس داریم که رویداد کلیک FAB* را مدیریت میکند.
* دکمه شناور FAB یا همان Floating Action Button کار این دکمه این هست ، که همیشه دم دسته یعنی حتی اگه یک لیست طولانی در لایوت داشته باشیم و اسکرول کنیم دکمه همیشه ثابت سرجای خودش هست.
public class MyClickHandlers { public void onFabClicked(View view) { Toast.makeText(getApplicationContext(), "FAB clicked!", Toast.LENGTH_SHORT).show(); } }
برای اتصال به این رویداد ما دوباره از تگ <variable>استفاده میکنیم با مسیری برای هندلر کردن کلاس.
در پایین این خط کد android:onClick=”@{handlers::onFabClicked}” کلیک FAB را به متد ()onFabClickedمتصل میکند.
<layout xmlns:bind="http://schemas.android.com/apk/res/android"> <data> <variable name="handlers" type="info.androidhive.databinding.MainActivity.MyClickHandlers" /> </data> <android.support.design.widget.CoordinatorLayout ...> <android.support.design.widget.FloatingActionButton ... android:onClick="@{handlers::onFabClicked}" /> </android.support.design.widget.CoordinatorLayout> </layout>
- برای اختصاص دادن رویداد فشار طولانی (long press) ,متد باید نوع Boolean را بجای فضای خالی (void) برگرداند .
()public boolean onButtonLongPressed
- همچنین میتوانید پارامترهارا در زمان اتصال انتقال بدهید.
public void onButtonClickWithParam(View view, User user) .اتصال شیء, user رااز لایوت UI دریافت می کند.در لایوت پارامترها میتوانند منتقل شوند با استفاده از :
android:onClick=”@{(v) -> handlers.onButtonClickWithParam(v, user)}”
- برای اتصال رویدادها ,binding.setHandlers(handlers) از اکتیویتی فراخوانده میشود.
package info.androidhive.databinding; import android.content.Context; import android.databinding.DataBindingUtil; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.view.View; import android.widget.Toast; import info.androidhive.databinding.databinding.ActivityMainBinding; public class MainActivity extends AppCompatActivity { private User user; private MyClickHandlers handlers; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main); setSupportActionBar(binding.toolbar); user = new User(); user.setName("Ravi Tamada"); user.setEmail("ravi@androidhive.info"); binding.setUser(user); handlers = new MyClickHandlers(this); binding.content.setHandlers(handlers); } public class MyClickHandlers { Context context; public MyClickHandlers(Context context) { this.context = context; } public void onFabClicked(View view) { Toast.makeText(getApplicationContext(), "FAB clicked!", Toast.LENGTH_SHORT).show(); } public void onButtonClick(View view) { Toast.makeText(getApplicationContext(), "Button clicked!", Toast.LENGTH_SHORT).show(); } public void onButtonClickWithParam(View view, User user) { Toast.makeText(getApplicationContext(), "Button clicked! Name: " + user.name, Toast.LENGTH_SHORT).show(); } public boolean onButtonLongPressed(View view) { Toast.makeText(getApplicationContext(), "Button long pressed!", Toast.LENGTH_SHORT).show(); return false; } } }
<?xml version="1.0" encoding="utf-8"?> <layout xmlns:bind="http://schemas.android.com/apk/res/android"> <data> <variable name="user" type="info.androidhive.databinding.User" /> <variable name="handlers" type="info.androidhive.databinding.MainActivity.MyClickHandlers" /> </data> <android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" ...> <android.support.design.widget.AppBarLayout ...> </android.support.design.widget.AppBarLayout> <include android:id="@+id/content" layout="@layout/content_main" bind:user="@{user}" /> <android.support.design.widget.FloatingActionButton android:id="@+id/fab" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="bottom|end" android:layout_margin="@dimen/fab_margin" android:onClick="@{handlers::onFabClicked}" app:srcCompat="@android:drawable/ic_dialog_email" /> </android.support.design.widget.CoordinatorLayout> </layout>
<?xml version="1.0" encoding="utf-8"?> <layout xmlns:android="http://schemas.android.com/apk/res/android"> <data> <variable name="user" type="info.androidhive.databinding.User" /> <variable name="handlers" type="info.androidhive.databinding.MainActivity.MyClickHandlers" /> </data> <LinearLayout xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:padding="@dimen/fab_margin" app:layout_behavior="@string/appbar_scrolling_view_behavior" tools:context=".MainActivity" tools:showIn="@layout/activity_main"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{user.name}" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{user.email}" /> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:onClick="@{handlers::onButtonClick}" android:text="CLICK" /> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:onClick="@{(v) -> handlers.onButtonClickWithParam(v, user)}" android:text="CLICK WITH PARAM" /> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="LONG PRESS" android:onLongClick="@{handlers::onButtonLongPressed}" /> </LinearLayout> </layout>
۶-بروز رسانی UI با استفاده از Observables
بدون آنکه متد setter() بطور واضح فراخوانی شود Observables راهی را برای همگامسازی خودکار UI با داده فراهم میکند .وقتی مقداری از یک ویژگی (property) در یک شیء تغییر کند , UIبروز خواهد شد.برای ساختن شیء observable کلاس را گسترش میدهیم (extend) از BaseObservable.
- برای ساختن یک ویژگی observable از علامت Bindable @روی متد() getter استفاده میکنیم.
- وقتی که داده ها تغییر میکنند در متد() setter برای بروزکردن رابط کاربر, notifyPropertyChanged(BR.property) فراخوانی میشود
- وقتی که Data Binding فعال باشد کلاس BR بطور خودکار تولید خواهد شد.
در پایین کلاس اصلاح شده User گسترش یافته (extend ) از BaseObservable.شما میتوانید متوجه شوید که در اینجاnotifyPropertyChanged بعد از نسبت دادن مقادیر جدید فراخوانی شده.
package info.androidhive.databinding; import android.databinding.BaseObservable; import android.databinding.Bindable; public class User extends BaseObservable { String name; String email; @Bindable public String getName() { return name; } public void setName(String name) { this.name = name; notifyPropertyChanged(BR.name); } @Bindable public String getEmail() { return email; } public void setEmail(String email) { this.email = email; notifyPropertyChanged(BR.email); } }
برای تست این , ما اطلاعات کاربر را درon FAB click تغییر دادیم .شما میتوانید ببینید که UI در on FAB click به درستی بروز شد.
public class MyClickHandlers { Context context; public MyClickHandlers(Context context) { this.context = context; } public void onFabClicked(View view) { user.setName("Ravi"); user.setEmail("ravi8x@gmail.com"); } }
۷-بروزرسانی UI با استفاده از ObservableFields
اگر کلاس شیء شما دارای ویژگیهای کمتری برای بروزرسانی باشد یا اگر شما نمیخواهید که هر فیلد را در شیء مشاهده کنید.میتوانید از ObservableFields برای بروزرسانی رابط کاربر استفاده کنید .شما میتوانید متغیر(variable) را به عنوان ObservableFields اعلام کنید و زمانیکه داده های جدید تنظیم(set) شود رابط کاربر بروز رسانی خواهد شد.
همان کلاس User را میتوان بصورت زیر با استفاده از ObservableFields اصلاح کرد.
package info.androidhive.databinding; import android.databinding.ObservableField; public class User { public static ObservableField<String> name = new ObservableField<>(); public static ObservableField<String> email = new ObservableField<>(); public ObservableField<String> getName() { return name; } public ObservableField<String> getEmail() { return email; } }
برای بروزرسانی مقادیر باید بجای استفاده از متد() setter مقدار جدید را مستقیما به ویژگی تخصیص داد.
public class MyClickHandlers { Context context; public MyClickHandlers(Context context) { this.context = context; } public void onFabClicked(View view) { user.name.set("Ravi"); user.email.set("ravi8x@gmail.com"); } }
۸-بارگیری تصاویر از (Glide or Picasso)URL
میتوانید یک ImageView را برای بارگیری تصویر به یکURL متصل کنید.برای اتصال شما میتوانید از علامتBindingAdapter @برای ویژگی(property) شیء استفاده کنید.
در زیر متغیر profileImage به مشخصه android:profileImage متصل شده است .تصویر با استفاده از کتابخانه Glide یا Picasso بارگذاری خواهد شد .
package info.androidhive.databinding; import android.databinding.BaseObservable; import android.databinding.Bindable; import android.databinding.BindingAdapter; import android.widget.ImageView; import com.bumptech.glide.Glide; import com.bumptech.glide.request.RequestOptions; public class User { //.. String profileImage; public String getProfileImage() { return profileImage; } public void setProfileImage(String profileImage) { this.profileImage = profileImage; } @BindingAdapter({"android:profileImage"}) public static void loadImage(ImageView view, String imageUrl) { 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); } }
برای بارگذاری تصویر داخل ImageView مشخصه android:profileImage=”@{user.profileImage}” را اضافه کنید.
<ImageView android:layout_width="100dp" android:layout_height="100dp" android:layout_marginTop="@dimen/fab_margin" android:profileImage="@{user.profileImage}" />
دقت کنید که اجازه اینترنت را در فایل manifest اضافه کرده باشید.
<uses-permission android:name="android.permission.INTERNET" />
۹-اتصال توابع جاوا
میتوانید توابع جاوا را به عناصر UI متصل کنید .حالا شما میخواهید قبل از نمایش UI عملیاتی را روی مقادیر انجام دهید .براحتی شما میتوانید اینکار را با استفاده از تگ <import>انجام دهید.
در اینجا متدی داریم که رشته را به تمام حروف بزرگ تبدیل میکند.
public class BindingUtils { public static String capitalize(String text) { return text.toUpperCase(); } }
برای صدازدن این تابع در لایوت خود ابتدا کلاس را با استفاده از تگ <import> وارد کنید و تابع مربوط به ویژگی را فراخوانی کنید.
<?xml version="1.0" encoding="utf-8"?> <layout xmlns:android="http://schemas.android.com/apk/res/android"> <data> <import type="info.androidhive.databinding.BindingUtils" /> </data> <LinearLayout ...> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{BindingUtils.capitalize(user.name)}" /> </LinearLayout> </layout>
با انجام موارد بالا در آموزش Data Binding در اندروید حالا ما یکسری اطلاعات پایه ای در مورد Data Binding داریم.
عالی بود ممنون