Android Kotlin Fundamentals 07.2: DiffUtil और RecyclerView के साथ डेटा बाइंडिंग

यह कोडलैब, Android Kotlin Fundamentals कोर्स का हिस्सा है. अगर कोडलैब को क्रम से पूरा किया जाता है, तो आपको इस कोर्स से सबसे ज़्यादा फ़ायदा मिलेगा. कोर्स के सभी कोडलैब, Android Kotlin Fundamentals कोडलैब के लैंडिंग पेज पर दिए गए हैं.

परिचय

पिछले कोडलैब में, आपने TrackMySleepQuality ऐप्लिकेशन को अपडेट किया था, ताकि RecyclerView में नींद की क्वालिटी के बारे में डेटा दिखाया जा सके. आपने अपना पहला RecyclerView बनाते समय जो तकनीकें सीखी थीं वे ज़्यादातर RecyclerViews के लिए काफ़ी हैं. ये RecyclerViews, ज़्यादा बड़ी नहीं होती हैं और इनमें सामान्य सूचियां दिखती हैं. हालांकि, ऐसी कई तकनीकें हैं जिनकी मदद से, बड़ी सूचियों के लिए RecyclerView को ज़्यादा असरदार बनाया जा सकता है. साथ ही, जटिल सूचियों और ग्रिड के लिए अपने कोड को बनाए रखना और उसे बढ़ाना आसान बनाया जा सकता है.

इस कोडलैब में, पिछले कोडलैब में बनाए गए स्लीप-ट्रैकर ऐप्लिकेशन का इस्तेमाल किया जाता है. आपको नींद से जुड़े डेटा की सूची को अपडेट करने का ज़्यादा असरदार तरीका पता चलता है. साथ ही, आपको RecyclerView के साथ डेटा बाइंडिंग का इस्तेमाल करने का तरीका पता चलता है. (अगर आपके पास पिछले कोडलैब का ऐप्लिकेशन नहीं है, तो इस कोडलैब के लिए स्टार्टर कोड डाउनलोड किया जा सकता है.)

आपको पहले से क्या पता होना चाहिए

  • ऐक्टिविटी, फ़्रैगमेंट, और व्यू का इस्तेमाल करके बुनियादी यूज़र इंटरफ़ेस बनाना.
  • फ़्रैगमेंट के बीच नेविगेट करना और फ़्रैगमेंट के बीच डेटा पास करने के लिए, safeArgs का इस्तेमाल करना.
  • मॉडल, मॉडल फ़ैक्ट्री, ट्रांसफ़ॉर्मेशन, और LiveData और उनके ऑब्ज़र्वर देखें.
  • Room डेटाबेस बनाने, DAO बनाने, और इकाइयों को तय करने का तरीका.
  • डेटाबेस और अन्य लंबे समय तक चलने वाले टास्क के लिए, कोराउटीन का इस्तेमाल कैसे करें.
  • Adapter, ViewHolder, और आइटम लेआउट के साथ बुनियादी RecyclerView को लागू करने का तरीका.

आपको क्या सीखने को मिलेगा

  • RecyclerView की ओर से दिखाई गई सूची को असरदार तरीके से अपडेट करने के लिए, DiffUtil का इस्तेमाल कैसे करें.
  • RecyclerView के साथ डेटा बाइंडिंग का इस्तेमाल करने का तरीका.
  • डेटा को बदलने के लिए, बाइंडिंग अडैप्टर का इस्तेमाल कैसे करें.

आपको क्या करना होगा

  • इस सीरीज़ के पिछले कोडलैब में दिए गए TrackMySleepQuality ऐप्लिकेशन का इस्तेमाल करें.
  • DiffUtil का इस्तेमाल करके सूची को आसानी से अपडेट करने के लिए, SleepNightAdapter को अपडेट करें.
  • डेटा को बदलने के लिए, बाइंडिंग अडैप्टर का इस्तेमाल करके RecyclerView के लिए डेटा बाइंडिंग लागू करें.

नींद को ट्रैक करने वाले ऐप्लिकेशन में दो स्क्रीन होती हैं. इन्हें फ़्रैगमेंट के तौर पर दिखाया जाता है. इसकी जानकारी नीचे दी गई इमेज में मौजूद है.

बाईं ओर दिखाई गई पहली स्क्रीन पर, ट्रैकिंग शुरू और बंद करने के बटन हैं. स्क्रीन पर, उपयोगकर्ता की नींद से जुड़ा कुछ डेटा दिखाया गया है. मिटाएं बटन दबाने पर, ऐप्लिकेशन ने उपयोगकर्ता के लिए जो भी डेटा इकट्ठा किया है वह हमेशा के लिए मिट जाता है. दाईं ओर दिखाई गई दूसरी स्क्रीन, नींद की क्वालिटी की रेटिंग चुनने के लिए है.

इस ऐप्लिकेशन को इस तरह से डिज़ाइन किया गया है कि यह यूज़र इंटरफ़ेस (यूआई) कंट्रोलर, ViewModel, LiveData, और Room डेटाबेस का इस्तेमाल करके, नींद के डेटा को सेव कर सके.

