اموزش کامل Navigation component در اندروید- قسمت اول

یکی از باحال ترین و هیجان انگیز ترین بخش های jetpack چیزی نیست جز navigation component که در واقع مجموعه ای از کتابخانه ها , پلاگین ها و ابزار هاست که 'جا به جایی' رو متحول میکنه. دیگه نیازی نیست با چیز هایی که خیلی رو اعصاب هستند مثل FragmentManager و ... سر و کار داشته باشید و کار خیلی سریع تر و تمیز تر انجام میشه

گوگل توصیه میکند که از الگو single activity پیروی کنیم. یعنی فقط یک اکتیویتی داشته باشیم و بقیه کار ها با فرگمنت ها انجام بشه. به خاطر همین ما مبنا اموزش رو همین قرار دادیم ولی اگر چند اکتیویتی هم داشته باشید فرقی نمیکند . مفاهیم یکی است . ولی ما مثل حرفه ای ها از همین الگو استفاده می کنیم:)

واقعا لازمه بگم که نگذارید تعاریف قلمبه این مبحث شما را از پا در اورد. با کمی تمرین و مطالعه خیلی راحت برایتان جا می افتد.

اول یک مقایسه بین روش قدیمی و navigation داشته باشیم. یک جور هایی اگر روش قدیمی رو بلد باشید کار کردن با navigation زیاد براتون سخت نیست.

فرض کنید یک اکتیویتی با تعدادی فرگمنت داشته باشیم که نیاز داریم بین این فرگمنت ها جا به جا شویم.در روش قدیمی در layout اکتیویتی باید که FrameLayout تعریف میکردیم که فرگمنت ها در اون جا به جا میشدند. جا به جایی این فرگمنت ها هم نیازمند استفاده از FragmentManager و ... بود. میتونستیم با fragment argument یک سری اطلاعات رو بین فرگمنت ها رد و بدل کنیم و ...

و اما در navigation کار خیلی راحت تر است ولی ارتباطی هم بین تعاریف navigation و روش قدیمی وجود داره که اگر برای اولین بار از اون استفاده میکنید ممکنه به درک شما کمک کنه. در navigation هم باید یک navHost تعریف کنیم که فرگمنت ها (یا اکتیویتی ها که بهشون میگیم مقصد) در اون جا به جا میشوند. مثل FrameLayout. هر navHost به یک نقشه(navigation graph) متصل میشه که اون نقشه میگه که چه مقصد هایی در اون navHost وجود داره و کاربر چطور میتونه بین اونها جا به جابه بشه(action). کنترل هر navHost رو یک navContorller به عهده میگیره که مثل FragmentManager میمونه با این تفاوت که هرnavHost یک navController اختصاصی داره.

پس با سه عنصر مهم navigation component اشنا شدیم:

۱- navHost: یک container یا نگه دارنده یک سری مقصد . مقصد ها در navHost جا به جا میشوند درست مثل FrameLayout.

۲− navigation graph : یک نقشه از همه مقصد ها و البته همه مسیر هایی که کاربر میتواند طی انها بین مقصد ها جا به جا شود(که به ان action میگوییم) . این نقشه را به یک navHost میدهیم و navHost از طریق این نقشه میفهمه کاربر کجا ها میتونه بره.

۳−action: هر اکشن میگه که کاربر میتونه از فلان مقصد به یک مقصد دیگه بره. هر وقت این اکشن رو فراخوانی میکنیم واقعا بین ان دو مقصد جابه جا میشویم.


و اما navigation graph که به اون نقشه میگیم از دو بخش تشکیل شده:

۱- مقصد یا destination : هم فرگمنت یا اکتیویتی که کاربر میتواند به انجا برود. صفحه هایی که در نقشه میبینید(شماره یک) مقصد های ما هستند.(فرگمنت یا اکتیویتی که کاربر میتواند به انجا برود)

۲− action: فلش هایی که مسیرهایی که کاربر میتواند بین مقصد ها طی کند را مشخص میکند.

