مؤلفه پیمایش کتابخانهای است که میتواند پیمایش پیچیده، پویانمایی انتقال، پیوند عمیق، و ارسال آرگومان بررسیشده در زمان ترجمه را بین صفحههای برنامهتان مدیریت کند.
این سند بهعنوان راهنمای عمومی برای انتقال برنامه موجود برای استفاده از مؤلفه «پیمایش» عمل میکند.
در سطح بالا، انتقال شامل این مراحل است:
منطق واسط کاربر مختص صفحهنمایش را از فعالیتها خارج کنید - منطق واسط کاربر برنامهتان را از فعالیتها خارج کنید و مطمئن شوید که هر فعالیت فقط منطق مؤلفههای واسط کاربر پیمایش سراسری را دارد، مانند
Toolbar، درحالیکه پیادهسازی هر صفحهنمایش را به یک قطعه یا مقصد سفارشی واگذار میکند.ادغام مؤلفه پیمایش - برای هر فعالیت، یک نمودار پیمایش بسازید که شامل یک یا چند قطعه مدیریتشده توسط آن فعالیت باشد. تراکنشهای قطعه را با عملیات مؤلفه پیمایش جایگزین کنید.
افزودن مقصدهای فعالیت - تماسهای
startActivity()را با کنشهایی که از مقصدهای فعالیت استفاده میکنند جایگزین کنید.ترکیب کردن فعالیتها - ترکیب کردن نمودارهای پیمایش در مواردی که چند فعالیت دارای چیدمان مشترک هستند.
پیشنیازها
این راهنما فرض میکند که قبلاً برنامهتان را برای استفاده از کتابخانههای AndroidX منتقل کردهاید. اگر این کار را نکردهاید، قبلاز ادامه دادن، پروژهتان را منتقل کنید تا از AndroidX استفاده کند.
منتقل کردن منطق واسط کاربر مختص صفحهنمایش به خارج از فعالیتها
فعالیتها مؤلفههای سطح سیستم هستند که تعامل گرافیکی بین برنامه شما و Android را تسهیل میکنند. فعالیتها در مانیفست برنامهتان ثبت میشوند تا Android بداند کدام فعالیتها برای راهاندازی دردسترس هستند. کلاس فعالیت همچنین به برنامه شما امکان میدهد به تغییرات Android واکنش نشان دهد، مثلاً وقتی واسط کاربر برنامهتان وارد پیشنما میشود یا از آن خارج میشود، میچرخد، و غیره. فعالیت همچنین میتواند بهعنوان مکانی برای همرسانی وضعیت بین صفحهها عمل کند.
در بافت برنامه شما، فعالیتها باید بهعنوان میزبان پیمایش عمل کنند و باید منطق و دانش نحوه انتقال بین صفحهها، ارسال داده، و غیره را داشته باشند. بااینحال، بهتر است مدیریت جزئیات میانای کاربری را به بخش کوچکتر و قابلاستفاده مجدد میانای کاربری بسپارید. پیادهسازی توصیهشده برای این الگو قطعهها است. برای کسب اطلاعات بیشتر درباره مزایای استفاده از قطعهها، فعالیت تکی: چرا، چه زمانی، و چگونه را ببینید. «پیمایش» ازطریق وابستگی navigation-fragment از قطعهها پشتیبانی میکند. ناوبری از انواع مقصد سفارشی نیز پشتیبانی میکند.
اگر برنامهتان از قطعهها استفاده نمیکند، اولین کاری که باید انجام دهید این است که هر صفحه در برنامهتان را برای استفاده از قطعه انتقال دهید. در این مرحله، فعالیت را برنمیدارید. بلکه، شما درحال ایجاد قطعهای برای نمایش صفحهنمایش و تقسیم منطق میانای کاربر خود براساس مسئولیت هستید.
معرفی قطعهها
برای نشان دادن فرایند معرفی قطعهها، بیایید با مثالی از برنامهای شروع کنیم که از دو صفحه تشکیل شده است: صفحه فهرست محصول و صفحه جزئیات محصول. با کلیک کردن روی محصولی در صفحه فهرست، کاربر به صفحه جزئیات هدایت میشود تا اطلاعات بیشتری درباره محصول کسب کند.