नींद का डेटा, RecyclerView में दिखाया जाता है. इस कोडलैब में, आपको DiffUtil और RecyclerView के लिए डेटा-बाइंडिंग वाला हिस्सा बनाने का तरीका बताया गया है. इस कोडलैब के बाद, आपका ऐप्लिकेशन पहले जैसा ही दिखेगा. हालांकि, यह ज़्यादा असरदार होगा. साथ ही, इसे स्केल करना और बनाए रखना आसान होगा.

पिछले कोडलैब में इस्तेमाल किए गए SleepTracker ऐप्लिकेशन का इस्तेमाल जारी रखा जा सकता है. इसके अलावा, GitHub से RecyclerViewDiffUtilDataBinding-Starter ऐप्लिकेशन डाउनलोड किया जा सकता है.

  1. अगर ज़रूरत हो, तो GitHub से RecyclerViewDiffUtilDataBinding-Starter ऐप्लिकेशन डाउनलोड करें और Android Studio में प्रोजेक्ट खोलें.
  2. ऐप्लिकेशन चलाएं.
  3. SleepNightAdapter.kt फ़ाइल खोलें.
  4. ऐप्लिकेशन के स्ट्रक्चर के बारे में जानने के लिए, कोड की जांच करें. उपयोगकर्ता को नींद का डेटा दिखाने के लिए, अडैप्टर पैटर्न के साथ RecyclerView का इस्तेमाल करने के बारे में फिर से जानने के लिए, यहां दिया गया डायग्राम देखें.

  • उपयोगकर्ता के इनपुट के आधार पर, ऐप्लिकेशन SleepNight ऑब्जेक्ट की सूची बनाता है. हर SleepNight ऑब्जेक्ट, एक रात की नींद, उसकी अवधि, और क्वालिटी के बारे में बताता है.
  • SleepNightAdapter, SleepNight ऑब्जेक्ट की सूची को इस तरह से बदलता है कि RecyclerView इसका इस्तेमाल कर सके और इसे दिखा सके.
  • SleepNightAdapter अडैप्टर, ViewHolders बनाता है. इसमें रीसाइकलर व्यू के लिए व्यू, डेटा, और मेटा जानकारी होती है, ताकि डेटा दिखाया जा सके.
  • RecyclerView, SleepNightAdapter का इस्तेमाल करके यह तय करता है कि कितने आइटम दिखाने हैं (getItemCount()). RecyclerView, onCreateViewHolder() और onBindViewHolder() का इस्तेमाल करके, डेटा से जुड़े व्यू होल्डर को दिखाता है.

notifyDataSetChanged() तरीका असरदार नहीं है

RecyclerView को यह बताने के लिए कि सूची में मौजूद किसी आइटम में बदलाव हुआ है और उसे अपडेट करने की ज़रूरत है, मौजूदा कोड SleepNightAdapter में notifyDataSetChanged() को कॉल करता है. इसे यहां दिखाया गया है.

var data =  listOf<SleepNight>()
   set(value) {
       field = value
       notifyDataSetChanged()
   }

हालांकि, notifyDataSetChanged() से RecyclerView को पता चलता है कि पूरी सूची अमान्य हो सकती है. इस वजह से, RecyclerView सूची में मौजूद हर आइटम को फिर से बाइंड करता है और फिर से बनाता है. इसमें वे आइटम भी शामिल हैं जो स्क्रीन पर नहीं दिखते. यह बहुत ज़्यादा गै़र-ज़रूरी काम है. बड़ी या मुश्किल सूचियों के लिए, इस प्रोसेस में इतना समय लग सकता है कि उपयोगकर्ता के सूची में स्क्रोल करने पर, डिसप्ले फ़्लिकर या रुक-रुक कर चले.

इस समस्या को ठीक करने के लिए, RecyclerView को बताएं कि असल में क्या बदलाव हुआ है. इसके बाद, RecyclerView सिर्फ़ उन व्यू को अपडेट कर सकता है जो स्क्रीन पर बदले हैं.

RecyclerView में, किसी एक एलिमेंट को अपडेट करने के लिए रिच एपीआई है. RecyclerView को यह बताने के लिए कि किसी आइटम में बदलाव हुआ है, notifyItemChanged() का इस्तेमाल किया जा सकता है. साथ ही, जोड़े गए, हटाए गए या एक जगह से दूसरी जगह ले जाए गए आइटम के लिए भी इसी तरह के फ़ंक्शन का इस्तेमाल किया जा सकता है. इसे मैन्युअल तरीके से भी किया जा सकता है, लेकिन यह काम आसान नहीं होगा. इसमें काफ़ी कोड शामिल हो सकता है.

हालांकि, इससे बेहतर तरीका भी है.

DiffUtil एक असरदार टूल है और यह आपके लिए मुश्किल काम को आसान बना देता है

RecyclerView में DiffUtil नाम की एक क्लास होती है. इसका इस्तेमाल दो सूचियों के बीच अंतर का हिसाब लगाने के लिए किया जाता है. DiffUtil, पुरानी और नई सूची की तुलना करके यह पता लगाता है कि दोनों में क्या अंतर है. यह उन आइटम का पता लगाता है जिन्हें जोड़ा, हटाया या बदला गया है. इसके बाद, यह Eugene W. Myers's difference algorithm का इस्तेमाल करता है. इससे यह पता चलता है कि नई सूची बनाने के लिए, पुरानी सूची में कम से कम कितने बदलाव करने होंगे.

