Android Kotlin Fundamentals 08.3 इंटरनेट डेटा के साथ फ़िल्टर करना और ज़्यादा जानकारी वाले व्यू देखना

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

शुरुआती जानकारी

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

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

  • फ़्रैगमेंट बनाने और इस्तेमाल करने का तरीका.
  • फ़्रैगमेंट के बीच नेविगेट करने और फ़्रैगमेंट के बीच डेटा पास करने के लिए, Safe Args (Gradle प्लगिन) का इस्तेमाल करने का तरीका.
  • व्यू मॉडल, व्यू मॉडल फ़ैक्ट्री, ट्रांसफ़ॉर्मेशन, और LiveData जैसे आर्किटेक्चर कॉम्पोनेंट का इस्तेमाल कैसे करें.
  • REST वेब सेवा से JSON कोड में बदला गया डेटा कैसे वापस पाएं और Retrofit और Moshi लाइब्रेरी की मदद से उस डेटा को Kotlin ऑब्जेक्ट में कैसे पार्स करें.

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

  • लेआउट फ़ाइलों में, जटिल बाइंडिंग एक्सप्रेशन इस्तेमाल करने का तरीका.
  • क्वेरी के विकल्पों के साथ, वेब सर्विस को Retrofit अनुरोध करने का तरीका.

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

  • MarsRealEstate ऐप्लिकेशन में बदलाव करके, मंगल ग्रह पर मौजूद उन प्रॉपर्टी को डॉलर के निशान वाले आइकॉन से मार्क करो जिन्हें बेचना है. इसके बजाय, उन प्रॉपर्टी को मार्क करो जिन्हें किराए पर देना है.
  • खास जानकारी वाले पेज पर मौजूद विकल्प मेन्यू का इस्तेमाल करके, वेब सेवा का ऐसा अनुरोध बनाएं जो मंगल ग्रह की प्रॉपर्टी को टाइप के हिसाब से फ़िल्टर करता हो.
  • Mars प्रॉपर्टी के लिए ज़्यादा जानकारी वाला फ़्रैगमेंट बनाएं. साथ ही, उस फ़्रैगमेंट को नेविगेशन के साथ खास जानकारी वाली ग्रिड से जोड़ें. इसके बाद, प्रॉपर्टी का डेटा उस फ़्रैगमेंट में पास करें.

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

ऐप्लिकेशन के इस वर्शन में, प्रॉपर्टी के टाइप (किराया बनाम खरीदना) के साथ काम किया जाता है. साथ ही, बिक्री के लिए उपलब्ध प्रॉपर्टी को मार्क करने के लिए, ग्रिड लेआउट में एक आइकॉन जोड़ा जाता है:

आपने ऐप्लिकेशन के विकल्प मेन्यू में बदलाव किया है, ताकि ग्रिड को फ़िल्टर करके सिर्फ़ किराये या बिक्री के लिए उपलब्ध प्रॉपर्टी दिखाई जा सकें:

आखिर में, किसी प्रॉपर्टी के लिए ज़्यादा जानकारी वाला व्यू बनाया जाता है. इसके बाद, नेविगेशन की मदद से, खास जानकारी वाली ग्रिड पर मौजूद आइकॉन को ज़्यादा जानकारी वाले फ़्रैगमेंट से कनेक्ट किया जाता है:

अब तक, आपने Mars प्रॉपर्टी के डेटा का सिर्फ़ वह हिस्सा इस्तेमाल किया है जो प्रॉपर्टी की इमेज का यूआरएल है. हालांकि, MarsProperty क्लास में तय की गई प्रॉपर्टी के डेटा में आईडी, कीमत, और टाइप (किराया या बिक्री के लिए) भी शामिल होता है. आपको याद दिलाने के लिए, यहां वेब सेवा से मिले JSON डेटा का स्निपेट दिया गया है:

{
   "price":8000000,
   "id":"424908",
   "type":"rent",
   "img_src": "http://mars.jpl.nasa.gov/msl-raw-images/msss/01000/mcam/1000ML0044631290305226E03_DXXX.jpg"
},

इस टास्क में, आपको Mars प्रॉपर्टी टाइप का इस्तेमाल करके, बिक्री के लिए उपलब्ध प्रॉपर्टी के खास जानकारी वाले पेज पर डॉलर के निशान वाली इमेज जोड़नी है.

पहला चरण: MarsProperty को अपडेट करके, उसमें टाइप शामिल करना

MarsProperty क्लास, वेब सेवा से मिली हर प्रॉपर्टी के लिए डेटा स्ट्रक्चर तय करती है. पिछले कोडलैब में, आपने Moshi लाइब्रेरी का इस्तेमाल किया था. इससे, मार्स वेब सेवा से मिले रॉ JSON रिस्पॉन्स को अलग-अलग MarsProperty डेटा ऑब्जेक्ट में पार्स किया गया था.

इस चरण में, MarsProperty क्लास में कुछ लॉजिक जोड़ा जाता है. इससे यह पता चलता है कि कोई प्रॉपर्टी किराए पर दी जाती है या नहीं. इसका मतलब है कि टाइप, स्ट्रिंग "rent" या "buy" है या नहीं. इस लॉजिक का इस्तेमाल एक से ज़्यादा जगहों पर किया जाएगा. इसलिए, इसे डेटा क्लास में रखना बेहतर है, न कि इसे दोहराना.

  1. पिछले कोडलैब से MarsRealEstate ऐप्लिकेशन खोलें. (अगर आपके पास यह ऐप्लिकेशन नहीं है, तो MarsRealEstateGrid डाउनलोड करें.)
  2. network/MarsProperty.kt खोलें. MarsProperty क्लास डेफ़िनिशन में एक बॉडी जोड़ें. साथ ही, isRental के लिए एक कस्टम गेटर जोड़ें, जो ऑब्जेक्ट के "rent" टाइप का होने पर true दिखाता है.