این نقشه شکل کلی برنامه ما را مشخص میکند. چند مقصد داریم که طی action ها بین هم جابه میشوند.

حرف زدن بسه. بریم سراغ کار عملی

چیکار میکنیم؟

اگر بخوام کل مراحل رو خلاصه وار توضیح بدم(همه مراحل به صورت مفصل بررسی میشوند. فقط این رو داشته باشید بد نیست) :

  • اول یک اکتیویتی را به عنوان navHost انتخاب میکنیم. مثل اینکه قبلا به layout اکتیویتی یک FrameLayout اضافه میکردیم. مقصد ها در این navHost جا به جا میشوند.
  • بعد یک یا چند فرگمنت که میخواهیم در این navHost وجود داشته باشند را میسازیم.
  • سپس یک نقشه میسازیم, همه این مقصد ها را به ان اضافه میکنیم و با ابزار های گرافیکی action ها (مسیر هایی که کاربر میتواند طی ان جا به جا شود) را رسم میکنیم.
  • بعد از بر اساس فعالیت های کاربر مثل کلیک روی یک دگمه یکی از action ها رو فرا میخوانیم - هر اکشن میگه از فلان مقصد به مقصد دیگر برو- و طی ان میتوانیم اطلاعات جا به جا کنیم و کار های معمولی

اول dependency بعد کار

برای اینکه با navigation کار کنید به این dependency های نیاز دارید:

dependencies {
  def nav_version = &quot2.3.0-alpha06&quot

  // Java language implementation
  implementation &quotandroidx.navigation:navigation-fragment:$nav_version&quot
  implementation &quotandroidx.navigation:navigation-ui:$nav_version&quot

  // Kotlin
  implementation &quotandroidx.navigation:navigation-fragment-ktx:$nav_version&quot
  implementation &quotandroidx.navigation:navigation-ui-ktx:$nav_version&quot

  // Dynamic Feature Module Support
  implementation &quotandroidx.navigation:navigation-dynamic-features-fragment:$nav_version&quot

  // Testing Navigation
  androidTestImplementation &quotandroidx.navigation:navigation-testing:$nav_version&quot
}

×× ممکن است وقتی این مقاله را میخوانید ورژن جدیدی منتشر شده باشد

ساخت navigation graph

این نقشه یک نوع جدید resource است که همه مقصد ها و action ها در اون تعریف میشه. ظاهر گرافیکی این نقشه اینطوریه:


و مثل layout ها میتونیم هم با نوشتن تگ های XML و به صورت گرافیکی با Navigation Editor اون رو تغییر بدیم. برای ساخت نقشه این مسیر رو دنبال کنید

  1. روی پوشه res راست کلیک کنید و New > Android Resource File را انتخاب کنید. صفحه New Resource File ظاهر میشود.
  2. یک اسم مثل "nav_graph" انتخاب کنید.
  3. در بخش Resource type , و Navigation را برگزینید.

وقتی برای اولین بار این کار را کردید به صورت خودکار یک پوشه به اسم navigation ایجاد میشود و nav_graph در ان قرار میگیرد.

وقتی نقشه ساخته شد اندروید استودیو نقشه را در Navigation Editor باز میکند که در ان میتوانید به صورت گرافیکی نقشه را تغییر دهید. اگر تب text را انتخاب کنید با همچین چیزی مواجه میشوید:

<?xml version=&quot1.0&quot encoding=&quotutf-8&quot?>
<navigation xmlns:android=&quothttp://schemas.android.com/apk/res/android&quot
            xmlns:app=&quothttp://schemas.android.com/apk/res-auto&quot
            android:id=&quot@+id/nav_graph&quot>

</navigation> 

همانطور که میبینیدroot element همه نقشه ها <navigation> است. این فایل با تگ ها <destination> و <action> پر خواهد شد.

اضافه کردن NavHost به اکتیویتی

