یک روش جدید برای انتقال داده‌ بین فرگمنت ها

ترجمه و تالیف : پوریا شریفی
تاریخ انتشار : 04 شهریور 99
خواندن در 2 دقیقه
دسته بندی ها : اندروید

از زمان به وجود آمدن مولفه‌های معماری اندروید، توسعه اندروید بسیار کارآمدتر و ساده‌تر شده است. از طرف دیگر، مفاهیمی مانند منبع واحد و single activity تحول عظیمی را به همراه داشتند.

با ایده توسعه با کیفیت بالا، توسعه‌دهندگان تمایل دارند از اکتیویتی‌های سنتی به فرگمنت های موثر منتقل شوند. این امر بسیاری از توسعه‌دهندگان را در موقعیتی فریبنده در مورد ارتباط داده بین فرگمنت‌ها قرار می‌دهد (از جمله خودم در بعضی از موارد).

مشکل

انتقال داده بین یک فرگمنت با فرگمنت دیگر بدون هیچ‌گونه ارجاع (context، رابط‌ها، viewModel اشتراکی، یا داده‌های زنده در سطح برنامه) تقریبا غیر ممکن به نظر می‌رسد. اما اگر کمی عمیق‌تر نگاه کنید، یک راه‌حل پیدا خواهید کرد: targetFragment.

targetFragment راهی برای برقراری ارتباط با فرگمنت های موجود در پشته فراهم می‌کند. در مبدا نیازی به هیچ ارجاعی از مقصد نیست. به زبان ساده targetFragment روشی برای پیاده‌سازی عملکرد onActivityResult در فرگمنت است.

چگونه کار می‌کند

بیایید showOptionsDialog را در فرگمنت والد صدا بزنیم، که یک OptionsDialogFragment را با لیستی از گزینه‌ها نمایش می‌دهد. تنها کار جدیدی که ما در اینجا انجام می‌دهیم این است که فرگمنت مورد نظر را با کد درخواست تنظیم کنیم:

private fun showOptionsDialog() {
        val optionsDialogInstance = OptionsDialogFragment()
        optionsDialogInstance.setTargetFragment(this, 1)
        optionsDialogInstance.show(childFragmentManager, optionsDialogInstance.tag)
}

سپس ما باید در فرگمنتی که داده‌ها را دریافت می‌کنیم OnActivityResult را پیاده‌سازی کنیم. پس از آن تنها چیزی که باقی می‌ماند، انتقال داده‌ها از فرگمنت منبع است. برای این کار همانند زیر عمل می‌کنیم:

fun onExit(selectionValue: String){
        val intent = Intent()
        intent.putExtra("selection",selectionValue)
        intent.putExtra(TYPE,type)
        targetFragment?.onActivityResult(targetRequestCode, Activity.RESULT_OK, intent)
}

خب، این خوب است اما ما می‌توانیم داده‌ها را به یک فرگمنت انتقال دهیم بدون اینکه به آن ارجاعی داشته باشیم. اما مشکل این است که عملکرد targetFragment تنها در صورتی کار می‌کند که فرگمنت های منبع و مقصد در یک fragmentManager باشند. اگر فرگمنتی از childFragmentManager متاثر شده باشد، آنگاه targetFragment کار نخواهد کرد.

راه‌حل

بعد گذشت نزدیک به یک دهه، تیم اندروید تمرکز خود را روی این موضوع آغاز کردند. با انتشار Fragment 1.3.0-alpha04، اکنون هر FragmentManager، FragmentResultOwner را پیاده‌سازی می‌کند. این بدان معنی است که یک FragmentManager می‌تواند به عنوان یک فروشگاه مرکزی برای نتایج فرگمنت عمل کند.

این تغییر اجازه می‌دهد تا هر فرگمنت با تنظیم fragmentResult و گوش دادن به آن نتایج با هم ارتباط برقرار کنند، بدون نیاز به ارجاع مستقیم فرگمنت‌ها به یکدیگر. برخلاف targetFragment در همه fragmentManagerها کار می‌کند.

نحوه عملکرد

ابتدا می‌بینیم که چکونه می‌توان داده‌ها را در سطح FragmentManager و سپس بین FragmentManager فرزند و والد انتقال داد. برای انتقال داده به فرگمنت مقصد از فرگمنت مبدا، باید یک listener با یک کلید واژه خاص اضافه کنیم. فقط bundle که با این کلید ارسال شده است در اینجا فراخوانی می‌شود. همانند زیر:

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    // Use the Kotlin extension in the fragment-ktx artifact
    setFragmentResultListener("requestKey") { key, bundle ->
        val result = bundle.getString("name")
        // Do something with the result...
    }
}

اکنون زمان آن رسیده که داده‌ها را از فرگمنت منبع منتقل کنید، همانطور که در پایین نشان داده شده است:

tvSave.setOnClickListener {
    setResult("requestKey", bundleOf("name" to updatedValue))
}

این بسیار ساده است و کار خوبی است وقتی که هر دو فرگمنت در یک FragmentManager هستند. اگر چندین بار داده را ارسال کردید، setResult همیشه آخرین داده را به مقصد منتقل می‌کند. اگر listener در زمان فراخوانی setResult تنظیم نشده باشد، داده‌ها را ذخیره می‌کند و هنگام اختصاص listener آن را ارسال می‌کند. از همه مهم‌تر به یاد داشته باشید که فقط یک listener را با یک کلید خاص اعلام کنید.

در مورد ارتباط بین فرگمنت های والد و فرزند چه می‌توان گفت؟ خب، ما هم برای آن راه‌حل داریم. برای توضیح سطح بالا، فرگمنت فرزند داده‌ها را به childFragmentManager ارسال می‌کند، سپس داده‌ها را به فرگمنت والد منتقل می‌کنند. پیاده‌سازی آن همانند بالا است. تنها تفاوت این است که ما باید همانطور که در زیر آمده است listener را به fragmentManager فرزند اضافه کنیم:

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    // We set the listener on the child fragmentManager
    childFragmentManager.setResultListener("uniquerequestKey") { key, bundle ->
        val result = bundle.getString("name")
        // Do something with the result..
    }
}

همین :). ارسال داده‌ها همانند موارد فوق است. ما باید setResult را با یک کلید منحصر به فرد و داده bundle فراخوانی کنیم.

نتیجه

من می‌دانم که همه ما به کار با یک رابط، viewModel اشتراکی، و راه‌های بیشتری برای برقراری ارتباط بین فرگمنت‌ها عادت کرده‌ایم. اما منصوخ شدن targetFragment و ارائه شدن fragmentResult API یک راه‌حل platform-native ارائه می‌دهد. این راه‌حل هنوز در مراحل آلفا است و فکر می‌کنم در روزهای آینده بسیار مفید خواهد بود، زیرا همه ما بیشتر از هر زمانی به روی توسعه فرگمنت‌گرا متمرکز شده‌ایم.

منبع

گردآوری و تالیف پوریا شریفی
آفلاین
user-avatar

ابتدا که با برنامه‌نویسی آشنا شدم به سمت php و طراحی وب رفتم، بعد از اون به توسعه‌ی اندروید علاقه‌مند شدم و تقریبا ۲ سال است که مشغول به برنامه‌نویسی اندروید هستم، همچنین عاشق یادگیری چیزهای جدید هستم.

دیدگاه‌ها و پرسش‌ها

برای ارسال نظر لازم است ابتدا وارد سایت شوید
در حال دریافت نظرات از سرور، لطفا منتظر بمانید