data class MarsProperty(
       val id: String,
       @Json(name = "img_src") val imgSrcUrl: String,
       val type: String,
       val price: Double)  {
   val isRental
       get() = type == "rent"
}

दूसरा चरण: ग्रिड आइटम का लेआउट अपडेट करना

अब आपको इमेज की ग्रिड के लिए आइटम लेआउट अपडेट करना है, ताकि डॉलर का निशान सिर्फ़ उन प्रॉपर्टी इमेज पर दिखे जो बिक्री के लिए हैं:

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

  1. res/layout/grid_view_item.xml खोलें. यह RecyclerView के लिए, ग्रिड लेआउट में मौजूद हर सेल की लेआउट फ़ाइल है. फ़िलहाल, फ़ाइल में प्रॉपर्टी की इमेज के लिए सिर्फ़ <ImageView> एलिमेंट मौजूद है.
  2. <data> एलिमेंट में, View क्लास के लिए <import> एलिमेंट जोड़ें. लेआउट फ़ाइल में डेटा बाइंडिंग एक्सप्रेशन के अंदर किसी क्लास के कॉम्पोनेंट का इस्तेमाल करने के लिए, इंपोर्ट का इस्तेमाल किया जाता है. इस मामले में, आपको View.GONE और View.VISIBLE कॉन्स्टेंट का इस्तेमाल करना है. इसलिए, आपको View क्लास को ऐक्सेस करने की ज़रूरत होगी.
<import type="android.view.View"/>
  1. पूरी इमेज व्यू को FrameLayout से घेरें, ताकि डॉलर-साइन ड्रॉएबल को प्रॉपर्टी की इमेज के ऊपर स्टैक किया जा सके.
<FrameLayout
   android:layout_width="match_parent"
   android:layout_height="170dp">
             <ImageView 
                    android:id="@+id/mars_image"
            ...
</FrameLayout>
  1. नए पैरंट FrameLayout को भरने के लिए, ImageView एट्रिब्यूट के लिए android:layout_height एट्रिब्यूट को match_parent में बदलें.
android:layout_height="match_parent"
  1. FrameLayout के अंदर, पहले <ImageView> एलिमेंट के ठीक नीचे दूसरा <ImageView> एलिमेंट जोड़ें. यहां दी गई परिभाषा का इस्तेमाल करें. यह इमेज, ग्रिड आइटम के निचले दाएं कोने में, मंगल ग्रह की इमेज के ऊपर दिखती है. साथ ही, डॉलर के निशान वाले आइकॉन के लिए, res/drawable/ic_for_sale_outline.xml में तय किए गए ड्रॉएबल का इस्तेमाल करती है.
<ImageView
   android:id="@+id/mars_property_type"
   android:layout_width="wrap_content"
   android:layout_height="45dp"
   android:layout_gravity="bottom|end"
   android:adjustViewBounds="true"
   android:padding="5dp"
   android:scaleType="fitCenter"
   android:src="@drawable/ic_for_sale_outline"
   tools:src="@drawable/ic_for_sale_outline"/>
  1. mars_property_type इमेज व्यू में android:visibility एट्रिब्यूट जोड़ें. प्रॉपर्टी टाइप की जांच करने के लिए, बाइंडिंग एक्सप्रेशन का इस्तेमाल करें. साथ ही, किराये पर लेने के लिए View.GONE या खरीदने के लिए View.VISIBLE के तौर पर दृश्यता असाइन करें.
 android:visibility="@{property.rental ? View.GONE : View.VISIBLE}"

अब तक आपने सिर्फ़ उन लेआउट में बाइंडिंग एक्सप्रेशन देखे हैं जिनमें <data> एलिमेंट में तय किए गए अलग-अलग वैरिएबल का इस्तेमाल किया जाता है. बाइंडिंग एक्सप्रेशन बहुत असरदार होते हैं. इनकी मदद से, टेस्ट और गणित के हिसाब-किताब जैसे काम, पूरी तरह से अपने एक्सएमएल लेआउट में किए जा सकते हैं. इस मामले में, टेस्ट करने के लिए टर्नरी ऑपरेटर (?:) का इस्तेमाल किया जाता है. जैसे, क्या यह ऑब्जेक्ट किराये पर दिया जाता है? 'सही' के लिए एक नतीजा (View.GONE के साथ डॉलर-साइन आइकॉन छिपाएं) और 'गलत' के लिए दूसरा नतीजा (View.VISIBLE के साथ वह आइकॉन दिखाएं) दिया जाता है.

नई पूरी grid_view_item.xml फ़ाइल यहां दी गई है:

<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>
       <import type="android.view.View"/>
       <variable
           name="property"
           type="com.example.android.marsrealestate.network.MarsProperty" />
   </data>
   <FrameLayout
       android:layout_width="match_parent"
       android:layout_height="170dp">

       <ImageView
           android:id="@+id/mars_image"
           android:layout_width="match_parent"
           android:layout_height="match_parent"
           android:scaleType="centerCrop"
           android:adjustViewBounds="true"
           android:padding="2dp"
           app:imageUrl="@{property.imgSrcUrl}"
           tools:src="@tools:sample/backgrounds/scenic"/>

       <ImageView
           android:id="@+id/mars_property_type"
           android:layout_width="wrap_content"
           android:layout_height="45dp"
           android:layout_gravity="bottom|end"
           android:adjustViewBounds="true"
           android:padding="5dp"
           android:scaleType="fitCenter"
           android:src="@drawable/ic_for_sale_outline"
           android:visibility="@{property.rental ? View.GONE : View.VISIBLE}"
           tools:src="@drawable/ic_for_sale_outline"/>
   </FrameLayout>