DiffUtil को यह पता चल जाता है कि क्या बदला है. इसके बाद, DiffUtil उस जानकारी का इस्तेमाल करके, सिर्फ़ उन आइटम को अपडेट कर सकता है जिन्हें बदला गया है, जोड़ा गया है, हटाया गया है या जिनकी जगह बदली गई है. यह पूरी सूची को फिर से बनाने से ज़्यादा असरदार है.RecyclerView

इस टास्क में, आपको SleepNightAdapter को अपग्रेड करके DiffUtil का इस्तेमाल करना है, ताकि डेटा में हुए बदलावों के लिए RecyclerView को ऑप्टिमाइज़ किया जा सके.

पहला चरण: SleepNightDiffCallback लागू करना

DiffUtil क्लास की सुविधाओं का इस्तेमाल करने के लिए, DiffUtil.ItemCallback को एक्सटेंड करें.

  1. SleepNightAdapter.kt खोलें.
  2. SleepNightAdapter के लिए पूरी क्लास डेफ़िनिशन के नीचे, SleepNightDiffCallback नाम की नई टॉप-लेवल क्लास बनाएं. यह क्लास DiffUtil.ItemCallback को बढ़ाती है. SleepNight को सामान्य पैरामीटर के तौर पर पास करें.
class SleepNightDiffCallback : DiffUtil.ItemCallback<SleepNight>() {
}
  1. कर्सर को SleepNightDiffCallback क्लास के नाम में रखें.
  2. Alt+Enter (Mac पर Option+Enter ) दबाएं और सदस्यों को लागू करें चुनें.
  3. खुलने वाले डायलॉग बॉक्स में, areItemsTheSame() और areContentsTheSame() तरीकों को चुनने के लिए, Shift दबाकर बाईं ओर क्लिक करें. इसके बाद, ठीक है पर क्लिक करें.

    इससे, नीचे दिखाए गए तरीके से, SleepNightDiffCallback में दोनों तरीकों के लिए स्टब जनरेट होते हैं. DiffUtil इन दो तरीकों का इस्तेमाल करके यह पता लगाता है कि सूची और आइटम में क्या बदलाव हुए हैं.
    override fun areItemsTheSame(oldItem: SleepNight, newItem: SleepNight): Boolean {
        TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
    }

    override fun areContentsTheSame(oldItem: SleepNight, newItem: SleepNight): Boolean {
        TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
    }
  1. areItemsTheSame() के अंदर, TODO को ऐसे कोड से बदलें जो यह जांच करता हो कि पास किए गए दो SleepNight आइटम, oldItem और newItem एक जैसे हैं या नहीं. अगर आइटम का nightId एक जैसा है, तो इसका मतलब है कि वे एक ही आइटम हैं. इसलिए, true एट्रिब्यूट की वैल्यू के तौर पर 'हां' सेट करें. अगर ऐसा नहीं है, तो false दिखाएं. DiffUtil इस टेस्ट का इस्तेमाल यह पता लगाने के लिए करता है कि कोई आइटम जोड़ा गया, हटाया गया या उसे किसी दूसरी जगह ले जाया गया.
override fun areItemsTheSame(oldItem: SleepNight, newItem: SleepNight): Boolean {
   return oldItem.nightId == newItem.nightId
}
  1. areContentsTheSame() के अंदर, देखें कि oldItem और newItem में एक ही डेटा है या नहीं. इसका मतलब है कि देखें कि वे बराबर हैं या नहीं. यह समानता जांच, सभी फ़ील्ड की जांच करेगी, क्योंकि SleepNight एक डेटा क्लास है. Data क्लास, आपके लिए equals और कुछ अन्य तरीकों को अपने-आप तय करती हैं. अगर oldItem और newItem के बीच कोई अंतर है, तो यह कोड DiffUtil को बताता है कि आइटम अपडेट कर दिया गया है.
override fun areContentsTheSame(oldItem: SleepNight, newItem: SleepNight): Boolean {
   return oldItem == newItem
}

बदलती हुई सूची को दिखाने के लिए, RecyclerView का इस्तेमाल करना एक सामान्य पैटर्न है. RecyclerView, ListAdapter नाम की एक अडैप्टर क्लास उपलब्ध कराता है. इससे आपको एक ऐसा RecyclerView अडैप्टर बनाने में मदद मिलती है जो किसी सूची पर आधारित होता है.

ListAdapter आपके लिए सूची को ट्रैक करता है और सूची अपडेट होने पर अडैप्टर को सूचना देता है.

