این کد لبه بخشی از دوره آموزشی Android Kotlin Fundamentals است. اگر به ترتیب روی کدها کار کنید، بیشترین ارزش را از این دوره خواهید گرفت. همه کد لبه های دوره در صفحه فرود کد لبه های Android Kotlin Fundamentals فهرست شده اند.
مقدمه
در کدهای قبلی در این دوره، از تابع findViewById()
برای دریافت ارجاع به view ها استفاده کردید. هنگامی که برنامه شما دارای سلسله مراتب نمای پیچیده است، findViewById()
گران است و سرعت برنامه را کاهش می دهد، زیرا Android سلسله مراتب view را از ریشه شروع می کند تا زمانی که نمای مورد نظر را پیدا کند. خوشبختانه راه بهتری وجود دارد.
برای تنظیم داده ها در نماها، از منابع رشته ای استفاده کرده اید و داده های مربوط به فعالیت را تنظیم کرده اید. اگر view از داده ها مطلع باشد کارآمدتر خواهد بود. و خوشبختانه دوباره این امکان پذیر است.
در این کد لبه، شما یاد می گیرید که چگونه از data binding برای حذف نیاز به findViewById()
استفاده کنید. همچنین یاد می گیرید که چگونه از اتصال داده برای دسترسی مستقیم به داده ها از یک نما استفاده کنید.
آنچه از قبل باید بدانید
باید با:
- اکتیویتی چیست و چگونه می توان یک اکتیویتی را با طرح بندی در
onCreate()
تنظیم کرد. - ایجاد نمای متنی و تنظیم متنی که نمای متنی نمایش داده می شود.
- استفاده از
findViewById()
برای دریافت ارجاع به view. - ایجاد و ویرایش یک طرح اولیه XML برای یک نما.
چیزی که یاد خواهید گرفت
- نحوه استفاده از Data Binding Library برای حذف تماس های ناکارآمد برای
findViewById()
. - نحوه دسترسی مستقیم به داده های برنامه از XML
کاری که خواهی کرد
- یک برنامه را تغییر دهید تا به جای
findViewById()
از data binding استفاده کند و مستقیماً از فایلهای XML به دادهها دسترسی داشته باشد.
در این کد لبه، شما با برنامه AboutMe شروع میکنید و برنامه را برای استفاده از data binding تغییر میدهید. وقتی کارتان تمام شد، برنامه دقیقاً یکسان به نظر می رسد!
این چیزی است که برنامه AboutMe انجام می دهد:
- هنگامی که کاربر برنامه را باز می کند، برنامه یک نام، یک فیلد برای وارد کردن نام مستعار، یک دکمه انجام شد ، یک تصویر ستاره و متن قابل پیمایش را نشان می دهد.
- کاربر می تواند یک نام مستعار وارد کرده و روی دکمه Done ضربه بزند. فیلد و دکمه قابل ویرایش با نمای متنی جایگزین می شوند که نام مستعار وارد شده را نشان می دهد.
میتوانید از کدی که در نرمافزار قبلی ایجاد کردهاید استفاده کنید، یا میتوانید کد AboutMeDataBinding-Starter را از GitHub دانلود کنید.
کدی که در کدهای قبلی نوشتید از تابع findViewById()
برای به دست آوردن ارجاع به view ها استفاده می کند.
هر بار که از findViewById()
برای جستجوی یک نما پس از ایجاد یا ایجاد مجدد ویو استفاده می کنید، سیستم اندروید در زمان اجرا سلسله مراتب view را طی می کند تا آن را پیدا کند. وقتی برنامه شما فقط تعداد انگشت شماری بازدید دارد، این مشکلی نیست. با این حال، برنامه های تولیدی ممکن است ده ها نمایش در یک طرح داشته باشند و حتی با بهترین طراحی، نماهای تو در تو نیز وجود خواهد داشت.
به طرحبندی خطی فکر کنید که شامل نمای پیمایشی است که حاوی نمای متنی است. برای یک سلسله مراتب نمای بزرگ یا عمیق، یافتن یک نما می تواند زمان کافی را صرف کند که به طور قابل توجهی سرعت برنامه را برای کاربر کاهش دهد. ذخیره نماها در متغیرها می تواند کمک کننده باشد، اما همچنان باید یک متغیر را برای هر نما، در هر فضای نام، مقداردهی اولیه کنید. با تعداد زیادی بازدید و فعالیت های متعدد، این نیز اضافه می شود.
یک راه حل این است که یک شی ایجاد کنید که حاوی یک مرجع برای هر نما باشد. این شی که یک شی Binding
نام دارد، می تواند توسط کل برنامه شما استفاده شود. به این تکنیک، اتصال داده می گویند. هنگامی که یک شیء الزام آور برای برنامه شما ایجاد شد، می توانید بدون نیاز به پیمودن سلسله مراتب view یا جستجوی داده ها، از طریق شی binding به نماها و سایر داده ها دسترسی داشته باشید.
اتصال داده ها دارای مزایای زیر است:
- کد کوتاهتر، خواندن آسانتر و نگهداری آسانتر از کدی است که از
findByView()
استفاده میکند. - داده ها و دیدگاه ها به وضوح از هم جدا شده اند. این مزیت اتصال داده ها بعداً در این دوره اهمیت فزاینده ای پیدا می کند.
- سیستم اندروید فقط یک بار سلسله مراتب نمایش را طی می کند تا هر نما را دریافت کند، و این در هنگام راه اندازی برنامه اتفاق می افتد، نه در زمان اجرا زمانی که کاربر در حال تعامل با برنامه است.
- برای دسترسی به نماها ایمنی نوع دریافت می کنید. ( امنیت تایپ به این معنی است که کامپایلر در حین کامپایل انواع را تایید می کند و اگر بخواهید نوع اشتباهی را به یک متغیر اختصاص دهید خطا می دهد.)
در این کار، اتصال داده را تنظیم میکنید و از data binding برای جایگزینی فراخوانیهای findViewById()
با فراخوانیهای شی binding استفاده میکنید.
مرحله 1: اتصال داده را فعال کنید
برای استفاده از اتصال داده، باید اتصال داده را در فایل Gradle خود فعال کنید، زیرا به طور پیش فرض فعال نیست. این به این دلیل است که اتصال داده ها زمان کامپایل را افزایش می دهد و ممکن است بر زمان راه اندازی برنامه تأثیر بگذارد.
- اگر برنامه AboutMe را از یک Codelab قبلی ندارید، کد AboutMeDataBinding-Starter را از GitHub دریافت کنید. آن را در Android Studio باز کنید.
- فایل
build.gradle (Module: app)
را باز کنید. - در داخل بخش
android
، قبل از بستن پرانتز، یک بخشdataBinding
اضافه کنید و رویtrue
enabled
کنید.
dataBinding {
enabled = true
}
- وقتی از شما خواسته شد، پروژه را همگام سازی کنید . اگر از شما خواسته نشد، File > Sync Project with Gradle Files را انتخاب کنید.
- می توانید برنامه را اجرا کنید، اما هیچ تغییری نمی بینید.
مرحله 2: فایل طرح بندی را تغییر دهید تا با اتصال داده قابل استفاده باشد
برای کار با data binding، باید طرح XML خود را با یک تگ <layout>
بپیچید. این به این دلیل است که کلاس ریشه دیگر یک گروه view نیست، بلکه یک طرح بندی است که شامل گروه ها و view ها است. سپس شی binding می تواند در مورد طرح بندی و نماهای موجود در آن بداند.
- فایل
activity_main.xml
را باز کنید. - به تب Text بروید.
-
<layout></layout>
به عنوان بیرونی ترین تگ در اطراف<LinearLayout>
اضافه کنید.
<layout>
<LinearLayout ... >
...
</LinearLayout>
</layout>
- برای رفع تورفتگی کد ، Code > Reformat code را انتخاب کنید.
اعلانهای فضای نام برای یک طرح باید در بیرونیترین تگ باشد.
- اعلانهای فضای نام را از
<LinearLayout>
برش داده و در تگ<layout>
قرار دهید. تگ<layout>
باز شما باید مانند شکل زیر باشد و تگ<LinearLayout>
فقط باید دارای ویژگی های view باشد.
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
- برنامه خود را بسازید و اجرا کنید تا مطمئن شوید که این کار را به درستی انجام داده اید.
مرحله 3: یک شی binding در اکتیویتی اصلی ایجاد کنید
به اکتیویتی اصلی یک مرجع به شی binding اضافه کنید تا بتوانید از آن برای دسترسی به نماها استفاده کنید:
- فایل
MainActivity.kt
را باز کنید. - قبل از
onCreate()
در سطح بالا یک متغیر برای شی binding ایجاد کنید. این متغیر معمولاًbinding
نامیده می شود.
نوعbinding
، کلاسActivityMainBinding
، توسط کامپایلر به طور خاص برای این فعالیت اصلی ایجاد می شود. نام از نام فایل layout گرفته شده است، یعنیactivity_main + Binding
.
private lateinit var binding: ActivityMainBinding
- اگر Android Studio از شما خواسته است،
ActivityMainBinding
وارد کنید. اگر از شما خواسته نشد، رویActivityMainBinding
کلیک کنید وAlt+Enter
(Option+Enter
در Mac) را فشار دهید تا این کلاس از دست رفته وارد شود. (برای میانبرهای صفحه کلید بیشتر، به میانبرهای صفحه کلید مراجعه کنید.)
بیانیهimport
باید شبیه شکل زیر باشد.
import com.example.android.aboutme.databinding.ActivityMainBinding
در مرحله بعد، تابع setContentView()
فعلی را با دستورالعملی جایگزین می کنید که موارد زیر را انجام می دهد:
- شی binding را ایجاد می کند.
- از تابع
setContentView()
از کلاسDataBindingUtil
برای مرتبط کردن طرحactivity_main
باMainActivity
استفاده می کند. این تابعsetContentView()
همچنین از تنظیمات اتصال داده برای view ها مراقبت می کند.
- در
onCreate()
فراخوانیsetContentView()
با خط کد زیر جایگزین کنید.
binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
-
DataBindingUtil
را وارد کنید.
import androidx.databinding.DataBindingUtil
مرحله 4: از شی binding برای جایگزینی همه فراخوانی ها برای findViewById() استفاده کنید.
اکنون میتوانید همه فراخوانهای findViewById()
را با ارجاع به viewهایی که در شی binding هستند جایگزین کنید. هنگامی که شی binding ایجاد می شود، کامپایلر نام نماهای موجود در شی binding را از شناسه نماهای موجود در طرح تولید می کند و آنها را به camel case تبدیل می کند. بنابراین، به عنوان مثال، done_button
در شی binding doneButton
میشود، nickname_edit
تبدیل به nicknameEdit
و nickname_text
به nicknameText
تبدیل میشود.
- در
onCreate()
کدی را که ازfindViewById()
برای پیدا کردنdone_button
استفاده میکند، با کدی که به دکمه موجود در شی binding اشاره میکند، جایگزین کنید.
این کد را جایگزین کنید:findViewById<Button>(R.id.
done_button
)
با:binding.doneButton
کد تمام شده شما برای تنظیم شنونده کلیک درonCreate()
باید شبیه این باشد.
binding.doneButton.setOnClickListener {
addNickname(it)
}
- همین کار را برای همه تماسهای
findViewById()
در تابعaddNickname()
انجام دهید.
همه مواردfindViewById<
View
>(R.id.
id_view
)
را باbinding.
idView
. این کار را به روش زیر انجام دهید:
- تعاریف متغیرهای
editText
وnicknameTextView
را به همراه فراخوانی آنها برایfindViewById()
حذف کنید. این به شما خطا می دهد. - خطاها را با دریافت نماهای
nicknameText
،nicknameEdit
وdoneButton
از شیbinding
به جای متغیرهای (حذف شده) برطرف کنید. -
view.visibility
را باbinding.doneButton.visibility
جایگزین کنید. استفاده ازbinding.doneButton
به جایview
پاس شده، کد را سازگارتر می کند.
نتیجه کد زیر است:
binding.nicknameText.text = binding.nicknameEdit.text
binding.nicknameEdit.visibility = View.GONE
binding.doneButton.visibility = View.GONE
binding.nicknameText.visibility = View.VISIBLE
- هیچ تغییری در عملکرد وجود ندارد. به صورت اختیاری، اکنون میتوانید پارامتر
view
را حذف کرده و همه کاربردهایview
را برای استفاده ازbinding.doneButton
در داخل این تابع بهروزرسانی کنید.
-
nicknameText
به یکString
نیاز دارد وnicknameEdit.text
Editable
است. هنگام استفاده از data binding، لازم است کهEditable
صریحاً به یکString
تبدیل کنید.
binding.nicknameText.text = binding.nicknameEdit.text.toString()
- می توانید واردات خاکستری شده را حذف کنید.
- با استفاده از
apply{}
تابع را Kotlinize کنید.
binding.apply {
nicknameText.text = nicknameEdit.text.toString()
nicknameEdit.visibility = View.GONE
doneButton.visibility = View.GONE
nicknameText.visibility = View.VISIBLE
}
- برنامه خود را بسازید و اجرا کنید ... و باید دقیقاً مانند قبل به نظر برسد و کار کند.
میتوانید از اتصال داده استفاده کنید تا یک کلاس داده را مستقیماً در دسترس یک View قرار دهید. این تکنیک کد را ساده می کند و برای رسیدگی به موارد پیچیده تر بسیار ارزشمند است.
برای این مثال، به جای تنظیم نام و نام مستعار با استفاده از منابع رشته، یک کلاس داده برای نام و نام مستعار ایجاد می کنید. شما کلاس داده را با استفاده از data binding در دسترس view قرار می دهید.
مرحله 1: کلاس داده MyName را ایجاد کنید
- در Android Studio در فهرست
java
، فایلMyName.kt
را باز کنید. اگر این فایل را ندارید، یک فایل Kotlin جدید ایجاد کنید و آن راMyName.kt
بنامید. - یک کلاس داده برای نام و نام مستعار تعریف کنید. از رشته های خالی به عنوان مقادیر پیش فرض استفاده کنید.
data class MyName(var name: String = "", var nickname: String = "")
مرحله 2: داده ها را به طرح بندی اضافه کنید
در فایل activity_main.xml
، نام در حال حاضر در یک TextView
از یک منبع رشته تنظیم شده است. شما باید مرجع نام را با ارجاع به داده در کلاس داده جایگزین کنید.
-
activity_main.xml
در تب Text باز کنید. - در بالای طرح، بین تگ های
<layout>
و<LinearLayout>
، یک تگ<data></data>
وارد کنید. این جایی است که نمای را با داده ها وصل خواهید کرد.
<data>
</data>
در داخل تگ های داده، می توانید متغیرهای نامگذاری شده ای را که ارجاع به یک کلاس را نگه می دارند، اعلام کنید.
- داخل تگ
<data>
، یک تگ<variable>
اضافه کنید. - یک پارامتر
name
اضافه کنید تا به متغیر نام"myName"
بدهید. یک پارامترtype
اضافه کنید و نوع را روی یک نام کاملاً واجد شرایط کلاس دادهMyName
(نام بسته + نام متغیر) تنظیم کنید.
<variable
name="myName"
type="com.example.android.aboutme.MyName" />
اکنون به جای استفاده از منبع رشته برای نام، می توانید به متغیر myName
ارجاع دهید.
-
android:text="@string/name"
با کد زیر جایگزین کنید.
@={}
دستوری برای دریافت دادههایی است که در داخل بریسهای فرفری ارجاع میشوند.
myName
به متغیر myName
اشاره میکند که قبلاً تعریف کردهاید، که به کلاس داده myName
اشاره میکند و ویژگی name
از کلاس واکشی میکند.
android:text="@={myName.name}"
مرحله 3: داده ها را ایجاد کنید
اکنون یک مرجع به داده های موجود در فایل طرح بندی خود دارید. بعد، داده های واقعی را ایجاد می کنید.
- فایل
MainActivity.kt
را باز کنید. - در بالای
onCreate()
یک متغیر خصوصی ایجاد کنید که طبق قراردادmyName
نیز نامیده می شود. متغیر را به عنوان نمونه ای از کلاس دادهMyName
اختصاص دهید که در نام ارسال می شود.
private val myName: MyName = MyName("Aleks Haecky")
- در
onCreate()
مقدار متغیرmyName
در فایل layout را با مقدار متغیرmyName
که به تازگی اعلام کردید تنظیم کنید. شما نمی توانید مستقیماً به متغیر در XML دسترسی پیدا کنید. شما باید از طریق شی binding به آن دسترسی داشته باشید.
binding.myName = myName
- این ممکن است یک خطا نشان دهد، زیرا باید پس از ایجاد تغییرات، شی binding را بهروزرسانی کنید. برنامه خود را بسازید و خطا برطرف شود.
مرحله 4: از کلاس داده برای نام مستعار در TextView استفاده کنید
مرحله آخر استفاده از کلاس داده برای نام مستعار در TextView
است.
-
activity_main.xml
را باز کنید. - در نمای متنی
nickname_text
، یک ویژگیtext
اضافه کنید. مطابق شکل زیرnickname
در کلاس داده ارجاع دهید.
android:text="@={myName.nickname}"
- در
ActivityMain
، جایگزین کنید
nicknameText.text = nicknameEdit.text.toString()
با کد برای تنظیم نام مستعار در متغیرmyName
.
myName?.nickname = nicknameEdit.text.toString()
پس از تنظیم نام مستعار، میخواهید کد شما رابط کاربری را با دادههای جدید تازهسازی کند. برای انجام این کار، باید تمام عبارات binding را باطل کنید تا با داده های صحیح دوباره ایجاد شوند.
- پس از تنظیم نام مستعار
invalidateAll()
اضافه کنید تا UI با مقدار موجود در شی binding به روز شود.
binding.apply {
myName?.nickname = nicknameEdit.text.toString()
invalidateAll()
...
}
- برنامه خود را بسازید و اجرا کنید، و باید دقیقاً مانند قبل کار کند.
پروژه Android Studio: AboutMeDataBinding
مراحل استفاده از data binding برای جایگزینی تماسها با findViewById()
:
- اتصال داده را در بخش اندروید فایل
build.gradle
فعال کنید:
dataBinding { enabled = true }
- از
<layout>
به عنوان نمای ریشه در طرح XML خود استفاده کنید. - یک متغیر الزام آور را تعریف کنید:
private lateinit var binding: ActivityMainBinding
- یک شیء اتصال در
MainActivity
ایجاد کنید، به جایsetContentView
:
binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
- فراخوانی های
findViewById()
را با ارجاع به view در شی binding جایگزین کنید. به عنوان مثال:
findViewById<Button>(R.id.done_button) ⇒ binding.doneBu
tton
(در مثال، نام view ازid
view در XML ایجاد شده است.)
مراحل اتصال نماها به داده ها:
- یک کلاس داده برای داده های خود ایجاد کنید.
- یک بلوک
<data>
داخل تگ<layout>
اضافه کنید. - یک
<variable>
با نام و نوع کلاس داده تعریف کنید.
<data>
<variable
name="myName"
type="com.example.android.aboutme.MyName" />
</data>
- در
MainActivity
، یک متغیر با نمونه ای از کلاس داده ایجاد کنید. به عنوان مثال:
private val myName: MyName = MyName("Aleks Haecky")
- در شیء binding، متغیر را روی متغیری که ایجاد کردید تنظیم کنید:
binding.myName = myName
- در XML، محتوای view را روی متغیری که در بلوک
<data>
تعریف کرده اید، تنظیم کنید. از نماد نقطه برای دسترسی به داده های داخل کلاس داده استفاده کنید.
android:text="@={myName.name}"
دوره بی ادبی:
مستندات توسعه دهنده اندروید:
این بخش، تکالیف احتمالی را برای دانشآموزانی که در این آزمایشگاه کد به عنوان بخشی از دورهای که توسط یک مربی هدایت میشود، کار میکنند، فهرست میکند. این وظیفه مربی است که موارد زیر را انجام دهد:
- در صورت نیاز تکالیف را تعیین کنید.
- نحوه ارسال تکالیف را با دانش آموزان در میان بگذارید.
- تکالیف را نمره دهید.
مربیان میتوانند از این پیشنهادات به اندازهای که میخواهند استفاده کنند، و باید با خیال راحت هر تکلیف دیگری را که فکر میکنند مناسب است، محول کنند.
اگر به تنهایی از طریق این کدها کار می کنید، از این تکالیف برای آزمایش دانش خود استفاده کنید.
به این سوالات پاسخ دهید
سوال 1
چرا می خواهید فراخوانی های صریح و ضمنی را برای findViewById()
به حداقل برسانید؟
- هر بار که
findViewById()
فراخوانی می شود، از سلسله مراتب view عبور می کند. -
findViewById()
روی رشته اصلی یا UI اجرا می شود. - این تماس ها می توانند رابط کاربری را کاهش دهند.
- احتمال خرابی اپلیکیشن شما کمتر است.
سوال 2
اتصال داده ها را چگونه توصیف می کنید؟
به عنوان مثال، در اینجا مواردی وجود دارد که می توانید در مورد اتصال داده ها بگویید:
- ایده بزرگ در مورد اتصال داده، ایجاد یک شی است که در زمان کامپایل، دو قسمت از اطلاعات دور را به هم متصل/نقشهبرداری/پیوند میدهد، به طوری که در زمان اجرا نیازی به جستجوی دادهها نباشید.
- شیئی که این اتصالات را به شما نشان می دهد، شیء اتصال نامیده می شود.
- شی binding توسط کامپایلر ایجاد می شود.
سوال 3
کدام یک از موارد زیر از مزایای اتصال داده ها نیست؟
- کد کوتاهتر، خواندن آسانتر و نگهداری آسانتر است.
- داده ها و دیدگاه ها به وضوح از هم جدا شده اند.
- سیستم اندروید برای دریافت هر نما فقط یک بار سلسله مراتب را طی می کند.
- فراخوانی
findViewById()
یک خطای کامپایلر ایجاد می کند. - ایمنی را برای دسترسی به نماها تایپ کنید .
سوال 4
وظیفه تگ <layout>
چیست؟
- شما آن را به دور نمای ریشه خود در طرح بندی می پیچید.
- صحافی ها برای همه نماها در یک طرح ایجاد می شوند.
- نمای سطح بالا را در یک طرح XML مشخص می کند که از اتصال داده استفاده می کند.
- می توانید از تگ
<data>
در داخل<layout>
برای اتصال یک متغیر به یک کلاس داده استفاده کنید.
سوال 5
راه صحیح ارجاع داده های محدود در طرح XML کدام است؟
-
android:text="@={myDataClass.property}"
-
android:text="@={myDataClass}"
-
android:text="@={myDataClass.property.toString()}"
-
android:text="@={myDataClass.bound_data.property}"
درس بعدی را شروع کنید:
برای پیوند به سایر کدهای این دوره، به صفحه فرود کد لبههای کد پایه Android Kotlin Fundamentals مراجعه کنید.