کار با فرگمنت ها در معماری تک اکتیویتی


چگونه نتیجه هر فرگمنت را در معماری تک اکتیویتی دریافت کنیم؟

در مقاله های پیشین مزایای استفاده از معماری تک اکتیویتی و پیاده سازی آن را بررسی نموده ایم. برنامه نویسان زیادی استفاده از معماری تک اکتیویتی را توصیه می کنند. در این مقاله می خواهیم یک مشکل را حین کار با معماری تک اکتیویتی بررسی کنیم. برای فرگمنت ها متدی مثل startActivityResult وجود ندارد. اگر با startActivityForResult ناآشنا هستید، متدی ست که با یک request code اجازه به جریان انداختن اکتیویتی را ایجاد می کند. و با تمام شدن پروسه اجرای اکتیویتی، اکتیویتی اول callbackی را در onActivityResult دریافت می کند.

در این مقاله بررسی می کنیم حین کار با تک اکتیویتی چگونه چنین فرآیندی را برای فرگمنت ها بسازیم.

طرح یک مثال

برای پیاده سازی این عملکرد برنامه ساده ای با دو فرگمنت می سازیم. یکی با نام DisplayNameFragment که یک پیام را نمایش می دهد. دکمه set name که با زدن آن وارد فرگمنت جدیدی می شویم. SetNameFragment که در این فرگمنت یک رشته به عنوان ورودی دریافت می شود. با زدن دکمه finish به فرگمنت قبلی باز می گردیم. و رشته دریافتی در فرگمنت دیگر، در فرگمنت فعلی نمایش داده می شود.

فرگمنت مقصد

نکته اصلی حل مشکل مطرح شده استفاده از فرگمنت مقصد می باشد. هنگامی که یک فرگمنت از بخش دیگری شروع می شود ، و فرگمنتی می خواهد نتیجه را به دست آورد، از فرگمنت مقصد استفاده می شود.

شروع یک فرگمنت

در این بخش فرگمنت SetNameFragment  را درون فرگمنت DisplayNameFragment آغاز می کنیم. قطعه کد زیر در onClickListener دکمه مربوطه قرار می گیرد :

private fun launchSetNameFragment() {
      val newFragment = SetNameFragment()
      val tag = SetNameFragment::class.java.simpleName

      newFragment.setTargetFragment(this, SET_NAME_REQUEST_CODE)
      (activity as? MainActivity)?.replace(newFragment, tag)
  }

متد ()MainActivity.replace فرگمنت موجود را با فرگمنتی که به آن پاس داده شده عوض می کند.

بازگرداندن مقدار نتیجه

فرگمنتی که به متد ()setTargetFragment پاس داده شده، در متد ()getTargetFragment دریافت می شود. پس به کمک قطعه کد زیر می توان درون SetNameFragment مقدار مورد نظر را به فرگمنت مورد نظر پاس دهیم. این قطعه کد نیز درون onClickListener دکمه مربوطه قرار می گیرد.

private fun returnWithName() {
      val name = name_input.text.toString()

      (targetFragment as? DisplayNameFragment)?.setName(name)
      activity?.supportFragmentManager?.popBackStackImmediate()
  }

در این مثال فرگمنت نهایی در واقع نمونه ای ساخته شده از DisplayNameFragment است. وقتی رشته مورد نظر دریافت می شود به کمک متد ()setName به DisplayNameFragment  پاس داده می شود. بعد به اکتیویتی دستور داده می شود تا فرگمنت DisplayNameFragment  را نمایش دهد.

مدیریت نتیجه

همان طور که backstack فرگمنت ها را مدیریت می کنی، به نتیجه هر فرگمنت دسترسی خواهید داشت. و این قسمت با add و replace فرگمنت ها هنگام ساخت عملی می شود.

// Adds a fragment on top of whatever might already be in the container.
  supportFragmentManager.beginTransaction()
     .add(R.id.container, newFragment, tag)
     .addToBackstack(tag)
     .commit()

  // Replaces whatever is in the container with the new fragment.
  supportFragmentManager.beginTransaction()
     .replace(R.id.container, newFragment, tag)
     .addToBackstack(tag)
     .commit()

به کمک add فرگمنت در بالاترین بخش backstack قرار می گیرد. هیچ اتفاقی برای فرگمنت قبلی نمی افتد فقط مثل بشقاب روی هم چیده می شوند. با قرارگیری فرگمنت جدید در backstack در فرگمنت قبلی نه متد ()onPause و نه متد () onDestroyView صدا زده نمی شوند.

با استفاده از replace هر فرگمنتی در محلی که وجود دارد از بین می رود و فرگمنت جایگزین به جای آن نمایش داده می شود. به این معنی که در فرگمنت قبلی متدهای متفاوت چرخه زندگی آن اجرا می شوند. تنها متدی که اجرا نمی شود ()onDestroy است چون فرگمنت نابود نشده است.

برای آشنایی با چرخه زندگی یک فرگمنت، مطالعه این مطلب در stackoverflow پیشنهاد می شود.

استفاده از add

اگر از add استفاده می کنید فرگمنت قبلی هم چنان در صفحه حضور دارد. پس باید به طور دستی حالت دلخواه از نمایش اکتیویتی ها را دستکاری کنید.

fun setName(name: String) {
    name_textview.text = getString(R.string.your_name_is).format(name)
}

استفاده از replace

اگر در چنین حالتی از replace استفاده کنید برنامه کرش خواهد کرد. به این دلیل که view مربوط به DisplayNameFragment  نابود شده و فرگمنت جدید می تواند نمایش داده شود. قطعه کد زیر سعی دارد تا به view ی که از بین رفته دست پیدا کند. به همین دلیل برنامه کرش می کند. برای جلوگیری از این اتفاق می توانیم رشته دریافتی را در سظح فیلدهای کلاس تعریف کنیم و دسترسی به آن را از فرگمنتی که از بین رفته حذف کنیم. در متد ()onResume تکست ویو مورد نظر با مقدار دریافتی از فرگمنت قبلی، به روز می شود.

lass DisplayNameFragment : Fragment() {

      private var name: String = ""

      override fun onResume() {
          super.onResume()

          name_textview.text = getString(R.string.your_name_is).format(name)
      }

      fun setName(name: String) {
          this.name = name
      }

      // ...
  }

پس هر دو راه، متناسب با نیاز برنامه ممکن هستند.

 

به این پست امتیاز دهید

روی ستاره های کلیک کنید و امتیاز بدید

میانگین امتیاز 5 / 5. تعداد: 1

از امتیاز شما متشکریم

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

Enter Captcha Here : *

Reload Image