</layout>
  1. ऐप्लिकेशन को कंपाइल और रन करें. ध्यान दें कि किराए पर न दिए जाने वाले प्रॉडक्ट के लिए, डॉलर के निशान वाला आइकॉन मौजूद है.

फ़िलहाल, आपका ऐप्लिकेशन खास जानकारी वाली ग्रिड में Mars की सभी प्रॉपर्टी दिखाता है. अगर कोई व्यक्ति मंगल ग्रह पर किराये की प्रॉपर्टी खोज रहा है, तो उसे यह बताने वाले आइकॉन दिखेंगे कि कौनसी प्रॉपर्टी बिक्री के लिए उपलब्ध हैं. हालांकि, पेज पर अब भी स्क्रोल करने के लिए कई प्रॉपर्टी मौजूद हैं. इस टास्क में, आपको खास जानकारी वाले फ़्रैगमेंट में एक विकल्प मेन्यू जोड़ना है. इससे उपयोगकर्ता, सिर्फ़ किराए पर उपलब्ध प्रॉपर्टी, सिर्फ़ बिक्री के लिए उपलब्ध प्रॉपर्टी या सभी प्रॉपर्टी दिखा पाएगा.

इस टास्क को पूरा करने का एक तरीका यह है कि खास जानकारी वाली ग्रिड में मौजूद हर MarsProperty के टाइप की जांच करें और सिर्फ़ मैच करने वाली प्रॉपर्टी दिखाएं. हालांकि, Mars की वेब सेवा में एक क्वेरी पैरामीटर या विकल्प (filter) होता है. इसकी मदद से, सिर्फ़ rent या buy टाइप की प्रॉपर्टी पाई जा सकती हैं. इस फ़िल्टर क्वेरी का इस्तेमाल, ब्राउज़र में realestate वेब सेवा के यूआरएल के साथ इस तरह किया जा सकता है:

https://android-kotlin-fun-mars-server.appspot.com/realestate?filter=buy

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

पहला चरण: Mars API सेवा को अपडेट करना

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

  1. network/MarsApiService.kt खोलें. इंपोर्ट के ठीक नीचे, enum नाम का MarsApiFilter बनाएं. इससे उन कॉन्स्टेंट को तय किया जा सकेगा जो वेब सेवा की क्वेरी वैल्यू से मेल खाते हैं.
enum class MarsApiFilter(val value: String) {
   SHOW_RENT("rent"),
   SHOW_BUY("buy"),
   SHOW_ALL("all") }
  1. फ़िल्टर क्वेरी के लिए स्ट्रिंग इनपुट लेने के लिए, getProperties() तरीके में बदलाव करें. साथ ही, उस इनपुट को @Query("filter") के साथ एनोटेट करें. जैसा कि यहां दिखाया गया है.

    जब कहा जाए, तब retrofit2.http.Query इंपोर्ट करें.

    @Query एनोटेशन, getProperties() तरीके (और इस तरह Retrofit) को फ़िल्टर करने के विकल्प के साथ वेब सेवा का अनुरोध करने के लिए कहता है. हर बार getProperties() को कॉल करने पर, अनुरोध किए गए यूआरएल में ?filter=type वाला हिस्सा शामिल होता है. इससे वेब सेवा को उस क्वेरी से मिलते-जुलते नतीजे दिखाने का निर्देश मिलता है.
fun getProperties(@Query("filter") type: String):  

दूसरा चरण: खास जानकारी वाले व्यू मॉडल को अपडेट करना

आपने OverviewViewModel में getMarsRealEstateProperties() तरीके का इस्तेमाल करके, MarsApiService से डेटा का अनुरोध किया है. अब आपको उस अनुरोध को अपडेट करना होगा, ताकि वह फ़िल्टर आर्ग्युमेंट ले सके.

  1. overview/OverviewViewModel.kt खोलें. पिछले चरण में किए गए बदलावों की वजह से, आपको Android Studio में गड़बड़ियां दिखेंगी. getMarsRealEstateProperties() कॉल में, MarsApiFilter (फ़िल्टर की संभावित वैल्यू का enum) को पैरामीटर के तौर पर जोड़ें.

    अनुरोध किए जाने पर com.example.android.marsrealestate.network.MarsApiFilter इंपोर्ट करें.