पहला चरण: अपने अडैप्टर को ListAdapter में बदलें

  1. SleepNightAdapter.kt फ़ाइल में, SleepNightAdapter का क्लास सिग्नेचर बदलकर ListAdapter को बढ़ाएं.
  2. अगर कहा जाए, तो androidx.recyclerview.widget.ListAdapter इंपोर्ट करें.
  3. SleepNight को ListAdapter फ़ंक्शन में पहले आर्ग्युमेंट के तौर पर जोड़ें. इसे SleepNightAdapter.ViewHolder से पहले जोड़ें.
  4. कंस्ट्रक्टर में SleepNightDiffCallback() को पैरामीटर के तौर पर जोड़ें. ListAdapter इसका इस्तेमाल करके यह पता लगाएगा कि सूची में क्या बदलाव हुआ है. आपकी पूरी की गई SleepNightAdapter क्लास का सिग्नेचर, यहां दिखाए गए सिग्नेचर की तरह दिखना चाहिए.
class SleepNightAdapter : ListAdapter<SleepNight, SleepNightAdapter.ViewHolder>(SleepNightDiffCallback()) {
  1. SleepNightAdapter क्लास में, सेटर के साथ-साथ data फ़ील्ड को मिटाएं. अब आपको इसकी ज़रूरत नहीं है, क्योंकि ListAdapter आपके लिए लिस्ट को ट्रैक करता है.
  2. getItemCount() के ओवरराइड को मिटा दें, क्योंकि getItemCount() आपके लिए इस तरीके को लागू करता है.ListAdapter
  3. onBindViewHolder() में मौजूद गड़बड़ी को ठीक करने के लिए, item वैरिएबल में बदलाव करें. item पाने के लिए data का इस्तेमाल करने के बजाय, ListAdapter के ज़रिए उपलब्ध कराए गए getItem(position) तरीके को कॉल करें.
val item = getItem(position)

दूसरा चरण: सूची को अपडेट रखने के लिए submitList() का इस्तेमाल करना

आपके कोड को ListAdapter को यह बताना होगा कि बदली हुई सूची कब उपलब्ध है. ListAdapter, submitList() नाम का एक तरीका उपलब्ध कराता है. इससे ListAdapter को यह सूचना मिलती है कि सूची का नया वर्शन उपलब्ध है. इस तरीके को कॉल करने पर, ListAdapter नई सूची की तुलना पुरानी सूची से करता है. साथ ही, उन आइटम का पता लगाता है जिन्हें जोड़ा, हटाया, बदला या एक जगह से दूसरी जगह ले जाया गया है. इसके बाद, ListAdapter, RecyclerView पर दिखाए गए आइटम अपडेट करता है.

  1. SleepTrackerFragment.kt खोलें.
  2. onCreateView() में, sleepTrackerViewModel पर मौजूद ऑब्ज़र्वर में, उस गड़बड़ी का पता लगाएं जिसमें आपने जिस data वैरिएबल को मिटाया है उसका रेफ़रंस दिया गया है.
  3. adapter.data = it को adapter.submitList(it) पर कॉल करने की सुविधा से बदलें. अपडेट किया गया कोड यहां दिया गया है.

sleepTrackerViewModel.nights.observe(viewLifecycleOwner, Observer {
   it?.let {
       adapter.submitList(it)
   }
})
  1. ऐप्लिकेशन चलाएं. यह तेज़ी से चलता है. अगर आपकी सूची छोटी है, तो हो सकता है कि आपको यह बदलाव न दिखे.

इस टास्क में, डेटा बाइंडिंग सेट अप करने के लिए, पिछले कोडलैब में इस्तेमाल की गई तकनीक का इस्तेमाल किया जाता है. साथ ही, findViewById() को कॉल करने की सुविधा हटा दी जाती है.

पहला चरण: लेआउट फ़ाइल में डेटा बाइंडिंग जोड़ना

  1. टेक्स्ट टैब में जाकर, list_item_sleep_night.xml लेआउट फ़ाइल खोलें.
  2. कर्सर को ConstraintLayout टैग पर रखें और Alt+Enter (Mac पर Option+Enter ) दबाएं. इसके बाद, इंटेंट मेन्यू ("तुरंत ठीक करें" मेन्यू) खुलता है.
  3. डेटा बाइंडिंग लेआउट में बदलें को चुनें. इससे लेआउट को <layout> में रैप किया जाता है और इसके अंदर <data> टैग जोड़ा जाता है.
  4. अगर ज़रूरी हो, तो वापस सबसे ऊपर जाएं. इसके बाद, <data> टैग में, sleep नाम का वैरिएबल घोषित करें.
  5. इसके type को SleepNight, com.example.android.trackmysleepquality.database.SleepNight का पूरी तरह क्वालिफ़ाइड नाम बनाएं. आपका पूरा किया गया <data> टैग, नीचे दिए गए उदाहरण की तरह दिखना चाहिए.
   <data>
        <variable
            name="sleep"
            type="com.example.android.trackmysleepquality.database.SleepNight"/>
    </data>
  1. Binding ऑब्जेक्ट को बनाने के लिए, Build > Clean Project को चुनें. इसके बाद, Build > Rebuild Project को चुनें. (अगर आपको अब भी समस्याएं आ रही हैं, तो फ़ाइल > कैश मेमोरी मिटाएं / फिर से शुरू करें चुनें.) ListItemSleepNightBinding बाइंडिंग ऑब्जेक्ट और उससे जुड़ा कोड, प्रोजेक्ट की जनरेट की गई फ़ाइलों में जोड़ दिया जाता है.

दूसरा चरण: डेटा बाइंडिंग का इस्तेमाल करके आइटम लेआउट को बड़ा करना