در این مثال، صفحههای فهرست و جزئیات درحالحاضر فعالیتهای جداگانهای هستند.
ایجاد کردن چیدمان جدید برای میزبانی واسط کاربر
برای معرفی قطعه، با ایجاد فایل چیدمان جدیدی برای فعالیتی که قطعه را میزبانی میکند شروع کنید. این کار جایگزین چیدمان نمای محتوای فعلی فعالیت میشود.
برای نمای ساده، میتوانید از FrameLayout استفاده کنید، همانطور که در مثال زیر نشان داده شده است product_list_host:
<FrameLayout
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/main_content"
android:layout_height="match_parent"
android:layout_width="match_parent" />
مشخصه id به بخش محتوایی اشاره دارد که بعداً قطعه را به آن اضافه میکنیم.
سپس، در تابع onCreate() فعالیتتان، مرجع فایل چیدمان را در تابع onCreate فعالیتتان اصلاح کنید تا به این فایل چیدمان جدید اشاره کند:
کاتلین
class ProductListActivity : AppCompatActivity() { ... override fun onCreate(savedInstanceState: Bundle?) { ... // Replace setContentView(R.layout.product_list) with the line below setContentView(R.layout.product_list_host) ... } }
جاوا
public class ProductListActivity extends AppCompatActivity { ... @Override public void onCreate(@Nullable Bundle savedInstanceState) { ... // Replace setContentView(R.layout.product_list); with the line below setContentView(R.layout.product_list_host); ... } }
از چیدمان موجود (product_list، در این مثال) بهعنوان نمای ریشه
برای قطعهای که میخواهید ایجاد کنید استفاده میشود.
ایجاد قطعه
قطعه جدیدی برای مدیریت واسط کاربر صفحهنمایشتان ایجاد کنید. بهتر است نام میزبان فعالیتتان را تغییر ندهید. تکهکد زیر از
ProductListFragment استفاده میکند، برای مثال:
کاتلین
class ProductListFragment : Fragment() { // Leave empty for now. }
جاوا
public class ProductListFragment extends Fragment { // Leave empty for now. }
انتقال منطق فعالیت به قطعه
با تعریف قطعه در جای خود، گام بعدی انتقال منطق واسط کاربر برای
این صفحه از فعالیت به این قطعه جدید است. اگر از معماری مبتنی بر فعالیت استفاده میکنید، احتمالاً منطق ایجاد نمای زیادی در تابع onCreate() فعالیت شما اتفاق میافتد.
در اینجا نمونهای از صفحه مبتنی بر فعالیت با منطق واسط کاربر آورده شده است که باید منتقل کنیم:
کاتلین
class ProductListActivity : AppCompatActivity() { // Views and/or ViewDataBinding references, Adapters... private lateinit var productAdapter: ProductAdapter private lateinit var binding: ProductListActivityBinding ... // ViewModels, System Services, other Dependencies... private val viewModel: ProductListViewModel by viewModels() ... override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // View initialization logic DataBindingUtil.setContentView(this, R.layout.product_list_activity) // Post view initialization logic // Connect adapters productAdapter = ProductAdapter(productClickCallback) binding.productsList.setAdapter(productAdapter) // Initialize view properties, set click listeners, etc. binding.productsSearchBtn.setOnClickListener {...} // Subscribe to state viewModel.products.observe(this, Observer { myProducts -> ... }) // ...and so on } ... }
جاوا
public class ProductListActivity extends AppCompatActivity { // Views and/or ViewDataBinding references, adapters... private ProductAdapter productAdapter; private ProductListActivityBinding binding; ... // ViewModels, system services, other dependencies... private ProductListViewModel viewModel; ... @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); // View initialization logic DataBindingUtil.setContentView(this, R.layout.product_list_activity); // Post view initialization logic // Connect adapters productAdapter = new ProductAdapter(productClickCallback); binding.productsList.setAdapter(productAdapter); // Initialize ViewModels and other dependencies ProductListViewModel viewModel = new ViewModelProvider(this).get(ProductListViewModel.java); // Initialize view properties, set click listeners, etc. binding.productsSearchBtn.setOnClickListener(v -> { ... }); // Subscribe to state viewModel.getProducts().observe(this, myProducts -> ... ); // ...and so on }
فعالیت شما ممکن است زمان و نحوه پیمایش کاربر به صفحه بعدی را نیز کنترل کند، همانطور که در مثال زیر نشان داده شده است:
کاتلین
// Provided to ProductAdapter in ProductListActivity snippet. private val productClickCallback = ProductClickCallback { product -> show(product) } fun show(product: Product) { val intent = Intent(this, ProductActivity::class.java) intent.putExtra(ProductActivity.KEY_PRODUCT_ID, product.id) startActivity(intent) }
جاوا
// Provided to ProductAdapter in ProductListActivity snippet. private ProductClickCallback productClickCallback = this::show; private void show(Product product) { Intent intent = new Intent(this, ProductActivity.class); intent.putExtra(ProductActivity.KEY_PRODUCT_ID, product.getId()); startActivity(intent); }
در داخل قطعه، این کار را بین
onCreateView()
و
onViewCreated()
توزیع میکنید و فقط منطق پیمایش در فعالیت باقی میماند:
کاتلین
class ProductListFragment : Fragment() { private lateinit var binding: ProductListFragmentBinding private val viewModel: ProductListViewModel by viewModels() // View initialization logic override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { binding = DataBindingUtil.inflate( inflater, R.layout.product_list, container, false ) return binding.root } // Post view initialization logic override fun onViewCreated(view: View, savedInstanceState: Bundle?) { // Connect adapters productAdapter = ProductAdapter(productClickCallback) binding.productsList.setAdapter(productAdapter) // Initialize view properties, set click listeners, etc. binding.productsSearchBtn.setOnClickListener {...} // Subscribe to state viewModel.products.observe(this, Observer { myProducts -> ... }) // ...and so on } // Provided to ProductAdapter private val productClickCallback = ProductClickCallback { product -> if (lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED)) { (requireActivity() as ProductListActivity).show(product) } } ... }
جاوا
public class ProductListFragment extends Fragment { private ProductAdapter productAdapter; private ProductListFragmentBinding binding; // View initialization logic @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { binding = DataBindingUtil.inflate( inflater, R.layout.product_list_fragment, container, false); return binding.getRoot(); } // Post view initialization logic @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { // Connect adapters binding.productsList.setAdapter(productAdapter); // Initialize ViewModels and other dependencies ProductListViewModel viewModel = new ViewModelProvider(this) .get(ProductListViewModel.class); // Initialize view properties, set click listeners, etc. binding.productsSearchBtn.setOnClickListener(...) // Subscribe to state viewModel.getProducts().observe(this, myProducts -> { ... }); // ...and so on // Provided to ProductAdapter private ProductClickCallback productClickCallback = new ProductClickCallback() { @Override public void onClick(Product product) { if (getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.STARTED)) { ((ProductListActivity) requireActivity()).show(product); } } }; ... }
در ProductListFragment، توجه داشته باشید که هیچ تماسی با
setContentView()
برای گسترش و اتصال چیدمان وجود ندارد. در یک قطعه، onCreateView() نمای ریشه را مقداردهی اولیه میکند. onCreateView() نمونهای از
LayoutInflater را میگیرد که میتواند برای
گسترش نمای ریشه براساس فایل منبع چیدمان استفاده شود. این مثال از طرحبندی product_list موجود که توسط فعالیت استفاده شده بود مجدداً استفاده میکند، زیرا هیچ چیزی در خود طرحبندی نیاز به تغییر ندارد.
اگر منطق واسط کاربری در توابع onStart()، onResume()، onPause()، یا onStop() فعالیت شما وجود دارد که به پیمایش مربوط نیست، میتوانید آنها را به توابع متناظر با همان نام در قطعه منتقل کنید.
مقداردهی اولیه قطعه در فعالیت میزبان
وقتی همه منطق میانای کاربر را به قطعه منتقل کردید، فقط منطق پیمایش باید در فعالیت باقی بماند.
کاتلین
class ProductListActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.product_list_host) } fun show(product: Product) { val intent = Intent(this, ProductActivity::class.java) intent.putExtra(ProductActivity.KEY_PRODUCT_ID, product.id) startActivity(intent) } }
جاوا
public class ProductListActivity extends AppCompatActivity { @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.product_list_host); } public void show(Product product) { Intent intent = new Intent(this, ProductActivity.class); intent.putExtra(ProductActivity.KEY_PRODUCT_ID, product.getId()); startActivity(intent); } }
مرحله آخر ایجاد نمونهای از قطعه در onCreate() است، درست
پساز تنظیم نمای محتوا:
کاتلین
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.product_list_host) if (savedInstanceState == null) { val fragment = ProductListFragment() supportFragmentManager .beginTransaction() .add(R.id.main_content, fragment) .commit() } }
جاوا
@Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.product_list_host); if (savedInstanceState == null) { ProductListFragment fragment = new ProductListFragment(); getSupportFragmentManager() .beginTransaction() .add(R.id.main_content, fragment) .commit(); } }
همانطور که در این مثال نشان داده شده است، FragmentManager بهطور خودکار تکهها را در تغییرات پیکربندی ذخیره و بازیابی میکند، بنابراین فقط درصورتی باید تکه را اضافه کنید که savedInstanceState تهی باشد.
انتقال موارد اضافی قصد به قطعه
اگر فعالیت شما Extras را ازطریق یک هدف دریافت میکند، میتوانید اینها را مستقیماً بهعنوان آرگومان به قطعه ارسال کنید.
در این مثال، ProductDetailsFragment آرگومانهایش را مستقیماً از موارد اضافی هدف فعالیت دریافت میکند:
کاتلین
... if (savedInstanceState == null) { val fragment = ProductDetailsFragment() // Intent extras and Fragment Args are both of type android.os.Bundle. fragment.arguments = intent.extras supportFragmentManager .beginTransaction() .add(R.id.main_content, fragment) .commit() } ...
جاوا
... if (savedInstanceState == null) { ProductDetailsFragment fragment = new ProductDetailsFragment(); // Intent extras and fragment Args are both of type android.os.Bundle. fragment.setArguments(getIntent().getExtras()); getSupportFragmentManager() .beginTransaction() .add(R.id.main_content, fragment) .commit(); } ...
در این مرحله، باید بتوانید اجرای برنامهتان را با صفحه اول بهروزرسانیشده برای استفاده از قطعه آزمایش کنید. به انتقال بقیه صفحههای مبتنی بر فعالیت ادامه دهید و پساز هر تکرار، زمانی را به آزمایش اختصاص دهید.
ادغام مؤلفه «ناوبری»
وقتی از معماری مبتنی بر قطعه استفاده میکنید، آماده شروع ادغام مؤلفه «ناوبری» هستید.
ابتدا، جدیدترین وابستگیهای Navigation را با دنبال کردن دستورالعملهای موجود در یادداشتهای انتشار کتابخانه Navigation به پروژهتان اضافه کنید.
ایجاد نمودار پیمایش
مؤلفه «ناوبری» پیکربندی ناوبری برنامهتان را در یک فایل منبع بهصورت نمودار نشان میدهد، درست مانند نحوه نمایش نماهای برنامهتان. این کار کمک میکند پیمایش برنامهتان خارج از پایگاه کدتان سازماندهی شود و راهی برای ویرایش پیمایش برنامهتان بهصورت بصری فراهم میکند.
برای ایجاد نمودار پیمایش، ابتدا پوشه منبع جدیدی بهنام
navigation ایجاد کنید. برای افزودن نمودار، روی این دایرکتوری کلیک راست کنید و
جدید > فایل منبع پیمایش را انتخاب کنید.

مؤلفه «پیمایش» از فعالیت بهعنوان
میزبان پیمایش
استفاده میکند و وقتی کاربران در برنامهتان پیمایش میکنند، تکههای مجزا را در آن میزبان جابهجا میکند. قبلاز اینکه بتوانید چیدمان پیمایش برنامهتان را بهصورت بصری شروع کنید، باید NavHost را در فعالیتی که قرار است میزبان این نمودار باشد پیکربندی کنید. ازآنجاییکه از قطعهها استفاده میکنیم، میتوانیم از پیادهسازی پیشفرض NavHost مؤلفه «پیمایش» استفاده کنیم،
NavHostFragment.
NavHostFragment ازطریق FragmentContainerView پیکربندی میشود
که در داخل فعالیت میزبان قرار دارد، همانطور که در مثال زیر نشان داده شده است:
<androidx.fragment.app.FragmentContainerView
android:name="androidx.navigation.fragment.NavHostFragment"
app:navGraph="@navigation/product_list_graph"
app:defaultNavHost="true"
android:id="@+id/main_content"
android:layout_width="match_parent"
android:layout_height="match_parent" />
مشخصه app:NavGraph به نمودار ناوبری منسوب به این میزبان ناوبری اشاره میکند. تنظیم این خصوصیت نمودار پیمایش را گسترش میدهد و خصوصیت نمودار را روی NavHostFragment تنظیم میکند. مشخصه app:defaultNavHost تضمین میکند که NavHostFragment شما دکمه «برگشت» سیستم را رهگیری میکند.
اگر از پیمایش سطح بالا مانند DrawerLayout یا
BottomNavigationView استفاده میکنید، این FragmentContainerView
جایگزین عنصر نمای محتوای اصلی شما میشود. برای مشاهده نمونهها،
بهروزرسانی مؤلفههای میانای کاربر با NavigationUI
را ببینید.
برای چیدمان ساده، میتوانید این عنصر FragmentContainerView را بهعنوان فرزند ریشه ViewGroup اضافه کنید:
<FrameLayout
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="match_parent"
android:layout_width="match_parent">
<androidx.fragment.app.FragmentContainerView
android:id="@+id/main_content"
android:name="androidx.navigation.fragment.NavHostFragment"
app:navGraph="@navigation/product_list_graph"
app:defaultNavHost="true"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
اگر روی برگه طراحی در پایین کلیک کنید، باید نموداری مشابه
نمودار نشاندادهشده در زیر ببینید. در گوشه بالا سمت راست نمودار، زیر
مقصدها، میتوانید ارجاعی به فعالیت NavHost را در قالب
layout_name (resource_id) ببینید.

روی دکمه بهعلاوه
در نزدیکی بالا کلیک کنید تا قطعههایتان را به این نمودار اضافه کنید.
مؤلفه «پیمایش» به صفحهنمایشهای تکی بهعنوان مقصد اشاره میکند. مقصدها میتوانند قطعه، فعالیت، یا مقصد سفارشی باشند. میتوانید هر نوع مقصدی را به نمودارتان اضافه کنید، اما توجه داشته باشید که مقصدهای فعالیت مقصدهای پایانی درنظر گرفته میشوند، زیرا وقتی به مقصد فعالیت پیمایش میکنید، در میزبان و نمودار پیمایش جداگانهای فعالیت میکنید.
مؤلفه «پیمایش» به روشی اشاره دارد که کاربران ازطریق آن از یک مقصد به مقصد دیگر میروند، که به آن کنشها میگویند. کنشها همچنین میتوانند پویانماییهای انتقال و رفتار پاپ را توصیف کنند.
برداشتن تراکنشهای قطعهای
اکنون که از مؤلفه «پیمایش» استفاده میکنید، اگر بین صفحههای مبتنی بر قطعه تحت یک فعالیت پیمایش میکنید، میتوانید تعاملات
FragmentManager
را بردارید.
اگر برنامهتان از چند قطعه تحت یک فعالیت یا پیمایش سطح بالا مثل چیدمان کشویی یا پیمایش پایین استفاده میکند، احتمالاً از FragmentManager و FragmentTransactions برای افزودن یا جایگزین کردن قطعهها در بخش محتوای اصلی واسط کاربرتان استفاده میکنید. اکنون میتوان این را بااستفاده از مؤلفه «ناوبری» جایگزین و ساده کرد، بهاینصورت که کنشهایی برای پیوند دادن مقصدها در نمودار ارائه میشود و سپس بااستفاده از NavController پیمایش میشود.
در اینجا چند سناریو که ممکن است با آنها مواجه شوید بههمراه نحوه برخورد با انتقال برای هر سناریو آورده شده است.
فعالیت تکی که چندین قطعه را مدیریت میکند
اگر یک فعالیت دارید که چندین قطعه را مدیریت میکند، کد فعالیت شما ممکن است شبیه این باشد:
کاتلین
class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) // Logic to load the starting destination // when the Activity is first created if (savedInstanceState == null) { val fragment = ProductListFragment() supportFragmentManager.beginTransaction() .add(R.id.fragment_container, fragment, ProductListFragment.TAG) .commit() } } // Logic to navigate the user to another destination. // This may include logic to initialize and set arguments on the destination // fragment or even transition animations between the fragments (not shown here). fun navigateToProductDetail(productId: String) { val fragment = new ProductDetailsFragment() val args = Bundle().apply { putInt(KEY_PRODUCT_ID, productId) } fragment.arguments = args supportFragmentManager.beginTransaction() .addToBackStack(ProductDetailsFragment.TAG) .replace(R.id.fragment_container, fragment, ProductDetailsFragment.TAG) .commit() } }
جاوا
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // Logic to load the starting destination when the activity is first created. if (savedInstanceState == null) { val fragment = ProductListFragment() supportFragmentManager.beginTransaction() .add(R.id.fragment_container, fragment, ProductListFragment.TAG) .commit(); } } // Logic to navigate the user to another destination. // This may include logic to initialize and set arguments on the destination // fragment or even transition animations between the fragments (not shown here). public void navigateToProductDetail(String productId) { Fragment fragment = new ProductDetailsFragment(); Bundle args = new Bundle(); args.putInt(KEY_PRODUCT_ID, productId); fragment.setArguments(args); getSupportFragmentManager().beginTransaction() .addToBackStack(ProductDetailsFragment.TAG) .replace(R.id.fragment_container, fragment, ProductDetailsFragment.TAG) .commit(); } }
در مقصد منبع، ممکن است در پاسخ به رویدادی، تابع پیمایش را فراخوانی کنید، همانطور که در زیر نشان داده شده است:
کاتلین
class ProductListFragment : Fragment() { ... override fun onViewCreated(view: View, savedInstanceState: Bundle?) { // In this example a callback is passed to respond to an item clicked // in a RecyclerView productAdapter = ProductAdapter(productClickCallback) binding.productsList.setAdapter(productAdapter) } ... // The callback makes the call to the activity to make the transition. private val productClickCallback = ProductClickCallback { product -> (requireActivity() as MainActivity).navigateToProductDetail(product.id) } }
جاوا
public class ProductListFragment extends Fragment { ... @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { // In this example a callback is passed to respond to an item clicked in a RecyclerView productAdapter = new ProductAdapter(productClickCallback); binding.productsList.setAdapter(productAdapter); } ... // The callback makes the call to the activity to make the transition. private ProductClickCallback productClickCallback = product -> ( ((MainActivity) requireActivity()).navigateToProductDetail(product.getId()) ); }
این را میتوان با بهروزرسانی نمودار ناوبری برای تنظیم مقصد شروع و کنشها برای پیوند دادن مقصدها و تعریف کردن شناسه درصورت نیاز جایگزین کرد:
<navigation 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"
android:id="@+id/product_list_graph"
app:startDestination="@id/product_list">
<fragment
android:id="@+id/product_list"
android:name="com.example.android.persistence.ui.ProductListFragment"
android:label="Product List"
tools:layout="@layout/product_list">
<action
android:id="@+id/navigate_to_product_detail"
app:destination="@id/product_detail" />
</fragment>
<fragment
android:id="@+id/product_detail"
android:name="com.example.android.persistence.ui.ProductDetailFragment"
android:label="Product Detail"
tools:layout="@layout/product_detail">
<argument
android:name="product_id"
app:argType="integer" />
</fragment>
</navigation>
سپس میتوانید فعالیتتان را بهروز کنید:
کاتلین
class MainActivity : AppCompatActivity() { // No need to load the start destination, handled automatically by the Navigation component override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) } }
جاوا
public class MainActivity extends AppCompatActivity { // No need to load the start destination, handled automatically by the Navigation component @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } }
این فعالیت دیگر به روش navigateToProductDetail() نیاز ندارد. در بخش بعدی، ProductListFragment را بهروز میکنیم تا از NavController برای پیمایش به صفحه جزئیات محصول بعدی استفاده کند.
ارسال ایمن متغیرهای مستقل
مؤلفه Navigation دارای افزونه Gradle بهنام Safe Args است که کلاسهای سازنده و شیء سادهای را برای دسترسی ایمن تایپی به آرگومانهای مشخصشده برای مقصدها و کنشها تولید میکند.
وقتی افزونه اعمال شد، هر آرگومانی که در مقصد در نمودار ناوبری شما تعریف شده باشد باعث میشود چارچوب مؤلفه Navigation کلاس Arguments تولید کند که آرگومانهای ایمن تایپی را برای مقصد هدف ارائه میدهد.
تعریف کردن کنش باعث میشود افزونه کلاس پیکربندی Directions را تولید کند که میتوان از آن برای گفتن به NavController درباره نحوه هدایت کاربر به مقصد هدف استفاده کرد. وقتی کنشی به مقصدی اشاره میکند که به آرگومان نیاز دارد، کلاس Directions تولیدشده شامل روشهای سازندهای است که به آن پارامترها نیاز دارند.
Inside the fragment, use NavController and the generated Directions class to
provide type-safe arguments to the target destination, as shown in the following
example:
کاتلین
class ProductListFragment : Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { // In this example a callback is passed to respond to an item clicked in a RecyclerView productAdapter = ProductAdapter(productClickCallback) binding.productsList.setAdapter(productAdapter) } ... // The callback makes the call to the NavController to make the transition. private val productClickCallback = ProductClickCallback { product -> val directions = ProductListDirections.navigateToProductDetail(product.id) findNavController().navigate(directions) } }
جاوا
public class ProductListFragment extends Fragment { ... @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { // In this example a callback is passed to respond to an item clicked in a RecyclerView productAdapter = new ProductAdapter(productClickCallback); binding.productsList.setAdapter(productAdapter); } ... // The callback makes the call to the activity to make the transition. private ProductClickCallback productClickCallback = product -> { ProductListDirections.ViewProductDetails directions = ProductListDirections.navigateToProductDetail(product.getId()); NavHostFragment.findNavController(this).navigate(directions); }; }
پیمایش سطح بالا
اگر برنامهتان از DrawerLayout استفاده میکند، ممکن است منطق پیکربندی زیادی در فعالیتتان داشته باشید که باز و بسته کردن کشو و پیمایش به مقصدهای دیگر را مدیریت میکند.
فعالیت حاصل ممکن است چیزی شبیه به این باشد:
کاتلین
class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelectedListener { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val toolbar: Toolbar = findViewById(R.id.toolbar) setSupportActionBar(toolbar) val drawerLayout: DrawerLayout = findViewById(R.id.drawer_layout) val navView: NavigationView = findViewById(R.id.nav_view) val toggle = ActionBarDrawerToggle( this, drawerLayout, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close ) drawerLayout.addDrawerListener(toggle) toggle.syncState() navView.setNavigationItemSelectedListener(this) } override fun onBackPressed() { val drawerLayout: DrawerLayout = findViewById(R.id.drawer_layout) if (drawerLayout.isDrawerOpen(GravityCompat.START)) { drawerLayout.closeDrawer(GravityCompat.START) } else { super.onBackPressed() } } override fun onNavigationItemSelected(item: MenuItem): Boolean { // Handle navigation view item clicks here. when (item.itemId) { R.id.home -> { val homeFragment = HomeFragment() show(homeFragment) } R.id.gallery -> { val galleryFragment = GalleryFragment() show(galleryFragment) } R.id.slide_show -> { val slideShowFragment = SlideShowFragment() show(slideShowFragment) } R.id.tools -> { val toolsFragment = ToolsFragment() show(toolsFragment) } } val drawerLayout: DrawerLayout = findViewById(R.id.drawer_layout) drawerLayout.closeDrawer(GravityCompat.START) return true } } private fun show(fragment: Fragment) { val drawerLayout = drawer_layout as DrawerLayout val fragmentManager = supportFragmentManager fragmentManager .beginTransaction() .replace(R.id.main_content, fragment) .commit() drawerLayout.closeDrawer(GravityCompat.START) }
جاوا
public class MainActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Toolbar toolbar = findViewById(R.id.toolbar); setSupportActionBar(toolbar); DrawerLayout drawer = findViewById(R.id.drawer_layout); NavigationView navigationView = findViewById(R.id.nav_view); ActionBarDrawerToggle toggle = new ActionBarDrawerToggle( this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close); drawer.addDrawerListener(toggle); toggle.syncState(); navigationView.setNavigationItemSelectedListener(this); } @Override public void onBackPressed() { DrawerLayout drawer = findViewById(R.id.drawer_layout); if (drawer.isDrawerOpen(GravityCompat.START)) { drawer.closeDrawer(GravityCompat.START); } else { super.onBackPressed(); } } @Override public boolean onNavigationItemSelected(MenuItem item) { // Handle navigation view item clicks here. int id = item.getItemId(); if (id == R.id.home) { Fragment homeFragment = new HomeFragment(); show(homeFragment); } else if (id == R.id.gallery) { Fragment galleryFragment = new GalleryFragment(); show(galleryFragment); } else if (id == R.id.slide_show) { Fragment slideShowFragment = new SlideShowFragment(); show(slideShowFragment); } else if (id == R.id.tools) { Fragment toolsFragment = new ToolsFragment(); show(toolsFragment); } DrawerLayout drawer = findViewById(R.id.drawer_layout); drawer.closeDrawer(GravityCompat.START); return true; } private void show(Fragment fragment) { DrawerLayout drawerLayout = findViewById(R.id.drawer_layout); FragmentManager fragmentManager = getSupportFragmentManager(); fragmentManager .beginTransaction() .replace(R.id.main_content, fragment) .commit(); drawerLayout.closeDrawer(GravityCompat.START); } }
پساز افزودن مؤلفه «پیمایش» به پروژه و ایجاد نمودار پیمایش، هریک از مقصدهای محتوا را از نمودارتان اضافه کنید (مانند صفحه اصلی، گالری، نمایش اسلاید، و ابزارها از مثال بالا). مطمئن شوید
که مقادیر id گزینه منو با مقادیر مقصد id مرتبط با آنها مطابقت داشته باشد،
همانطور که در زیر نشان داده شده است:
<!-- activity_main_drawer.xml -->
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
tools:showIn="navigation_view">
<group android:checkableBehavior="single">
<item
android:id="@+id/home"
android:icon="@drawable/ic_menu_camera"
android:title="@string/menu_home" />
<item
android:id="@+id/gallery"
android:icon="@drawable/ic_menu_gallery"
android:title="@string/menu_gallery" />
<item
android:id="@+id/slide_show"
android:icon="@drawable/ic_menu_slideshow"
android:title="@string/menu_slideshow" />
<item
android:id="@+id/tools"
android:icon="@drawable/ic_menu_manage"
android:title="@string/menu_tools" />
</group>
</menu>
<!-- activity_main_graph.xml -->
<navigation 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"
android:id="@+id/main_graph"
app:startDestination="@id/home">
<fragment
android:id="@+id/home"
android:name="com.example.HomeFragment"
android:label="Home"
tools:layout="@layout/home" />
<fragment
android:id="@+id/gallery"
android:name="com.example.GalleryFragment"
android:label="Gallery"
tools:layout="@layout/gallery" />
<fragment
android:id="@+id/slide_show"
android:name="com.example.SlideShowFragment"
android:label="Slide Show"
tools:layout="@layout/slide_show" />
<fragment
android:id="@+id/tools"
android:name="com.example.ToolsFragment"
android:label="Tools"
tools:layout="@layout/tools" />
</navigation>
اگر مقادیر id را از منو و نمودار مطابقت دهید، میتوانید
NavController را برای این فعالیت سیمکشی کنید تا ناوبری را بهطور خودکار براساس
مورد منو مدیریت کند. NavController همچنین باز و بسته کردن
DrawerLayout و مدیریت رفتار دکمههای «بالا» و «برگشت» را بهدرستی انجام میدهد.
سپس MainActivity شما میتواند بهروز شود تا NavController را به
Toolbar و NavigationView متصل کند.
برای نمونه، تکهکد زیر را ببینید:
کاتلین
class MainActivity : AppCompatActivity() { val drawerLayout by lazy { findViewById<DrawerLayout>(R.id.drawer_layout) } val navController by lazy { (supportFragmentManager.findFragmentById(R.id.main_content) as NavHostFragment).navController } val navigationView by lazy { findViewById<NavigationView>(R.id.nav_view) } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val toolbar = findViewById<Toolbar>(R.id.toolbar) setSupportActionBar(toolbar) // Show and Manage the Drawer and Back Icon setupActionBarWithNavController(navController, drawerLayout) // Handle Navigation item clicks // This works with no further action on your part if the menu and destination id’s match. navigationView.setupWithNavController(navController) } override fun onSupportNavigateUp(): Boolean { // Allows NavigationUI to support proper up navigation or the drawer layout // drawer menu, depending on the situation return navController.navigateUp(drawerLayout) } }
جاوا
public class MainActivity extends AppCompatActivity { private DrawerLayout drawerLayout; private NavController navController; private NavigationView navigationView; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); drawerLayout = findViewById(R.id.drawer_layout); NavHostFragment navHostFragment = (NavHostFragment) getSupportFragmentManager().findFragmentById(R.id.main_content); navController = navHostFragment.getNavController(); navigationView = findViewById(R.id.nav_view); Toolbar toolbar = findViewById(R.id.toolbar); setSupportActionBar(toolbar); // Show and Manage the Drawer and Back Icon NavigationUI.setupActionBarWithNavController(this, navController, drawerLayout); // Handle Navigation item clicks // This works with no further action on your part if the menu and destination id’s match. NavigationUI.setupWithNavController(navigationView, navController); } @Override public boolean onSupportNavigateUp() { // Allows NavigationUI to support proper up navigation or the drawer layout // drawer menu, depending on the situation. return NavigationUI.navigateUp(navController, drawerLayout); } }
میتوانید از همین تکنیک هم برای پیمایش مبتنی بر BottomNavigationView و هم برای پیمایش مبتنی بر منو استفاده کنید. برای مثالهای بیشتر، بهروزرسانی مؤلفههای واسط کاربر با NavigationUI را ببینید.
افزودن مقصدهای فعالیت
وقتی هر صفحه در برنامهتان برای استفاده از مؤلفه «پیمایش» سیمکشی شد، و
دیگر از FragmentTransactions برای انتقال بین
مقصدهای مبتنی بر قطعه استفاده نمیکنید، مرحله بعدی حذف
فراخوانیهای startActivity است.
ابتدا، مکانهایی را در برنامهتان شناسایی کنید که دو گراف ناوبری جداگانه دارید
و از startActivity برای انتقال بین آنها استفاده میکنید.
این مثال شامل دو نمودار (A و B) و یک فراخوانی startActivity() برای انتقال از A به B است.

کاتلین
fun navigateToProductDetails(productId: String) { val intent = Intent(this, ProductDetailsActivity::class.java) intent.putExtra(KEY_PRODUCT_ID, productId) startActivity(intent) }
جاوا
private void navigateToProductDetails(String productId) { Intent intent = new Intent(this, ProductDetailsActivity.class); intent.putExtra(KEY_PRODUCT_ID, productId); startActivity(intent);
سپس، اینها را با مقصد فعالیتی در «نمودار الف» که نشاندهنده پیمایش به فعالیت میزبان «نمودار ب» است جایگزین کنید. اگر آرگومانهایی دارید که باید به مقصد شروع «نمودار ب» ارسال شود، میتوانید آنها را در تعریف مقصد فعالیت تعیین کنید.
در مثال زیر، «گراف الف» مقصد فعالیتی را تعریف میکند که آرگومان product_id را بههمراه کنشی دریافت میکند. نمودار «ب» تغییری ندارد.

نمایش XML نمودارهای الف و ب ممکن است به این شکل باشد:
<!-- Graph A -->
<navigation 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"
android:id="@+id/product_list_graph"
app:startDestination="@id/product_list">
<fragment
android:id="@+id/product_list"
android:name="com.example.android.persistence.ui.ProductListFragment"
android:label="Product List"
tools:layout="@layout/product_list_fragment">
<action
android:id="@+id/navigate_to_product_detail"
app:destination="@id/product_details_activity" />
</fragment>
<activity
android:id="@+id/product_details_activity"
android:name="com.example.android.persistence.ui.ProductDetailsActivity"
android:label="Product Details"
tools:layout="@layout/product_details_host">
<argument
android:name="product_id"
app:argType="integer" />
</activity>
</navigation>
<!-- Graph B -->
<navigation 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"
app:startDestination="@id/product_details">
<fragment
android:id="@+id/product_details"
android:name="com.example.android.persistence.ui.ProductDetailsFragment"
android:label="Product Details"
tools:layout="@layout/product_details_fragment">
<argument
android:name="product_id"
app:argType="integer" />
</fragment>
</navigation>
میتوانید بااستفاده از همان سازوکارهایی که برای پیمایش به مقصدهای قطعه استفاده میکنید، به فعالیت میزبان «نمودار ب» پیمایش کنید:
کاتلین
fun navigateToProductDetails(productId: String) { val directions = ProductListDirections.navigateToProductDetail(productId) findNavController().navigate(directions) }
جاوا
private void navigateToProductDetails(String productId) { ProductListDirections.NavigateToProductDetail directions = ProductListDirections.navigateToProductDetail(productId); Navigation.findNavController(getView()).navigate(directions);
آرگومانهای مقصد فعالیت را به قطعه مقصد شروع ارسال کنید
اگر فعالیت مقصد موارد اضافی دریافت کند، مانند مثال قبلی، میتوانید این موارد را مستقیماً بهعنوان آرگومان به مقصد شروع ارسال کنید، اما باید نمودار پیمایش میزبان خود را بهصورت دستی در متد onCreate() فعالیت میزبان تنظیم کنید تا بتوانید موارد اضافی intent را بهعنوان آرگومان به قطعه ارسال کنید، همانطور که در زیر نشان داده شده است:
کاتلین
class ProductDetailsActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.product_details_host) val navHostFragment = supportFragmentManager.findFragmentById(R.id.main_content) as NavHostFragment val navController = navHostFragment.navController navController .setGraph(R.navigation.product_detail_graph, intent.extras) } }
جاوا
public class ProductDetailsActivity extends AppCompatActivity { @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.product_details_host); NavHostFragment navHostFragment = (NavHostFragment) getSupportFragmentManager().findFragmentById(R.id.main_content); NavController navController = navHostFragment.getNavController(); navController .setGraph(R.navigation.product_detail_graph, getIntent().getExtras()); } }
دادهها را میتوان بااستفاده از کلاس آرگومانهای تولیدشده از آرگومانهای قطعه Bundle بیرون کشید، همانطور که در مثال زیر نشان داده شده است:
کاتلین
class ProductDetailsFragment : Fragment() { val args by navArgs<ProductDetailsArgs>() override fun onViewCreated(view: View, savedInstanceState: Bundle?) { val productId = args.productId ... } ...
جاوا
public class ProductDetailsFragment extends Fragment { ProductDetailsArgs args; @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); args = ProductDetailsArgs.fromBundle(requireArguments()); } @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { int productId = args.getProductId(); ... } ...
ترکیب کردن فعالیتها
در مواردی که چند فعالیت از چیدمان یکسانی استفاده میکنند، میتوانید نمودارهای پیمایش را ترکیب کنید، مثلاً FrameLayout سادهای که حاوی یک قطعه است. در
بیشتر این موارد، میتوانید همه عناصر را از هر
نمودار پیمایش ترکیب کنید و هر عنصر مقصد فعالیت را به مقصد تکهتکه
بهروز کنید.
مثال زیر نمودارهای الف و ب از بخش قبلی را ترکیب میکند:
قبلاز ترکیب کردن:
<!-- Graph A -->
<navigation 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"
android:id="@+id/product_list_graph"
app:startDestination="@id/product_list">
<fragment
android:id="@+id/product_list"
android:name="com.example.android.persistence.ui.ProductListFragment"
android:label="Product List Fragment"
tools:layout="@layout/product_list">
<action
android:id="@+id/navigate_to_product_detail"
app:destination="@id/product_details_activity" />
</fragment>
<activity
android:id="@+id/product_details_activity"
android:name="com.example.android.persistence.ui.ProductDetailsActivity"
android:label="Product Details Host"
tools:layout="@layout/product_details_host">
<argument android:name="product_id"
app:argType="integer" />
</activity>
</navigation>
<!-- Graph B -->
<navigation 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"
android:id="@+id/product_detail_graph"
app:startDestination="@id/product_details">
<fragment
android:id="@+id/product_details"
android:name="com.example.android.persistence.ui.ProductDetailsFragment"
android:label="Product Details"
tools:layout="@layout/product_details">
<argument
android:name="product_id"
app:argType="integer" />
</fragment>
</navigation>
پساز ترکیب:
<!-- Combined Graph A and B -->
<navigation 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"
android:id="@+id/product_list_graph"
app:startDestination="@id/product_list">
<fragment
android:id="@+id/product_list"
android:name="com.example.android.persistence.ui.ProductListFragment"
android:label="Product List Fragment"
tools:layout="@layout/product_list">
<action
android:id="@+id/navigate_to_product_detail"
app:destination="@id/product_details" />
</fragment>
<fragment
android:id="@+id/product_details"
android:name="com.example.android.persistence.ui.ProductDetailsFragment"
android:label="Product Details"
tools:layout="@layout/product_details">
<argument
android:name="product_id"
app:argType="integer" />
</fragment>
</navigation>
اگر نام کنشهایتان را هنگام ادغام یکسان نگه دارید، این فرایند میتواند بهطور یکپارچه انجام شود و نیازی به تغییر در پایگاه کد موجود شما نخواهد بود. برای مثال،
navigateToProductDetail در اینجا بدون تغییر باقی میماند. تنها تفاوت این است که
این کنش اکنون نشاندهنده پیمایش به مقصد قطعه در همان
NavHost بهجای مقصد فعالیت است:
کاتلین
fun navigateToProductDetails(productId: String) { val directions = ProductListDirections.navigateToProductDetail(productId) findNavController().navigate(directions) }
جاوا
private void navigateToProductDetails(String productId) { ProductListDirections.NavigateToProductDetail directions = ProductListDirections.navigateToProductDetail(productId); Navigation.findNavController(getView()).navigate(directions);
منابع تکمیلی
برای اطلاعات بیشتر درباره ناوبری، موضوعات زیر را ببینید:
- بهروزرسانی مؤلفههای میانای کاربر با NavigationUI - با نحوه مدیریت پیمایش با نوار برنامه بالایی، کشوی پیمایش، و پیمایش پایین آشنا شوید
- آزمایش پیمایش - با نحوه آزمایش کردن گردش کار پیمایش برای برنامهتان آشنا شوید