private fun getMarsRealEstateProperties(filter: MarsApiFilter) {
  1. Retrofit सेवा में getProperties() को किए गए कॉल में बदलाव करें, ताकि उस फ़िल्टर क्वेरी को स्ट्रिंग के तौर पर पास किया जा सके.
var getPropertiesDeferred = MarsApi.retrofitService.getProperties(filter.value)
  1. init {} ब्लॉक में, getMarsRealEstateProperties() को आर्ग्युमेंट के तौर पर MarsApiFilter.SHOW_ALL पास करें, ताकि ऐप्लिकेशन के पहली बार लोड होने पर सभी प्रॉपर्टी दिखें.
init {
   getMarsRealEstateProperties(MarsApiFilter.SHOW_ALL)
}
  1. क्लास के आखिर में, एक updateFilter() मैथड जोड़ें. यह MarsApiFilter आर्ग्युमेंट लेता है और उस आर्ग्युमेंट के साथ getMarsRealEstateProperties() को कॉल करता है.
fun updateFilter(filter: MarsApiFilter) {
   getMarsRealEstateProperties(filter)
}

तीसरा चरण: फ़्रैगमेंट को विकल्प मेन्यू से कनेक्ट करना

आखिरी चरण में, ओवरफ़्लो मेन्यू को फ़्रैगमेंट से जोड़ना होता है. इससे जब उपयोगकर्ता कोई मेन्यू विकल्प चुनता है, तब व्यू मॉडल पर updateFilter() कॉल किया जा सकता है.

  1. res/menu/overflow_menu.xml खोलें. MarsRealEstate ऐप्लिकेशन में पहले से ही एक ओवरफ़्लो मेन्यू मौजूद है. इसमें तीन विकल्प उपलब्ध हैं: सभी प्रॉपर्टी दिखाना, सिर्फ़ किराए पर उपलब्ध प्रॉपर्टी दिखाना, और सिर्फ़ बिक्री के लिए उपलब्ध प्रॉपर्टी दिखाना.
<menu xmlns:android="http://schemas.android.com/apk/res/android">
   <item
       android:id="@+id/show_all_menu"
       android:title="@string/show_all" />
   <item
       android:id="@+id/show_rent_menu"
       android:title="@string/show_rent" />
   <item
       android:id="@+id/show_buy_menu"
       android:title="@string/show_buy" />
</menu>
  1. overview/OverviewFragment.kt खोलें. क्लास के आखिर में, मेन्यू आइटम चुनने के लिए onOptionsItemSelected() तरीके का इस्तेमाल करें.
override fun onOptionsItemSelected(item: MenuItem): Boolean {
} 
  1. onOptionsItemSelected() में, सही फ़िल्टर के साथ व्यू मॉडल पर updateFilter() वाले तरीके को कॉल करें. विकल्पों के बीच स्विच करने के लिए, Kotlin when {} ब्लॉक का इस्तेमाल करें. डिफ़ॉल्ट फ़िल्टर वैल्यू के लिए, MarsApiFilter.SHOW_ALL का इस्तेमाल करें. true को वापस लाओ, क्योंकि तुमने मेन्यू आइटम को हैंडल कर लिया है. अनुरोध किए जाने पर, MarsApiFilter (com.example.android.marsrealestate.network.MarsApiFilter) को इंपोर्ट करें. onOptionsItemSelected() का पूरा तरीका यहां दिया गया है.
override fun onOptionsItemSelected(item: MenuItem): Boolean {
   viewModel.updateFilter(
           when (item.itemId) {
               R.id.show_rent_menu -> MarsApiFilter.SHOW_RENT
               R.id.show_buy_menu -> MarsApiFilter.SHOW_BUY
               else -> MarsApiFilter.SHOW_ALL
           }
   )
   return true
}
  1. ऐप्लिकेशन को कंपाइल और रन करें. ऐप्लिकेशन, प्रॉपर्टी के सभी टाइप के साथ पहली खास जानकारी वाली ग्रिड लॉन्च करता है. साथ ही, बिक्री के लिए उपलब्ध प्रॉपर्टी को डॉलर के निशान से मार्क करता है.
  2. विकल्प मेन्यू से, किराया चुनें. प्रॉपर्टी फिर से लोड होती हैं और उनमें से किसी के भी साथ डॉलर का आइकॉन नहीं दिखता. (सिर्फ़ किराये पर दी जाने वाली प्रॉपर्टी दिखाई जाती हैं.) फ़िल्टर की गई प्रॉपर्टी दिखाने के लिए, डिसप्ले को रीफ़्रेश होने में कुछ समय लग सकता है.
  3. विकल्प मेन्यू से खरीदें चुनें. प्रॉपर्टी फिर से लोड होती हैं और सभी डॉलर के आइकॉन के साथ दिखती हैं. (सिर्फ़ बिक्री के लिए उपलब्ध प्रॉपर्टी दिखाई जाती हैं.)

अब आपके पास Mars की प्रॉपर्टी के आइकॉन की स्क्रोल करने वाली ग्रिड है. हालांकि, अब आपको ज़्यादा जानकारी चाहिए. इस टास्क में, किसी प्रॉपर्टी की जानकारी दिखाने के लिए, एक जानकारी वाला फ़्रैगमेंट जोड़ा जाता है. ज़्यादा जानकारी वाले फ़्रैगमेंट में, बड़ी इमेज, कीमत, और प्रॉपर्टी का टाइप दिखेगा. इससे पता चलेगा कि प्रॉपर्टी किराये पर उपलब्ध है या बिक्री के लिए.

जब उपयोगकर्ता खास जानकारी वाली ग्रिड में मौजूद किसी इमेज पर टैप करता है, तब यह फ़्रैगमेंट लॉन्च होता है. इसके लिए, आपको onClick ग्रिड आइटम में onClick लिसनर जोड़ना होगा. इसके बाद, नए फ़्रैगमेंट पर जाएं.RecyclerView इन सभी सबक में, आपने ViewModel में LiveData बदलाव करके नेविगेट किया है. आपने नेविगेशन कॉम्पोनेंट के Safe Args प्लगिन का इस्तेमाल करके, चुनी गई MarsProperty जानकारी को खास जानकारी वाले फ़्रैगमेंट से ज़्यादा जानकारी वाले फ़्रैगमेंट में पास किया है.

पहला चरण: जानकारी वाले व्यू का मॉडल बनाएं और जानकारी वाले लेआउट को अपडेट करें

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

  1. detail/DetailViewModel.kt खोलें. नेटवर्क से जुड़ी Kotlin फ़ाइलें network फ़ोल्डर में और खास जानकारी वाली फ़ाइलें overview में होती हैं. इसी तरह, detail फ़ोल्डर में, ज़्यादा जानकारी वाले व्यू से जुड़ी फ़ाइलें होती हैं. ध्यान दें कि DetailViewModel क्लास (फ़िलहाल खाली है) कंस्ट्रक्टर में marsProperty को पैरामीटर के तौर पर लेता है.
class DetailViewModel( marsProperty: MarsProperty,
                     app: Application) : AndroidViewModel(app) {
}
  1. क्लास की परिभाषा में, चुनी गई Mars प्रॉपर्टी के लिए LiveData जोड़ें, ताकि वह जानकारी ज़्यादा जानकारी वाले व्यू में दिख सके. MarsProperty को होल्ड करने के लिए, MutableLiveData बनाने के सामान्य पैटर्न का पालन करें. इसके बाद, न बदलने वाली सार्वजनिक LiveData प्रॉपर्टी को दिखाएं.

    अनुरोध किए जाने पर, androidx.lifecycle.LiveData और androidx.lifecycle.MutableLiveData इंपोर्ट करें.
private val _selectedProperty = MutableLiveData<MarsProperty>()
val selectedProperty: LiveData<MarsProperty>
   get() = _selectedProperty
  1. एक init {} ब्लॉक बनाएं और कंस्ट्रक्टर से मिले MarsProperty ऑब्जेक्ट की मदद से, चुनी गई Mars प्रॉपर्टी की वैल्यू सेट करें.
    init {
        _selectedProperty.value = marsProperty
    }
  1. res/layout/fragment_detail.xml खोलें और उसे डिज़ाइन व्यू में देखें.

    यह जानकारी वाले फ़्रैगमेंट के लिए लेआउट फ़ाइल है. इसमें बड़ी फ़ोटो के लिए ImageView, प्रॉपर्टी टाइप (किराये पर या बिक्री के लिए) के लिए TextView, और कीमत के लिए TextView शामिल है. ध्यान दें कि कंस्ट्रेंट लेआउट को ScrollView के साथ रैप किया गया है. इसलिए, अगर व्यू डिसप्ले के लिए बहुत बड़ा हो जाता है, तो यह अपने-आप स्क्रोल हो जाएगा. उदाहरण के लिए, जब उपयोगकर्ता इसे लैंडस्केप मोड में देखता है.
  2. लेआउट के टेक्स्ट टैब पर जाएं. लेआउट में सबसे ऊपर, <ScrollView> एलिमेंट से ठीक पहले, <data> एलिमेंट जोड़ें, ताकि ज़्यादा जानकारी वाले व्यू मॉडल को लेआउट से जोड़ा जा सके.
<data>
   <variable
       name="viewModel"
       type="com.example.android.marsrealestate.detail.DetailViewModel" />
</data>
  1. app:imageUrl एट्रिब्यूट को ImageView एलिमेंट में जोड़ें. इसे व्यू मॉडल की चुनी गई प्रॉपर्टी से imgSrcUrl पर सेट करें.

    Glide का इस्तेमाल करके इमेज लोड करने वाला बाइंडिंग अडैप्टर, यहां भी अपने-आप इस्तेमाल हो जाएगा. ऐसा इसलिए, क्योंकि यह अडैप्टर सभी app:imageUrl एट्रिब्यूट पर नज़र रखता है.
 app:imageUrl="@{viewModel.selectedProperty.imgSrcUrl}"

दूसरा चरण: ओवरव्यू व्यू मॉडल में नेविगेशन तय करना

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

  1. overview/OverviewViewModel.kt खोलें. _navigateToSelectedProperty MutableLiveData प्रॉपर्टी जोड़ें और उसे न बदलने वाले LiveData के साथ दिखाएं.

    जब यह LiveData बदलकर गैर-शून्य हो जाता है, तब नेविगेशन ट्रिगर होता है. (जल्द ही, इस वैरिएबल को मॉनिटर करने और नेविगेशन को ट्रिगर करने के लिए कोड जोड़ा जाएगा.)
private val _navigateToSelectedProperty = MutableLiveData<MarsProperty>()
val navigateToSelectedProperty: LiveData<MarsProperty>
   get() = _navigateToSelectedProperty
  1. क्लास के आखिर में, एक displayPropertyDetails() तरीका जोड़ें. यह तरीका, _navigateToSelectedProperty को चुनी गई Mars प्रॉपर्टी पर सेट करता है.
fun displayPropertyDetails(marsProperty: MarsProperty) {
   _navigateToSelectedProperty.value = marsProperty
}
  1. displayPropertyDetailsComplete() तरीका जोड़ें, जो _navigateToSelectedProperty की वैल्यू को शून्य कर देता है. नेविगेशन की स्थिति को 'पूरा हुआ' के तौर पर मार्क करने के लिए, आपको इसकी ज़रूरत होगी. साथ ही, इससे यह पक्का किया जा सकेगा कि उपयोगकर्ता के जानकारी वाले पेज से वापस आने पर, नेविगेशन फिर से ट्रिगर न हो.
fun displayPropertyDetailsComplete() {
   _navigateToSelectedProperty.value = null
}

तीसरा चरण: ग्रिड अडैप्टर और फ़्रैगमेंट में क्लिक लिसनर सेट अप करना

  1. overview/PhotoGridAdapter.kt खोलें. क्लास के आखिर में, एक कस्टम OnClickListener क्लास बनाएं. यह क्लास, marsProperty पैरामीटर के साथ लैम्डा लेती है. क्लास में, onClick() फ़ंक्शन को lambda पैरामीटर पर सेट करें.
class OnClickListener(val clickListener: (marsProperty:MarsProperty) -> Unit) {
     fun onClick(marsProperty:MarsProperty) = clickListener(marsProperty)
}
  1. ऊपर की ओर स्क्रोल करके, PhotoGridAdapter के लिए क्लास डेफ़िनिशन पर जाएं. इसके बाद, कंस्ट्रक्टर में एक प्राइवेट OnClickListener प्रॉपर्टी जोड़ें.
class PhotoGridAdapter( private val onClickListener: OnClickListener ) :
       ListAdapter<MarsProperty,              
           PhotoGridAdapter.MarsPropertyViewHolder>(DiffCallback) {
  1. onBindviewHolder() तरीके में, ग्रिड आइटम में onClickListener जोड़कर फ़ोटो को क्लिक करने लायक बनाएं. getItem() and bind() को कॉल करने के बीच में, क्लिक लिसनर को तय करें.
override fun onBindViewHolder(holder: MarsPropertyViewHolder, position: Int) {
   val marsProperty = getItem(position)
   holder.itemView.setOnClickListener {
       onClickListener.onClick(marsProperty)
   }
   holder.bind(marsProperty)
}
  1. overview/OverviewFragment.kt खोलें. onCreateView() तरीके में, binding.photosGrid.adapter प्रॉपर्टी को शुरू करने वाली लाइन को यहां दिखाई गई लाइन से बदलें.

    यह कोड, PhotoGridAdapter.onClickListener ऑब्जेक्ट को PhotoGridAdapter कंस्ट्रक्टर में जोड़ता है. साथ ही, पास किए गए MarsProperty ऑब्जेक्ट के साथ viewModel.displayPropertyDetails() को कॉल करता है. इससे नेविगेशन के लिए व्यू मॉडल में LiveData ट्रिगर होता है.
binding.photosGrid.adapter = PhotoGridAdapter(PhotoGridAdapter.OnClickListener {
   viewModel.displayPropertyDetails(it)
})

चौथा चरण: नेविगेशन ग्राफ़ में बदलाव करना और MarsProperty को पार्सल करने लायक बनाना

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

फ़िलहाल, आपके पास टैप को हैंडल करने के लिए PhotoGridAdapter से क्लिक लिसनर है. साथ ही, व्यू मॉडल से नेविगेशन को ट्रिगर करने का तरीका है. हालांकि, आपके पास अब तक ऐसा कोई MarsProperty ऑब्जेक्ट नहीं है जिसे ज़्यादा जानकारी वाले फ़्रैगमेंट में पास किया जा रहा हो. इसके लिए, नेविगेशन कॉम्पोनेंट से Safe Args का इस्तेमाल किया जाता है.

  1. res/navigation/nav_graph.xml खोलें. नेविगेशन ग्राफ़ के लिए एक्सएमएल कोड देखने के लिए, टेक्स्ट टैब पर क्लिक करें.
  2. ज़्यादा जानकारी वाले फ़्रैगमेंट के लिए, <fragment> एलिमेंट में नीचे दिखाया गया <argument> एलिमेंट जोड़ें. इस आर्ग्युमेंट को selectedProperty कहा जाता है और इसका टाइप MarsProperty है.
<argument
   android:name="selectedProperty"
   app:argType="com.example.android.marsrealestate.network.MarsProperty"
   />
  1. ऐप्लिकेशन को कंपाइल करें. नेविगेशन में गड़बड़ी दिख रही है, क्योंकि MarsProperty पार्सल करने लायक नहीं है. Parcelable इंटरफ़ेस की मदद से ऑब्जेक्ट को क्रम से लगाया जा सकता है, ताकि ऑब्जेक्ट का डेटा फ़्रैगमेंट या ऐक्टिविटी के बीच पास किया जा सके. इस मामले में, MarsProperty ऑब्जेक्ट में मौजूद डेटा को Safe Args के ज़रिए, जानकारी वाले फ़्रैगमेंट में पास करने के लिए, MarsProperty को Parcelable इंटरफ़ेस लागू करना होगा. अच्छी बात यह है कि Kotlin, उस इंटरफ़ेस को लागू करने के लिए एक आसान शॉर्टकट उपलब्ध कराता है.
  2. network/MarsProperty.kt खोलें. क्लास डेफ़िनिशन में @Parcelize एनोटेशन जोड़ें.

    जब अनुरोध किया जाए, तब kotlinx.android.parcel.Parcelize इंपोर्ट करें.

    @Parcelize एनोटेशन, Kotlin Android एक्सटेंशन का इस्तेमाल करता है. इससे इस क्लास के लिए, Parcelable इंटरफ़ेस में मौजूद तरीकों को अपने-आप लागू किया जा सकता है. आपको कुछ और करने की ज़रूरत नहीं है!
@Parcelize
data class MarsProperty (
  1. Parcelable को बढ़ाने के लिए, MarsProperty की क्लास की परिभाषा बदलें.

    अनुरोध किए जाने पर, android.os.Parcelable इंपोर्ट करें.

    MarsProperty क्लास की परिभाषा अब ऐसी दिखती है:
@Parcelize
data class MarsProperty (
       val id: String,
       @Json(name = "img_src") val imgSrcUrl: String,
       val type: String,
       val price: Double) : Parcelable {

पांचवां चरण: फ़्रैगमेंट कनेक्ट करना

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

  1. overview/OverviewFragment.kt खोलें. onCreateView() में, फ़ोटो ग्रिड अडैप्टर को शुरू करने वाली लाइनों के नीचे, यहां दिखाई गई लाइनों को जोड़ें. इससे, ओवरव्यू व्यू मॉडल से navigatedToSelectedProperty को देखा जा सकेगा.

    अनुरोध किए जाने पर, androidx.lifecycle.Observer और androidx.navigation.fragment.findNavController इंपोर्ट करें.

    ऑब्ज़र्वर यह जांच करता है कि क्या MarsProperty—लैंबडा में मौजूद it—शून्य नहीं है. अगर ऐसा है, तो उसे findNavController() वाले फ़्रैगमेंट से नेविगेशन कंट्रोलर मिलता है. displayPropertyDetailsComplete() को कॉल करें, ताकि व्यू मॉडल को LiveData को शून्य स्थिति पर रीसेट करने के लिए कहा जा सके. इससे, ऐप्लिकेशन के OverviewFragment पर वापस आने पर, नेविगेशन को गलती से फिर से ट्रिगर नहीं किया जाएगा.
viewModel.navigateToSelectedProperty.observe(this, Observer {
   if ( null != it ) {   
      this.findNavController().navigate(
              OverviewFragmentDirections.actionShowDetail(it))             
      viewModel.displayPropertyDetailsComplete()
   }
})
  1. detail/DetailFragment.kt खोलें. इस लाइन को onCreateView() तरीके में, setLifecycleOwner() को कॉल करने के ठीक नीचे जोड़ें. इस लाइन से, Safe Args से चुना गया MarsProperty ऑब्जेक्ट मिलता है.

    ध्यान दें कि इसमें Kotlin के not-null assertion ऑपरेटर (!!) का इस्तेमाल किया गया है. अगर selectedProperty मौजूद नहीं है, तो कोई गड़बड़ी हुई है. ऐसे में, आपको कोड से null pointer चाहिए. (प्रोडक्शन कोड में, आपको उस गड़बड़ी को किसी तरह से ठीक करना चाहिए.)
 val marsProperty = DetailFragmentArgs.fromBundle(arguments!!).selectedProperty
  1. नया DetailViewModelFactory पाने के लिए, यह लाइन जोड़ें. DetailViewModel का इंस्टेंस पाने के लिए, DetailViewModelFactory का इस्तेमाल करें. स्टार्टर ऐप्लिकेशन में DetailViewModelFactory को लागू किया गया है. इसलिए, आपको सिर्फ़ इसे शुरू करना है.
val viewModelFactory = DetailViewModelFactory(marsProperty, application)
  1. आखिर में, इस लाइन को जोड़ें, ताकि आपको फ़ैक्ट्री से DetailViewModel मिल सके और सभी हिस्सों को कनेक्ट किया जा सके.
      binding.viewModel = ViewModelProviders.of(
                this, viewModelFactory).get(DetailViewModel::class.java)
  1. ऐप्लिकेशन को कंपाइल और रन करें. इसके बाद, मंगल ग्रह की किसी भी प्रॉपर्टी की फ़ोटो पर टैप करें. इसके बाद, उस प्रॉपर्टी की जानकारी के लिए ज़्यादा जानकारी वाला फ़्रैगमेंट दिखेगा. खास जानकारी वाले पेज पर वापस जाने के लिए, 'वापस जाएं' बटन पर टैप करें. ध्यान दें कि ज़्यादा जानकारी वाली स्क्रीन में अब भी कुछ ही जानकारी दिख रही है. अगले टास्क में, आपको उस जानकारी वाले पेज में प्रॉपर्टी का डेटा जोड़ना होगा.

फ़िलहाल, ज़्यादा जानकारी वाले पेज पर सिर्फ़ मंगल ग्रह की वही फ़ोटो दिखती है जो आपको खास जानकारी वाले पेज पर दिखती है. MarsProperty क्लास में प्रॉपर्टी का टाइप (किराये पर लेना या खरीदना) और प्रॉपर्टी की कीमत भी होती है. जानकारी वाले पेज पर, ये दोनों वैल्यू शामिल होनी चाहिए. साथ ही, यह भी बताया जाना चाहिए कि किराये की प्रॉपर्टी के लिए, कीमत हर महीने के हिसाब से है. इन दोनों को लागू करने के लिए, व्यू मॉडल में LiveData ट्रांसफ़ॉर्मेशन का इस्तेमाल करें.

  1. res/values/strings.xml खोलें. स्टार्टर कोड में स्ट्रिंग रिसॉर्स शामिल होते हैं. ये स्ट्रिंग रिसॉर्स, नीचे दिखाए गए हैं. इनकी मदद से, ज़्यादा जानकारी वाले व्यू के लिए स्ट्रिंग बनाई जा सकती हैं. कीमत के लिए, प्रॉपर्टी के टाइप के हिसाब से display_price_monthly_rental या display_price संसाधन का इस्तेमाल किया जाएगा.
<string name="type_rent">Rent</string>
<string name="type_sale">Sale</string>
<string name="display_type">For %s</string>
<string name="display_price_monthly_rental">$%,.0f/month</string>
<string name="display_price">$%,.0f</string>
  1. detail/DetailViewModel.kt खोलें. क्लास के सबसे नीचे, यहां दिया गया कोड जोड़ें.

    अनुरोध किए जाने पर, androidx.lifecycle.Transformations इंपोर्ट करें.

    इस ट्रांसफ़ॉर्मेशन से यह पता चलता है कि चुनी गई प्रॉपर्टी किराये पर दी जाती है या नहीं. इसके लिए, पहले टास्क में इस्तेमाल किए गए टेस्ट का ही इस्तेमाल किया जाता है. अगर प्रॉपर्टी किराए पर दी जाती है, तो ट्रांसफ़ॉर्मेशन, Kotlin when {} स्विच वाले संसाधनों से सही स्ट्रिंग चुनता है. इन दोनों स्ट्रिंग के आखिर में एक नंबर होना चाहिए. इसलिए, बाद में property.price को जोड़ें.
val displayPropertyPrice = Transformations.map(selectedProperty) {
   app.applicationContext.getString(
           when (it.isRental) {
               true -> R.string.display_price_monthly_rental
               false -> R.string.display_price
           }, it.price)
}
  1. प्रोजेक्ट में स्ट्रिंग संसाधनों का ऐक्सेस पाने के लिए, जनरेट की गई R क्लास को इंपोर्ट करें.
import com.example.android.marsrealestate.R
  1. displayPropertyPrice ट्रांसफ़ॉर्मेशन के बाद, नीचे दिखाया गया कोड जोड़ें. यह ट्रांसफ़ॉर्मेशन, कई स्ट्रिंग रिसॉर्स को एक साथ जोड़ता है. यह इस बात पर निर्भर करता है कि प्रॉपर्टी का टाइप किराये पर उपलब्ध जगह है या नहीं.
val displayPropertyType = Transformations.map(selectedProperty) {
   app.applicationContext.getString(R.string.display_type,
           app.applicationContext.getString(
                   when (it.isRental) {
                       true -> R.string.type_rent
                       false -> R.string.type_sale
                   }))
}
  1. res/layout/fragment_detail.xml खोलें. आपको बस एक और काम करना है. आपको नई स्ट्रिंग (जिन्हें आपने LiveData ट्रांसफ़ॉर्मेशन की मदद से बनाया है) को ज़्यादा जानकारी वाले व्यू से बाइंड करना होगा. इसके लिए, प्रॉपर्टी टाइप टेक्स्ट के लिए टेक्स्ट फ़ील्ड की वैल्यू को viewModel.displayPropertyType पर सेट करें. साथ ही, कीमत की वैल्यू के टेक्स्ट के लिए टेक्स्ट फ़ील्ड की वैल्यू को viewModel.displayPropertyPrice पर सेट करें.
<TextView
   android:id="@+id/property_type_text"
...
android:text="@{viewModel.displayPropertyType}"
...
   tools:text="To Rent" />

<TextView
   android:id="@+id/price_value_text"
...
android:text="@{viewModel.displayPropertyPrice}"
...
   tools:text="$100,000" />
  1. ऐप्लिकेशन को कंपाइल और चलाएं. अब प्रॉपर्टी का सारा डेटा, जानकारी वाले पेज पर अच्छी तरह से फ़ॉर्मैट किया हुआ दिखेगा.

Android Studio प्रोजेक्ट: MarsRealEstateFinal

बाइंडिंग एक्सप्रेशन

  • एक्सएमएल लेआउट फ़ाइलों में बाइंडिंग एक्सप्रेशन का इस्तेमाल करके, बाइंड किए गए डेटा पर प्रोग्राम से जुड़ी सामान्य कार्रवाइयां की जा सकती हैं. जैसे, गणित के हिसाब-किताब या शर्त के हिसाब से जांच करना.
  • अपनी लेआउट फ़ाइल में क्लास का रेफ़रंस देने के लिए, <data> टैग के अंदर <import> टैग का इस्तेमाल करें.

वेब सेवा की क्वेरी के विकल्प

  • वेब सेवाओं के अनुरोधों में वैकल्पिक पैरामीटर शामिल किए जा सकते हैं.
  • अनुरोध में क्वेरी पैरामीटर तय करने के लिए, Retrofit में @Query एनोटेशन का इस्तेमाल करें.

Udacity का कोर्स:

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

अन्य:

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

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

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

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

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

पहला सवाल

एक्सएमएल लेआउट फ़ाइल में <import> टैग क्या करता है?

▢ एक लेआउट फ़ाइल को दूसरी फ़ाइल में शामिल करें.

▢ लेआउट फ़ाइल में Kotlin कोड एम्बेड करें.

▢ डेटा से जुड़ी प्रॉपर्टी का ऐक्सेस दें.

▢ इससे आपको बाइंडिंग एक्सप्रेशन में क्लास और क्लास के सदस्यों का रेफ़रंस देने की सुविधा मिलती है.

दूसरा सवाल

Retrofit में, REST वेब सर्विस कॉल में क्वेरी का विकल्प कैसे जोड़ा जाता है?

▢ क्वेरी को अनुरोध यूआरएल के आखिर में जोड़ें.

▢ क्वेरी के लिए, अनुरोध करने वाले फ़ंक्शन में एक पैरामीटर जोड़ें और उस पैरामीटर को @Query से एनोटेट करें.

▢ अनुरोध बनाने के लिए, Query क्लास का इस्तेमाल करें.

▢ Retrofit बिल्डर में addQuery() तरीके का इस्तेमाल करें.

अगला लेसन शुरू करें: 9.1: रिपॉज़िटरी

इस कोर्स में मौजूद अन्य कोडलैब के लिंक के लिए, Android Kotlin Fundamentals कोडलैब का लैंडिंग पेज देखें.