  1. SleepNightAdapter.kt खोलें.
  2. ViewHolder क्लास में, from() तरीका ढूंढें.
  3. view वैरिएबल का एलान मिटाएं.

मिटाने का कोड:

val view = layoutInflater
       .inflate(R.layout.list_item_sleep_night, parent, false)
  1. view वैरिएबल की जगह पर, binding नाम का एक नया वैरिएबल तय करें. यह ListItemSleepNightBinding बाइंडिंग ऑब्जेक्ट को बढ़ाता है. इसे नीचे दिखाया गया है. बाइंडिंग ऑब्जेक्ट को ज़रूरी तौर पर इंपोर्ट करें.
val binding =
ListItemSleepNightBinding.inflate(layoutInflater, parent, false)
  1. फ़ंक्शन के आखिर में, view दिखाने के बजाय binding दिखाएं.
return ViewHolder(binding)
  1. गड़बड़ी को ठीक करने के लिए, अपने कर्सर को binding शब्द पर रखें. इंटेंशन मेन्यू खोलने के लिए, Alt+Enter (Mac पर Option+Enter) दबाएं.
  1. क्लास 'ViewHolder' के मुख्य कंस्ट्रक्टर के पैरामीटर 'itemView' के टाइप को 'ListItemSleepNightBinding' में बदलें को चुनें. इससे ViewHolder क्लास के पैरामीटर टाइप को अपडेट किया जाता है.

  1. हस्ताक्षर में हुए बदलाव को देखने के लिए, ViewHolder की क्लास डेफ़िनिशन तक ऊपर की ओर स्क्रोल करें. आपको itemView के लिए गड़बड़ी दिख रही है, क्योंकि आपने from() तरीके में itemView को binding में बदल दिया है.

    ViewHolder क्लास की परिभाषा में, itemView के किसी एक उदाहरण पर राइट क्लिक करें. इसके बाद, रीफ़ैक्टर करें > नाम बदलें को चुनें. नाम बदलकर binding कर दें.
  2. कंस्ट्रक्टर पैरामीटर binding को प्रॉपर्टी बनाने के लिए, इसके पहले val जोड़ें.
  3. पैरंट क्लास RecyclerView.ViewHolder को कॉल करते समय, पैरामीटर को binding से बदलकर binding.root करें. आपको View पास करना होगा. साथ ही, binding.root आपके आइटम लेआउट में रूट ConstraintLayout है.
  4. क्लास का एलान करने के बाद, आपका कोड यहां दिए गए कोड जैसा दिखना चाहिए.
class ViewHolder private constructor(val binding: ListItemSleepNightBinding) : RecyclerView.ViewHolder(binding.root){

आपको findViewById() पर किए गए कॉल के लिए भी गड़बड़ी दिखती है. इसे ठीक करने के लिए, यह तरीका आज़माएं.

तीसरा चरण: findViewById() को बदलें

अब findViewById() के बजाय binding ऑब्जेक्ट का इस्तेमाल करने के लिए, sleepLength, quality, और qualityImage प्रॉपर्टी अपडेट की जा सकती हैं.

  1. sleepLength, qualityString, और qualityImage के इनिशियलाइज़ेशन को बदलकर, binding ऑब्जेक्ट के व्यू का इस्तेमाल करें. इसके लिए, यहां दिया गया तरीका अपनाएं. इसके बाद, आपके कोड में कोई और गड़बड़ी नहीं दिखनी चाहिए.
val sleepLength: TextView = binding.sleepLength
val quality: TextView = binding.qualityString
val qualityImage: ImageView = binding.qualityImage

बाइंडिंग ऑब्जेक्ट का इस्तेमाल करने पर, आपको sleepLength, quality, और qualityImage प्रॉपर्टी को अब तय करने की ज़रूरत नहीं है. DataBinding लुकअप को कैश मेमोरी में सेव करेगा. इसलिए, इन प्रॉपर्टी को घोषित करने की ज़रूरत नहीं है.