فرض کنید NavHost همان FrameLayout است. باید ان را به layout اکتیویتی اضافه کنیم.

<?xml version=&quot1.0&quot encoding=&quotutf-8&quot?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android=&quothttp://schemas.android.com/apk/res/android&quot
    xmlns:app=&quothttp://schemas.android.com/apk/res-auto&quot
    xmlns:tools=&quothttp://schemas.android.com/tools&quot
    android:layout_width=&quotmatch_parent&quot
    android:layout_height=&quotmatch_parent&quot
    tools:context=&quot.MainActivity&quot>

    <androidx.appcompat.widget.Toolbar
        .../>

    <fragment
        android:id=&quot@+id/nav_host_fragment&quot
        android:name=&quotandroidx.navigation.fragment.NavHostFragment&quot
        android:layout_width=&quot0dp&quot
        android:layout_height=&quot0dp&quot
        app:layout_constraintLeft_toLeftOf=&quotparent&quot
        app:layout_constraintRight_toRightOf=&quotparent&quot
        app:layout_constraintTop_toTopOf=&quotparent&quot
        app:layout_constraintBottom_toBottomOf=&quotparent&quot

        app:defaultNavHost=&quottrue&quot
        app:navGraph=&quot@navigation/nav_graph&quot />

    <com.google.android.material.bottomnavigation.BottomNavigationView
        .../>

</androidx.constraintlayout.widget.ConstraintLayout>

اینجا سه تا نکته وجود داره:

  • با android:name مشخص میکنیم که navHost ما از چه نوعی خواهد بود. در اینجا گفتیم NavHostFragment یعنی این navHost چند فرگمنت را در خود نگه میدارد. معمولا این چیزیه که نیاز دارید.
  • با app:defaultNavHost="true" گفتیم که وقتی کاربر دکمه برگشت(back button) را فشار داد یهو از برنامه نپر بیرون برای خودت.
  • با app:navGraph="@navigation/nav_graph" هم ادرس نقشه مون رو مشخص کردیم. داریم بهش میگیم: داداش این navHost پیرو این نقشه است, فلان تعداد مقصد داره که با این action ها بین هم میتونن جا به جا بشن.

چطور یک مقصد اضافه کنیم

توی navigation editor(همون محیط گرافیکی) میتونیم با استفاده از آیکون پایین یکی از فرگمنت یا اکتیویتی هایی که درست کردیم رو به عنوان مقصد تعیین کنیم. بعد یک پیش نمایش از ظاهر فرگمنت یا اکتیویتی به نمایش در میاد که میتونیم اون رو جا به کنیم و نقشه خودمون رو بسازیم.

با انتخاب این ایکون در بالای navigation editor یک مقصد اضافه میکنیم.
با انتخاب این ایکون در بالای navigation editor یک مقصد اضافه میکنیم.

اتصال مقصد ها یا همون action

توی navigation editor روی یک مقصد کلیک کنید . دایره ای توخالی یا همچین چیزی رو باید ببینید:

اون دایره رو بگیرید و بکشید(درگ اند دراپ برای با کلاس ها) و روی یک مقصد ول کنید.یک فلش ایجاد میشه که مشخصه یک action است. از یک مقصد چند action هم میتوانند سرچشمه بگیرند که بعدا با کد میگیم که کِی این اکشن ها اجرا شوند یا به عبارتی از یک مقصد به مقصد دیگر رویم.

نگاهی به کد XML عکس بالا بیندازیم:

دو فرگمنت داریم که مقصد های ما هستند. اون مقصدی که اکشن از ان سرچشمه گرفته(از اون مقصد به مقصد دیگر رفته ایم) یک تگ <action> دارد. یک id که وقتی بخواهیم بین این رو مقصد جا به جا شویم از ان استفاده میکنیم و یک destination که مقصد را مشخص میکند.

