انتقال اطلاعات مورد نظر به اکتیویتی جزئیات یا به یک detail activity ایجاد شده
در این درس، فرآیند انتقال داده بین دو اکتیویتی در اندروید بهصورت تخصصی بررسی میشود؛ از ایجاد یک extra در Main Activity با استفاده از متد putExtra و ارسال یک مقدار کلیدی مانند PRODUCT_ID برای شناسایی محصول انتخاب شده، تا دریافت آن در Detail Activity با فراخوانی متد getStringExtra و استفاده از Data Provider جهت بازیابی شیء Product کامل. در ادامه، نحوه نمایش دقیق اطلاعات محصول از جمله نام، توضیحات و تصویر از طریق کنترلهای UI مانند TextView توضیح داده شده و با ارائه کدهای نمونه و رفع مشکلات رایج، دانشجویان برای درک بهتر مفاهیم intent، extras و bundle مجهز میشوند.
وقتی از یک اکتیویتی به اکتیویتی دیگه میرید میتونید یک داده به عنوان extra ضمیمه اکتیویتی تون کنید. حالا اینکه چرا اینکا رو میکنیم و به چه دردی میخوره الان میگم. یک extra میتونه یک نوع داده معمولی جاوا محسوب بشه مثل، string، number و یا Boolean و یا میتونید آرایه ای از این نوع داده ها رو ضمیمه ی اکتیویتیتون کنید. و حتی میتونید مجموعه های جفتی شامل نام و مقدار رو که به bundle ها معروف هستن و در واقع یک نوع Hash Map هستن رو ضمیمه اکتیویتی کنید. در این آموزش ویدئویی من فقط یک مقدار تکی رو میخوام از main activity ام به اکتیویتی detail ام بفرستم، و بعد در اکتیویتی detail، از اون مقدار استفاده کنم و اطلاعات مفصل تری که نیاز دارم رو برای نمایش در اون اکتیویتی بدست بیارم.
من در حال کار در این پروژه Display Details هستم، و کارم رو در کلاس Main Activity شروع میکنم. میرم سراغ کدی که اکتیویتی detail من رو باز میکنه. که در متد set OnItemClickListener این پایین قرار گرفته. میخوام کدهایی رو اینجا بعد از ایجاد شی intent اضافه کنم و همینطور میخوام یک ارجاعی به محصول جاری ای که کاربر انتخاب کرده بدم. یک متغیر از جنس Product ایجاد میکنم، و نامش رو Product میذارم البته با حروف کوچک.
و مقدارش رو با صدا زدن متد get برای لیست محصولاتم یعنی products و دادن آرگومان position به اون به دست میارم که در واقع position از متد On ItemClick اومده. به عبارت دیگه اومدم اون محصولی که روش کلیک شده رو در آوردم گذاشتم تو متغیر product که بشه محصول جاری من. خب products .get رو بعد از مساوی؛ فراخوانی میکنم و بعد position رو بهش میدم. بنابراین الان محصولم با ویژگی هاش رو در اختیار دارم و میتونم تک تک ویژگی هاش رو بدست بیارم. اگه خاطرتون باشه هر محصول چهار تا ویژگی داشت. ولی من میخوام با یک روش ساده تر اینکار رو بکنم. و فقط product ID یا آیدی محصول که کلید اصلی هستش رو بدست میارم و کاری با بقیه ویژگیهاش ندارم تا بعد تصمیم بگیرم میخام با اکتیویتی detail چکار کنم. متغیر کلید هم که میدونید متغیری هستش که منحصر بفرد هست و برای هر محصول یک مقدار متفاوت هست.
خب بعد، این داده رو به شی intent میدم. متدی با نام Intent .putExtra رو فراخوانی میکنم. میبینید که نسخه های مختلفی از این متد وجود داره و می بینید که همه اونها اولین آرگومان شون یک string هستش و این string هم مربوط به نام اون extra ای هست که قرار هست به اکتیویتی بعدی بره. و آرگومان دوم که انواع مختلفی نظیر bundle یا یک HashMap داره. من میخوام این نسخه رو انتخاب کنم، که باید یک رشته برای نام extra و یک دنباله یا آرایه از کاراکترها یا یک رشته برای مقدار extraبهش بدم.
باید نام این extra رو طوری تعیین کنیم که براحتی توسط هر دو اکتیویتی در دسترس باشه، پس یک مقدار ثابت برای اون در نظر میگیرم. اسمش رو میذارم PRODUCT_ID. پس این شد نام extra. ولی این ثابت هنوز وجود نداره، پس بعد از تایپ نامش، از راهنمای اندروید استودیو استفاده میکنم و یک مقدار ثابت یعنی گزینه constant field PRODUCT_ID رو واسش تعیین میکنم و در واقع این متغیر عضوی از کلاس main activity هستش پس کلاس target رو روی main activity میذارم. میخوام یک رشته برای مقدارش تعیین کنم و هر چیزی که بخوایید میتونید بذارید، ولی من میخوام مقدارش دقیقا همون نام ثابتش باشه پس مقدارش رو PRODUCT_ID میذارم.
بعد آلت یا آپشن رو به ترتیب برای ویندوز و مک و بعد هم F7 رو فشار میدم، و بعد میتونم مستقیم برم سراغ اون مقدار ثابتی که اینجا این پایین تعریفش کردم. بعد باید مقداری رو که قصد دارم به اکتیویتی بعدی یعنی detail activity بفرستم رو بهش بدم. واضح هست که این مقدار همون آیدی محصول مربوطه هست و این آیدی با این متد product .getProductId بدست میاد. الان من آیدی محصول رو به اینتنت دادم و و با این اینتنت اکیتیویتی بعدی بالا میاد و با استارت اکتیویتی detail این اکتیویتی میتونه آیدی محصول مورد نظر رو دریافت کنه. به کلاس Detail Activity میرم، و این کدها رو اینجا قبل از کار با Floating ActionButton و قبل از هر کد دیگه ای اضافه میکنم.
اول از همه باید مقدار extra ای که با اینتنت اومده رو بدست بیارم. یک متغیر string ایجاد میکنم و یکبار دیگه اسمش رو میذارم product Id. و از این عبارت برای بدست آوردن اون مقدار استفاده میکنم. اول متد get Intent رو صدا میزنم. که در واقع یک ارجاعی میده به شی intent که با اون این اکتیویتی لود شده. بعد متد get StringExtra رو فراخونی میکنم. توجه داشته باشید اینجا توی این لیست پیشنهادی علاوه بر get StringExtra، get BooleanExtra، get ByteExtra و get IntExtra و نسخه های دیگه هم وجود دارن.
پس وقتی میخواییم یک extra به اکتیویتی فرستنده اضافه کنیم همیشه از put Extra استفاده میکنیم. و وقتی میخواییم اون extra رو بدست بیاریم اول باید بدونیم نوع اون extra ای که دنبالش هستیم چی هست. و اینجا میدونم که از نوع string هستش پس get StringExtra رو فراخونی میکنم و حالا باید نام یا کلید اون مقدار extra رو وارد کنم. و اون رو از ثابت های Main Activity که قبلا ساختم بدست میارم، یعنی MainActivity .PRODUCT_ID. ولی این رشته رو جزء لیست ثابت های در دسترس نمیبینم. اینطور بگم که PRODUCT_ID رو توی این لیست پیشنهادی نمی بینم.
پس برمیگردم به کلاس Main Activity، و میبینم که سطح دسترسی مقدار متغیر ثابت PRODUCT_ID رو private تعیین کردم. کرسرم رو روی این کلمه قرار میدم با زدن آلت و اینتراز راهنمای اندروید استودیو استفاده میکنم و private رو به public تغیر میدم. و به Detail Activity برمیگردم و با فشردن کنترل space، ثابت مورد نظرم رو میبینم. خب حالا من PRODUCT_ID رو دارم و از این مقدار میتونم سایر اطلاعات محصولم رو بدست بیارم. کلاس Data Provider یک فیلد با نام product Map داره.
این یک نگاشت هست از همه اشیاء داده با یک کلیدی که از جنس String هست و یک داده از نوع Product. خب برمیگردم به کلاس Detail Activity، و از اون مپ به این شکل استفاده میکنم. یک متغیر از جنس product ایجاد میکنم، اسمش رو product میذارم و مقدارش رو با صدا زدن Data Provider .productMap.get واسش تعریف میکنم، و product Id رو به عنوان آرگومانش تعیین میکنم. خب حالا یک شی product کاملاً پیچیده در اختیار دارم.
و حالا میخوام کدی اضافه کنم که نام محصولم رو نمایش بده. یک ارجاعی به شی Text View ایجاد میکنم و مثل همیشه متد find ViewById رو صدا میزنم و آیدی Text View که میخوام نام محصولم داخلش نمایش داده بشه رو وارد میکنم. که آیدیش name Text هستش و برای اطمینان از نوع بازگشتی اش هم کستش میکنم. بعد tv .setText رو فراخونی میکنم و product .getName رو به عنوان ورودی اش تعیین میکنم. و حالا، اکتیویتی دوم باید نام محصول مورد نظر رو که از لیست انتخاب میکنم به درستی نمایش بده.
پس برنامه ام رو اجرا میکنم. و حالا وقتی یک آیتم رو لمس میکنم و وارد اکتیویتی جزییات میشم نام دقیق محصولی که انتخاب کردم رو به درستی نمایش میده. من آیتم اولی رو امتحان کردم حالا یکم میام پایینتر و ایندفعه Polo shirt رو انتخاب میکنم، و یکبار دیگه میبینیم که مقدار نامش رو به درستی نمایش میده. خب حالا باید بقیه کنترل های بصری رو هم کامل و سفارشی کنم. خوشبختانه، از قبل این کد رو جایی تو یکی از کلاس هام ذخیره داشتم. و فقط باید قسمتی از اون کد رو کپی و پیست کنم. به کلاس Product ListAdapter ام میرم.
اول، یک کپی از متد get BitmapFromAsset میگیرم. برمیگردم به کلاس Detail Activity، و اینجا پیستش میکنم. وقتی این پنجره بالا اومد من همه پیشنهاداتش رو برای ایمپورت هایی به کلاس جاری ام قبول میکنم. در واقع وقتی میخواییم یک تعداد کد در اینجا کپی کنیم اندروید استودیو تشخیص میده که احتیاج به تعدادی ایمپورت هست و من همه رو قبول و اوکی رو میزنم. خب باید یک تغییر کوچیک اینجا انجام بدم. وقتی get Assets رو فراخونی میکنم نیازی نیست که get Context رو قبلش صدا بزنم. چون الان این متد رو در داخل یک کلاس اکتیویتی فراخونی کردم و مشخصا معلوم هست که context همین کلاس اکتیویتی جاری هستش، و میتونم به طور مستقیم این متد رو در این کلاس جاری صدا بزنم.
بعد برمیگردم به کلاس Product ListAdapter ام، و همه این قسمت از کد رو انتخاب و کپی میکنم و برمیگردم به Detail Activity و اینجا بعد از تنظیمات Text View اورجینال ام پیستش میکنم، و یکبار دیگه تمام پیشنهادات مربوط به ایمپورت رو قبول میکنم. در این نسخه کد، نیازی به ارجاع دادن به view خاصی ندارم، چون از لایوت همین اکتیویتی میخوام استفاده کنم، پس convert View رو برای هر دو تا find ViewById حذف میکنم.
یک کوچولو کد دیگه برای نمایش نیاز دارم. یکبار دیگه یک متغیر Text View ایجاد میکنم و ایندفعه اسمش رو desc View مخفف description view یعنی توضیح محصول میذارم. و آیدی id .descriptionText رو از فایل لایوت بهش میدم. بعد descView .setText رو صدا میزنم و product .getDescription رو بعنوان ورودی بهش میدم. و حالا همه کدی که برای نمایش جزییات محصول لازم بود رو اضافه کردم.
البته اگه از کدهام ران بگیرم یک ارور دارم که باید حل بشه، و دلیلش اون کپی پیست ناقصی هست که انجام دادم. چون اینجا آخر این خط یک semicolon یادم رفته بود. کدم رو مجدد تست میکنم تا مطمئن بشم ارور دیگه ای وجود نداشته باشه. برای اینکار از منو Build، Make Module رو انتخاب میکنم. تا اگه اروری مشاهده شد حلش کنم و می بینید که ارری مشاهده نمیشه. یکبار دیگه از برنامه ام ران میگیرم. و حالا با لمس هر آیتم، نام، قیمت، توضیحات و تصویر مربوط به همون آیتم رو درست و دقیق مشاهده میکنم. و میام پایین و آیتم های دیگه رو هم امتحان میکنم.
پس همه اینهایی که قدم به قدم توضیح دادم مراحل مورد نیاز برای انتقال داده از یک اکتیویتی به اکتیویتی دیگه بود. اگه بخوام یک خلاصه کوتاه از کارهایی که کردیم بگم باید بگم اول یک extras ایجاد کردیم، که میتونستیم یک تعداد مقادیر چند تایی به عنوان extras تعیین کنیم یا همونطور که ما انجام دادیم فقط یک مقدار کلید خالی رو برای extra در نظر بگیریم و بعد این مقدار رو در اکتیویتی دریافت کننده که برای کار ما اکیتیوتی detail بود بدست بیاریم و در جای لازم از داده هاش استفاده کنیم.