  1. sleepLength, quality, और qualityImage प्रॉपर्टी के नामों पर राइट क्लिक करें. Refactor > Inline चुनें या Control+Command+N (Mac पर Option+Command+N) दबाएं.
  2. अपना ऐप्लिकेशन चलाएं. अगर आपके प्रोजेक्ट में गड़बड़ियां हैं, तो आपको उसे क्लीन और फिर से बनाना पड़ सकता है.

इस टास्क में, आपको अपने ऐप्लिकेशन को अपग्रेड करना है, ताकि वह डेटा बाइंडिंग और बाइंडिंग अडैप्टर का इस्तेमाल करके, व्यू में डेटा सेट कर सके.

पिछले कोडलैब में, आपने Transformations क्लास का इस्तेमाल करके LiveData लिया था. साथ ही, टेक्स्ट व्यू में दिखाने के लिए फ़ॉर्मैट की गई स्ट्रिंग जनरेट की थीं. हालांकि, अगर आपको अलग-अलग टाइप या जटिल टाइप बाइंड करने हैं, तो बाइंडिंग अडैप्टर दिए जा सकते हैं. इससे डेटा बाइंडिंग को उन टाइप का इस्तेमाल करने में मदद मिलती है. बाइंडिंग अडैप्टर ऐसे अडैप्टर होते हैं जो आपके डेटा को लेते हैं और उसे ऐसे फ़ॉर्मैट में बदलते हैं जिसका इस्तेमाल डेटा बाइंडिंग, व्यू को बाइंड करने के लिए कर सकती है. जैसे, टेक्स्ट या इमेज.

आपको तीन बाइंडिंग अडैप्टर लागू करने हैं. इनमें से एक अडैप्टर क्वालिटी इमेज के लिए और एक-एक अडैप्टर हर टेक्स्ट फ़ील्ड के लिए होगा. संक्षेप में, बाइंडिंग अडैप्टर को तय करने के लिए, एक ऐसा तरीका तय करें जो किसी आइटम और व्यू को लेता हो. साथ ही, उसे @BindingAdapter के साथ एनोटेट करें. तरीके के मुख्य हिस्से में, ट्रांसफ़ॉर्मेशन लागू किया जाता है. Kotlin में, डेटा पाने वाली व्यू क्लास पर एक्सटेंशन फ़ंक्शन के तौर पर बाइंडिंग अडैप्टर लिखा जा सकता है.

पहला चरण: बाइंडिंग अडैप्टर बनाना

ध्यान दें कि आपको इस चरण में कई क्लास इंपोर्ट करनी होंगी. इन्हें अलग-अलग नहीं दिखाया जाएगा.

  1. SleepNightAdapater.kt खोलें.
  2. ViewHolder क्लास में, bind() तरीका ढूंढें और याद करें कि यह तरीका क्या करता है. आपको binding.sleepLength, binding.quality, और binding.qualityImage की वैल्यू का हिसाब लगाने वाले कोड को लेना होगा. इसके बाद, इसे अडैप्टर के अंदर इस्तेमाल करना होगा. (अभी के लिए, कोड को ऐसे ही रहने दें. इसे बाद में बदला जाएगा.)
  3. sleeptracker पैकेज में, BindingUtils.kt नाम की फ़ाइल बनाएं और उसे खोलें.
  4. TextView पर setSleepDurationFormatted नाम के एक्सटेंशन फ़ंक्शन का एलान करें और उसमें SleepNight पास करें. यह फ़ंक्शन, नींद की अवधि का हिसाब लगाने और उसे फ़ॉर्मैट करने के लिए आपके अडैप्टर के तौर पर काम करेगा.
fun TextView.setSleepDurationFormatted(item: SleepNight) {}
  1. setSleepDurationFormatted के मुख्य हिस्से में, डेटा को व्यू से उसी तरह बाइंड करें जिस तरह आपने ViewHolder.bind() में किया था. convertDurationToFormatted() को कॉल करें. इसके बाद, TextView के text को फ़ॉर्मैट किए गए टेक्स्ट पर सेट करें. (यह TextView पर एक एक्सटेंशन फ़ंक्शन है. इसलिए, text प्रॉपर्टी को सीधे तौर पर ऐक्सेस किया जा सकता है.)
text = convertDurationToFormatted(item.startTimeMilli, item.endTimeMilli, context.resources)
  1. डेटा बाइंडिंग को इस बाइंडिंग अडैप्टर के बारे में बताने के लिए, फ़ंक्शन को @BindingAdapter के साथ एनोटेट करें.
  2. यह फ़ंक्शन, sleepDurationFormatted एट्रिब्यूट के लिए अडैप्टर है. इसलिए, @BindingAdapter को @BindingAdapter के लिए आर्ग्युमेंट के तौर पर पास करें.sleepDurationFormatted
@BindingAdapter("sleepDurationFormatted")
  1. दूसरा अडैप्टर, SleepNight ऑब्जेक्ट में मौजूद वैल्यू के आधार पर नींद की क्वालिटी सेट करता है. TextView पर setSleepQualityString() नाम का एक्सटेंशन फ़ंक्शन बनाएं और उसमें SleepNight पास करें.
  2. बॉडी में, डेटा को व्यू से उसी तरह बाइंड करें जिस तरह आपने ViewHolder.bind() में किया था. convertNumericQualityToString को कॉल करो और text सेट करो.
  3. फ़ंक्शन को @BindingAdapter("sleepQualityString") से एनोटेट करें.
@BindingAdapter("sleepQualityString")
fun TextView.setSleepQualityString(item: SleepNight) {
   text = convertNumericQualityToString(item.sleepQuality, context.resources)
}
  1. तीसरा बाइंडिंग अडैप्टर, इमेज व्यू पर इमेज सेट करता है. नीचे दिए गए तरीके से, ImageView पर एक्सटेंशन फ़ंक्शन बनाएं, setSleepImage को कॉल करें, और ViewHolder.bind() से कोड का इस्तेमाल करें.
@BindingAdapter("sleepImage")
fun ImageView.setSleepImage(item: SleepNight) {
   setImageResource(when (item.sleepQuality) {
       0 -> R.drawable.ic_sleep_0
       1 -> R.drawable.ic_sleep_1
       2 -> R.drawable.ic_sleep_2
       3 -> R.drawable.ic_sleep_3
       4 -> R.drawable.ic_sleep_4
       5 -> R.drawable.ic_sleep_5
       else -> R.drawable.ic_sleep_active
   })
}

दूसरा चरण: SleepNightAdapter को अपडेट करना