<?xml version=&quot1.0&quot encoding=&quotutf-8&quot?>
<navigation xmlns:app=&quothttp://schemas.android.com/apk/res-auto&quot
    xmlns:tools=&quothttp://schemas.android.com/tools&quot
    xmlns:android=&quothttp://schemas.android.com/apk/res/android&quot
    app:startDestination=&quot@id/blankFragment&quot>
    <fragment
        android:id=&quot@+id/blankFragment&quot
        android:name=&quotcom.example.cashdog.cashdog.BlankFragment&quot
        android:label=&quotfragment_blank&quot
        tools:layout=&quot@layout/fragment_blank&quot >
        <action
            android:id=&quot@+id/action_blankFragment_to_blankFragment2&quot
            app:destination=&quot@id/blankFragment2&quot />
    </fragment>
    <fragment
        android:id=&quot@+id/blankFragment2&quot
        android:name=&quotcom.example.cashdog.cashdog.BlankFragment2&quot
        android:label=&quotfragment_blank_fragment2&quot
        tools:layout=&quot@layout/fragment_blank_fragment2&quot />
</navigation>

جا به جایی بین مقصد ها:

حالا وقت ان رسیده که بالاخره جا به جا شویم. NavController را یادتان هست؟ هر navHost یک NavController اختصاصی دارد که جا به جایی بین مقصد های ان navHost را کنترل میکند. برای اینکه جا به جا شویم به یک شی از ان نیاز داریم:

اگر در کاتلین هستید اینطور یک شی از ان میگیریم(فرقی نمیکند از چه روشی):

  • Fragment.findNavController()
  • View.findNavController()
  • Activity.findNavController(viewId: Int)

و در جاوا:

  • NavHostFragment.findNavController(Fragment)
  • Navigation.findNavController(Activity, @IdRes int viewId)
  • Navigation.findNavController(View)

به کمک متد navigate میتوانیم به یک مقصد دیگر رویم.میتوانیم id مقصد را به عنوان پارامتر به ان دهیم یا به شیوه صحیح تر id اکشن را به ان دهیم.

navControllerObject.navigate(action_id  or destination_id )

جا به جایی اطلاعات بین مقصد ها:

میدانید دیگه. مثلا یک EditText داریم که کاربر اسم خود را در ان وارد میکند و وقتی روی یک دکمه کلیک کرد میخواهیم اسم ان در یک فرگمنت دیگر نمایش داده شود. این دیتا(اسم کاربر) را باید با شی ‌Bundle جا به جا کنیم. طبیعتا به صورت زوج های کلید-مقدار. این ‌bundle را به عنوان پارامتر دوم متد navigate میدهیم

Bundle bundle = new Bundle();
bundle.putString(&quotname&quot, user_name);
navControllerObject.navigate(action_id  or destination_id , bundle)

و در مقصد دریافت کننده با متد getArgument به ان bundle دسترسی خواهیم داشت:

TextView name = findViewById(R.id.textViewName);
name.setText(getArguments().getString(&quotname&quot));

فقط یک نکته مهم:
باید در مقصد دریافت کننده دیتا بگیم که قراره یک دیتایی برات ارسال بشه. از تب XML مقصدی که دیتا را دریافت میکند پیدا کنید و تگ <action> را به ان اضافه کنید:

<fragment android:id=&quot@+id/myFragment&quot >
     <argument
         android:name=&quotmyArg&quot
         app:argType=&quotinteger&quot
         android:defaultValue=&quot0&quot />
 </fragment>

این دیتا ورودی یک اسم دارد, نوع دارد که در app:argType مشخص شده و یک مقدار پیشفرض اختیاری دارد.

راه پیشنهادی گوگل برای تبادل دیتا استفاده از پلاگین safe args است که در یک مقاله جداگانه به ان میپردازیم.


برای اینکه مسلط تر بشید پیشنهاد میکنم داکیومنت ها navigation رو بخونید و یکم تمرین کنید. در قسمت بعد یکم مطالب پیشرفته تر میگیم و پلاگین safe args رو بررسی میکنیم و ...