  1. SleepNightAdapter.kt खोलें.
  2. bind() तरीके में मौजूद हर चीज़ को मिटा दें, क्योंकि अब डेटा बाइंडिंग और नए अडैप्टर का इस्तेमाल करके, यह काम किया जा सकता है.
fun bind(item: SleepNight) {
}
  1. bind() के अंदर, item को स्लीप असाइन करें, क्योंकि आपको बाइंडिंग ऑब्जेक्ट को अपने नए SleepNight के बारे में बताना है.
binding.sleep = item
  1. उस लाइन के नीचे, binding.executePendingBindings() जोड़ें. यह कॉल एक ऑप्टिमाइज़ेशन है, जो डेटा बाइंडिंग से तुरंत सभी लंबित बाइंडिंग को लागू करने के लिए कहता है. RecyclerView में बाइंडिंग अडैप्टर का इस्तेमाल करते समय, executePendingBindings() को कॉल करना हमेशा अच्छा होता है. इससे व्यू का साइज़ तय करने में थोड़ी तेज़ी आ सकती है.
 binding.executePendingBindings()

तीसरा चरण: एक्सएमएल लेआउट में बाइंडिंग जोड़ना

  1. list_item_sleep_night.xml खोलें.
  2. ImageView में, app प्रॉपर्टी जोड़ें. इसका नाम, इमेज सेट करने वाले बाइंडिंग अडैप्टर के नाम जैसा होना चाहिए. नीचे दिखाए गए तरीके से, sleep वैरिएबल पास करें.

    यह प्रॉपर्टी, अडैप्टर के ज़रिए व्यू और बाइंडिंग ऑब्जेक्ट के बीच कनेक्शन बनाती है. जब भी sleepImage का रेफ़रंस दिया जाता है, तब अडैप्टर, SleepNight से डेटा को अडैप्ट कर लेता है.
app:sleepImage="@{sleep}"
  1. sleep_length और quality_string टेक्स्ट व्यू के लिए भी ऐसा ही करें. जब भी sleepDurationFormatted या sleepQualityString का रेफ़रंस दिया जाता है, तब अडैप्टर, SleepNight से डेटा को अडैप्ट कर लेंगे.
app:sleepDurationFormatted="@{sleep}"
app:sleepQualityString="@{sleep}"
  1. अपना ऐप्लिकेशन चलाएं. यह ठीक उसी तरह काम करता है जैसे पहले करता था. डेटा में बदलाव होने पर, बाइंडिंग अडैप्टर व्यू को फ़ॉर्मैट करने और अपडेट करने का काम करते हैं. इससे ViewHolder आसान हो जाता है. साथ ही, कोड को पहले से बेहतर स्ट्रक्चर मिलता है.

आपने पिछली कुछ कसरतों के लिए एक ही सूची दिखाई है. ऐसा इसलिए किया गया है, ताकि आपको यह दिखाया जा सके कि Adapter इंटरफ़ेस की मदद से, कोड को कई अलग-अलग तरीकों से बनाया जा सकता है. आपका कोड जितना जटिल होगा, उसे अच्छी तरह से डिज़ाइन करना उतना ही ज़रूरी होगा. प्रोडक्शन ऐप्लिकेशन में, इन पैटर्न और अन्य पैटर्न का इस्तेमाल RecyclerView के साथ किया जाता है. ये सभी पैटर्न काम करते हैं और हर पैटर्न के अपने फ़ायदे हैं. आपको कौनसा विकल्प चुनना है, यह इस बात पर निर्भर करता है कि आपको क्या बनाना है.

बधाई हो! अब तक, आपने Android पर RecyclerView का इस्तेमाल करना सीख लिया होगा.

Android Studio प्रोजेक्ट: RecyclerViewDiffUtilDataBinding.

DiffUtil:

  • RecyclerView में DiffUtil नाम की एक क्लास होती है. इसका इस्तेमाल दो सूचियों के बीच अंतर का हिसाब लगाने के लिए किया जाता है.
  • DiffUtil में ItemCallBack नाम की एक क्लास है. दो सूचियों के बीच का अंतर पता लगाने के लिए, आपको इस क्लास को बढ़ाना होगा.
  • ItemCallback क्लास में, आपको areItemsTheSame() और areContentsTheSame() तरीकों को ओवरराइड करना होगा.

ListAdapter:

  • सूची को मैनेज करने की कुछ सुविधाएं बिना किसी शुल्क के पाने के लिए, RecyclerView.Adapter के बजाय ListAdapter क्लास का इस्तेमाल करें. हालांकि, अगर ListAdapter का इस्तेमाल किया जाता है, तो आपको अन्य लेआउट के लिए अपना अडैप्टर लिखना होगा. इसलिए, यह कोडलैब आपको ऐसा करने का तरीका दिखाता है.
  • Android Studio में इंटेंशन मेन्यू खोलने के लिए, कर्सर को कोड के किसी भी आइटम पर रखें और Alt+Enter (Mac पर Option+Enter) दबाएं. यह मेन्यू, कोड को फिर से व्यवस्थित करने और तरीकों को लागू करने के लिए स्टब बनाने में खास तौर पर मददगार होता है. यह मेन्यू, कॉन्टेक्स्ट के हिसाब से काम करता है. इसलिए, सही मेन्यू पाने के लिए आपको कर्सर को सही जगह पर रखना होगा.

डेटा बाइंडिंग:

  • आइटम लेआउट में डेटा बाइंडिंग का इस्तेमाल करके, डेटा को व्यू से बाइंड करें.

बाइंडिंग अडैप्टर:

  • आपने पहले Transformations का इस्तेमाल करके, डेटा से स्ट्रिंग बनाई थीं. अगर आपको अलग-अलग या जटिल टाइप के डेटा को बाइंड करना है, तो बाइंडिंग अडैप्टर उपलब्ध कराएं. इससे डेटा बाइंडिंग को उनका इस्तेमाल करने में मदद मिलेगी.
  • बाइंडिंग अडैप्टर को तय करने के लिए, एक ऐसा तरीका तय करें जो आइटम और व्यू लेता हो. साथ ही, इस तरीके को @BindingAdapter से एनोटेट करें. Kotlin में, बाइंडिंग अडैप्टर को View पर एक्सटेंशन फ़ंक्शन के तौर पर लिखा जा सकता है. उस प्रॉपर्टी का नाम पास करें जिसे अडैप्टर अडैप्ट करता है. उदाहरण के लिए:
@BindingAdapter("sleepDurationFormatted")
  • एक्सएमएल लेआउट में, बाइंडिंग अडैप्टर के नाम वाली app प्रॉपर्टी सेट करें. डेटा के साथ वैरिएबल पास करें. उदाहरण के लिए:
.app:sleepDurationFormatted="@{sleep}"

Udacity के कोर्स:

Android डेवलपर का दस्तावेज़:

अन्य संसाधन:

इस सेक्शन में, उन छात्र-छात्राओं के लिए होमवर्क असाइनमेंट की सूची दी गई है जो किसी शिक्षक के कोर्स के हिस्से के तौर पर इस कोडलैब पर काम कर रहे हैं. शिक्षक के पास ये विकल्प होते हैं:

  • अगर ज़रूरी हो, तो होमवर्क असाइन करें.
  • छात्र-छात्राओं को बताएं कि होमवर्क असाइनमेंट कैसे सबमिट किए जाते हैं.
  • होमवर्क असाइनमेंट को ग्रेड दें.

शिक्षक इन सुझावों का इस्तेमाल अपनी ज़रूरत के हिसाब से कर सकते हैं. साथ ही, वे चाहें, तो कोई दूसरा होमवर्क भी दे सकते हैं.

अगर आपको यह कोडलैब खुद से पूरा करना है, तो अपनी जानकारी की जांच करने के लिए, इन होमवर्क असाइनमेंट का इस्तेमाल करें.

इन सवालों के जवाब दें

पहला सवाल

DiffUtil का इस्तेमाल करने के लिए, इनमें से कौनसी चीज़ें ज़रूरी हैं? लागू होने वाले सभी विकल्पों को चुनें.

ItemCallBack क्लास को बढ़ाएं.

▢ बदलें areItemsTheSame().

▢ बदलें areContentsTheSame().

▢ सामानों के बीच अंतर को ट्रैक करने के लिए, डेटा बाइंडिंग का इस्तेमाल करें.

सवाल 2

बाइंडिंग अडैप्टर के बारे में इनमें से कौनसी बातें सही हैं?

▢ बाइंडिंग अडैप्टर, @BindingAdapter एनोटेशन वाला फ़ंक्शन होता है.

▢ बाइंडिंग अडैप्टर का इस्तेमाल करके, डेटा फ़ॉर्मैटिंग को व्यू होल्डर से अलग किया जा सकता है.

▢ अगर आपको बाइंडिंग अडैप्टर का इस्तेमाल करना है, तो आपको RecyclerViewAdapter का इस्तेमाल करना होगा.

▢ जब आपको मुश्किल डेटा को बदलना हो, तब बाइंडिंग अडैप्टर एक अच्छा विकल्प है.

सवाल 3

आपको बाइंडिंग अडैप्टर के बजाय Transformations का इस्तेमाल कब करना चाहिए? लागू होने वाले सभी विकल्पों को चुनें.

▢ आपका डेटा सामान्य है.

▢ स्ट्रिंग को फ़ॉर्मैट किया जा रहा है.

▢ आपकी सूची बहुत लंबी है.

▢ आपके ViewHolder में सिर्फ़ एक व्यू शामिल है.

अगला लेसन शुरू करें: 7.3: RecyclerView के साथ GridLayout