নেভিগেশন কম্পোনেন্টের সাথে প্রোগ্রাম্যাটিক্যালি ইন্টার‍্যাক্ট করা

নেভিগেশন কম্পোনেন্ট নির্দিষ্ট কিছু নেভিগেশন এলিমেন্ট প্রোগ্রামের সাহায্যে তৈরি ও ইন্টার‍্যাক্ট করার উপায় প্রদান করে।

NavHostFragment তৈরি করুন

আপনি NavHostFragment.create() ব্যবহার করে নির্দিষ্ট গ্রাফ রিসোর্স সহ প্রোগ্রাম্যাটিক্যালি NavHostFragment তৈরি করতে পারবেন, যেমনটি নিচের উদাহরণে দেখানো হয়েছে:

Kotlin

val finalHost = NavHostFragment.create(R.navigation.example_graph)
supportFragmentManager.beginTransaction()
    .replace(R.id.nav_host, finalHost)
    .setPrimaryNavigationFragment(finalHost) // equivalent to app:defaultNavHost="true"
    .commit()

জাভা

NavHostFragment finalHost = NavHostFragment.create(R.navigation.example_graph);
getSupportFragmentManager().beginTransaction()
    .replace(R.id.nav_host, finalHost)
    .setPrimaryNavigationFragment(finalHost) // equivalent to app:defaultNavHost="true"
    .commit();

মনে রাখবেন যে setPrimaryNavigationFragment(finalHost) আপনার NavHost-কে সিস্টেমের ফিরে যান বোতাম প্রেস করা ইন্টারসেপ্ট করতে দেয়। এছাড়াও, আপনি app:defaultNavHost="true" যোগ করে আপনার NavHost XML-এ এই আচরণ প্রয়োগ করতে পারেন। আপনি যদি কাস্টম 'ফিরে যান' বোতামের আচরণ প্রয়োগ করেন এবং আপনার NavHost যাতে 'ফিরে যান' বোতাম প্রেস করাকে ইন্টারসেপ্ট না করে, তাহলে আপনি null থেকে setPrimaryNavigationFragment()-এ পাস করতে পারবেন।

Navigation 2.2.0 থেকে শুরু করে, আপনি NavController.getBackStackEntry() কল করে নেভিগেশন স্ট্যাকের যেকোনও গন্তব্যের জন্য NavBackStackEntry-এর রেফারেন্স পেতে পারেন, এতে গন্তব্য আইডি পাস করা হয়। ব্যাক স্ট্যাকে নির্দিষ্ট গন্তব্যের একাধিক ইনস্ট্যান্স থাকলে, getBackStackEntry() স্ট্যাক থেকে সবচেয়ে উপরের ইনস্ট্যান্স রিটার্ন করে।

ফেরত আসা NavBackStackEntry-এ Lifecycle, ViewModelStore এবং SavedStateRegistry ডেস্টিনেশন লেভেলে প্রদান করা হয়। ব্যাক স্ট্যাকে ডেস্টিনেশনের লাইফটাইমের জন্য এইসব অবজেক্ট বৈধ। ব্যাক স্ট্যাক থেকে সংশ্লিষ্ট ডেস্টিনেশন পপ অফ করা হলে, Lifecycle ধ্বংস হয়ে যায়, স্টেট আর সেভ করা হয় না এবং যেকোনও ViewModel অবজেক্ট মুছে ফেলা হয়।

এইসব প্রপার্টি আপনাকে Lifecycle এবং ViewModel অবজেক্ট ও ক্লাসের জন্য স্টোর দেয় যা সেভ করা স্ট্যাটাস-এর সাথে কাজ করে, আপনি যে ধরনের ডেস্টিনেশনই ব্যবহার করুন না কেন। এটি বিশেষ করে সেইসব ডেস্টিনেশনের ধরন নিয়ে কাজ করার সময় উপযোগী হয় যেগুলির সাথে অটোমেটিক কোনও Lifecycle যুক্ত থাকে না, যেমন কাস্টম ডেস্টিনেশন।

যেমন, আপনি কোনও ফ্র্যাগমেন্ট বা অ্যাক্টিভিটির Lifecycle যেভাবে পর্যবেক্ষণ করেন, ঠিক সেভাবেই কোনও NavBackStackEntry-এর Lifecycle পর্যবেক্ষণ করতে পারবেন। এছাড়াও, NavBackStackEntry হল একটি LifecycleOwner, যার অর্থ হল আপনি এটি ব্যবহার করতে পারবেন যখন LiveData বা অন্যান্য লাইফসাইকেল-অ্যাওয়ার কম্পোনেন্ট পর্যবেক্ষণ করছেন, যেমনটি নিম্নলিখিত উদাহরণে দেখানো হয়েছে:

Kotlin

myViewModel.liveData.observe(backStackEntry, Observer { myData ->
    // react to live data update
})

জাভা

myViewModel.getLiveData().observe(backStackEntry, myData -> {
    // react to live data update
});

আপনি যখনই navigate() কল করেন, লাইফসাইকেল স্ট্যাটাস অটোমেটিক আপডেট হয়। ব্যাক স্ট্যাকের শীর্ষে নেই এমন গন্তব্যের লাইফসাইকেল স্ট্যাটাস RESUMED থেকে STARTED-এ চলে যায় যদি গন্তব্যগুলি এখনও FloatingWindow গন্তব্যের অধীনে দৃশ্যমান হয়, যেমন একটি ডায়ালগ গন্তব্য, বা অন্যথায় STOPPED-এ চলে যায়।

আগের গন্তব্যে ফলাফল ফিরিয়ে দেওয়া হচ্ছে

Navigation 2.3 এবং তার পরবর্তী ভার্সনে, NavBackStackEntry-এর মাধ্যমে SavedStateHandle-এ অ্যাক্সেস পাওয়া যায়। SavedStateHandle হল একটি কী-ভ্যালু ম্যাপ যা ডেটা স্টোর ও ফিরিয়ে আনার জন্য ব্যবহার করা যেতে পারে। এইসব ভ্যালু প্রসেস বন্ধ করার মাধ্যমে বজায় থাকে, যার মধ্যে কনফিগারেশন পরিবর্তন অন্তর্ভুক্ত এবং একই অবজেক্টের মাধ্যমে উপলভ্য থাকে। প্রদত্ত SavedStateHandle ব্যবহার করে, আপনি ডেস্টিনেশনের মধ্যে ডেটা অ্যাক্সেস ও পাস করতে পারবেন। এটি বিশেষ করে একটি গন্তব্য থেকে ডেটা ফেরত পাওয়ার প্রক্রিয়া হিসাবে উপযোগী স্ট্যাক থেকে পপ অফ হওয়ার পরে।

ডেস্টিনেশন খ থেকে ডেস্টিনেশন ক-তে ডেটা ফেরত পাঠাতে, প্রথমে ডেস্টিনেশন ক-কে সেট-আপ করুন যাতে এটি SavedStateHandle-এ ফলাফল শুনতে পায়। এটি করতে, getCurrentBackStackEntry() API ব্যবহার করে NavBackStackEntry ফিরিয়ে আনুন এবং তারপর SavedStateHandle-এর দেওয়া LiveData observe করুন।

Kotlin

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    val navController = findNavController();
    // We use a String here, but any type that can be put in a Bundle is supported
    navController.currentBackStackEntry?.savedStateHandle?.getLiveData<String>("key")?.observe(
        viewLifecycleOwner) { result ->
        // Do something with the result.
    }
}

জাভা

@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
    NavController navController = NavHostFragment.findNavController(this);
    // We use a String here, but any type that can be put in a Bundle is supported
    MutableLiveData<String> liveData = navController.getCurrentBackStackEntry()
            .getSavedStateHandle()
            .getLiveData("key");
    liveData.observe(getViewLifecycleOwner(), new Observer<String>() {
        @Override
        public void onChanged(String s) {
            // Do something with the result.
        }
    });
}

গন্তব্য B-তে, আপনাকে অবশ্যই getPreviousBackStackEntry() API ব্যবহার করে গন্তব্য A-এর SavedStateHandle-এ ফলাফল set করতে হবে।

Kotlin

navController.previousBackStackEntry?.savedStateHandle?.set("key", result)

জাভা

navController.getPreviousBackStackEntry().getSavedStateHandle().set("key", result);

আপনি যদি কোনও ফলাফল একবারই ম্যানেজ করতে চান, তাহলে ফলাফল মুছে দিতে আপনাকে অবশ্যই SavedStateHandle-এ remove() কল করতে হবে। আপনি ফলাফল না সরালে, LiveData যেকোনও নতুন Observer ইনস্ট্যান্সের জন্য শেষ ফলাফল ফেরত দেওয়া চালিয়ে যাবে।

ডায়ালগ ডেস্টিনেশন ব্যবহার করার সময় বিবেচ্য বিষয়

আপনি যখন এমন কোনও গন্তব্যে navigate করেন যা NavHost-এর সম্পূর্ণ ভিউ নেয় (যেমন <fragment> গন্তব্য), তখন আগের গন্তব্যের লাইফসাইকেল বন্ধ হয়ে যায়, যা SavedStateHandle-এর মাধ্যমে প্রদান করা LiveData-এ কোনও কলব্যাক হওয়া আটকায়।

তবে, ডায়ালগ গন্তব্যে নেভিগেট করার সময়, আগের গন্তব্যও স্ক্রিনে দেখা যায় এবং তাই সেটি বর্তমান গন্তব্য না হওয়া সত্ত্বেও STARTED হিসেবে দেখানো হয়। এর অর্থ হল, getCurrentBackStackEntry()-এ কল করা, onViewCreated()-এর মতো লাইফসাইকেল মেথডের মধ্যে থেকে, কনফিগারেশন পরিবর্তন বা প্রসেস বন্ধ করা এবং রিক্রিয়েশনের পরে (যেহেতু ডায়ালগ অন্য ডেস্টিনেশনের উপরে রিস্টোর করা হয়) ডায়ালগ ডেস্টিনেশনের NavBackStackEntry ফিরিয়ে দেবে। তাই আপনি সবসময় সঠিক NavBackStackEntry ব্যবহার করছেন কিনা তা নিশ্চিত করতে, আপনার গন্তব্যের আইডির সাথে getBackStackEntry() ব্যবহার করা উচিত।

এর অর্থ হল, আপনি ফলাফল LiveData-এ যে Observer সেট করবেন তা ডায়ালগ ডেস্টিনেশন স্ক্রিনে থাকাকালীনও ট্রিগার হবে। আপনি যদি শুধুমাত্র তখনই ফলাফল চেক করতে চান যখন ডায়ালগ ডেস্টিনেশন বন্ধ থাকে এবং অন্তর্নিহিত ডেস্টিনেশন বর্তমান ডেস্টিনেশন হয়ে যায়, তাহলে আপনি NavBackStackEntry-এর সাথে সম্পর্কিত Lifecycle পর্যবেক্ষণ করতে পারেন এবং শুধুমাত্র এটি RESUMED হয়ে গেলে ফলাফলটি ফিরিয়ে আনতে পারেন।

Kotlin

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    val navController = findNavController();
    // After a configuration change or process death, the currentBackStackEntry
    // points to the dialog destination, so you must use getBackStackEntry()
    // with the specific ID of your destination to ensure we always
    // get the right NavBackStackEntry
    val navBackStackEntry = navController.getBackStackEntry(R.id.your_fragment)

    // Create our observer and add it to the NavBackStackEntry's lifecycle
    val observer = LifecycleEventObserver { _, event ->
        if (event == Lifecycle.Event.ON_RESUME
            && navBackStackEntry.savedStateHandle.contains("key")) {
            val result = navBackStackEntry.savedStateHandle.get<String>("key");
            // Do something with the result
        }
    }
    navBackStackEntry.lifecycle.addObserver(observer)

    // As addObserver() does not automatically remove the observer, we
    // call removeObserver() manually when the view lifecycle is destroyed
    viewLifecycleOwner.lifecycle.addObserver(LifecycleEventObserver { _, event ->
        if (event == Lifecycle.Event.ON_DESTROY) {
            navBackStackEntry.lifecycle.removeObserver(observer)
        }
    })
}

জাভা

@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);
    NavController navController = NavHostFragment.findNavController(this);
    // After a configuration change or process death, the currentBackStackEntry
    // points to the dialog destination, so you must use getBackStackEntry()
    // with the specific ID of your destination to ensure we always
    // get the right NavBackStackEntry
    final NavBackStackEntry navBackStackEntry = navController.getBackStackEntry(R.id.your_fragment);

    // Create our observer and add it to the NavBackStackEntry's lifecycle
    final LifecycleEventObserver observer = new LifecycleEventObserver() {
        @Override
        public void onStateChanged(@NonNull LifecycleOwner source, @NonNull Lifecycle.Event event) {
            if (event.equals(Lifecycle.Event.ON_RESUME)
                && navBackStackEntry.getSavedStateHandle().contains("key")) {
                String result = navBackStackEntry.getSavedStateHandle().get("key");
                // Do something with the result
            }
        }
    };
    navBackStackEntry.getLifecycle().addObserver(observer);

    // As addObserver() does not automatically remove the observer, we
    // call removeObserver() manually when the view lifecycle is destroyed
    getViewLifecycleOwner().getLifecycle().addObserver(new LifecycleEventObserver() {
        @Override
        public void onStateChanged(@NonNull LifecycleOwner source, @NonNull Lifecycle.Event event) {
            if (event.equals(Lifecycle.Event.ON_DESTROY)) {
                navBackStackEntry.getLifecycle().removeObserver(observer)
            }
        }
    });
}
হবে

নেভিগেশন ব্যাক স্ট্যাক প্রতিটি স্বতন্ত্র গন্তব্যের জন্য NavBackStackEntry স্টোর করে, শুধু তাই নয়, এটি প্রতিটি পেরেন্ট নেভিগেশন গ্রাফের জন্যও স্টোর করে যেখানে স্বতন্ত্র গন্তব্য থাকে। এটি আপনাকে নেভিগেশন গ্রাফে স্কোপ করা NavBackStackEntry ফিরিয়ে আনার অনুমতি দেয়। নেভিগেশন গ্রাফ-স্কোপড NavBackStackEntry এমন একটি ViewModel তৈরি করার উপায় প্রদান করে যা নেভিগেশন গ্রাফে স্কোপ করা থাকে, এর ফলে আপনি গ্রাফের গন্তব্যের মধ্যে UI-সম্পর্কিত ডেটা শেয়ার করতে পারেন। এইভাবে তৈরি করা যেকোনও ViewModel অবজেক্ট ততক্ষণ পর্যন্ত লাইভ থাকে যতক্ষণ না সংশ্লিষ্ট NavHost এবং এর ViewModelStore মুছে ফেলা হয় অথবা যতক্ষণ না ব্যাক স্ট্যাক থেকে নেভিগেশন গ্রাফ পপ করা হয়।

নেভিগেশন গ্রাফে স্কোপ করা ViewModel কীভাবে ফিরিয়ে আনা যায় তা নিম্নলিখিত উদাহরণে দেখানো হয়েছে:

Kotlin

val viewModel: MyViewModel
        by navGraphViewModels(R.id.my_graph)

জাভা

NavBackStackEntry backStackEntry = navController.getBackStackEntry(R.id.my_graph);
MyViewModel viewModel = new ViewModelProvider(backStackEntry).get(MyViewModel.class);

আপনি Navigation 2.2.0 বা তার আগের ভার্সন ব্যবহার করলে, ViewModels-এর সাথে সেভ করা স্টেট ব্যবহার করার জন্য আপনাকে নিজস্ব ফ্যাক্টরি প্রদান করতে হবে, যেমনটি নিম্নলিখিত উদাহরণে দেখানো হয়েছে:

Kotlin

val viewModel: MyViewModel by navGraphViewModels(R.id.my_graph) {
    SavedStateViewModelFactory(requireActivity().application, requireParentFragment())
}

জাভা

NavBackStackEntry backStackEntry = navController.getBackStackEntry(R.id.my_graph);

ViewModelProvider viewModelProvider = new ViewModelProvider(
        backStackEntry.getViewModelStore(),
        new SavedStateViewModelFactory(
                requireActivity().getApplication(), requireParentFragment()));

MyViewModel myViewModel = provider.get(myViewModel.getClass());

ViewModel সম্পর্কে আরও তথ্যের জন্য, ViewModel ওভারভিউ দেখুন।

ইনফ্লেটেড নেভিগেশন গ্রাফে পরিবর্তন করা হচ্ছে

আপনি রানটাইমে ডাইনামিকভাবে ইনফ্লেটেড নেভিগেশন গ্রাফ পরিবর্তন করতে পারবেন।

যেমন, আপনার কাছে যদি একটি BottomNavigationView থাকে যা একটি NavGraph-এর সাথে বাউন্ড করা থাকে, তাহলে NavGraph-এর ডিফল্ট ডেস্টিনেশন অ্যাপ স্টার্টআপে বেছে নেওয়া ট্যাবকে নির্দেশ করে। তবে, আপনাকে এই আচরণ ওভাররাইড করতে হতে পারে, যেমন যখন কোনও ব্যবহারকারীর পছন্দ অ্যাপ স্টার্টআপে লোড করার জন্য একটি পছন্দের ট্যাব নির্দিষ্ট করে। অন্যথায়, ব্যবহারকারীর পুরনো অ্যাক্টিভিটির উপর ভিত্তি করে আপনার অ্যাপকে শুরুর ট্যাব পরিবর্তন করতে হতে পারে। আপনি NavGraph-এর ডিফল্ট ডেস্টিনেশন ডাইনামিকভাবে নির্দিষ্ট করে এইসব ক্ষেত্রে সহায়তা করতে পারবেন।

এই NavGraph-এর কথা বিবেচনা করুন:

<?xml version="1.0" encoding="utf-8"?>
<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/nav_graph"
    app:startDestination="@id/home">
    <fragment
        android:id="@+id/home"
        android:name="com.example.android.navigation.HomeFragment"
        android:label="fragment_home"
        tools:layout="@layout/fragment_home" />
    <fragment
        android:id="@+id/location"
        android:name="com.example.android.navigation.LocationFragment"
        android:label="fragment_location"
        tools:layout="@layout/fragment_location" />
    <fragment
        android:id="@+id/shop"
        android:name="com.example.android.navigation.ShopFragment"
        android:label="fragment_shop"
        tools:layout="@layout/fragment_shop" />
    <fragment
        android:id="@+id/settings"
        android:name="com.example.android.navigation.SettingsFragment"
        android:label="fragment_settings"
        tools:layout="@layout/fragment_settings" />
</navigation>

এই গ্রাফ লোড করা হলে, app:startDestination অ্যাট্রিবিউট নির্দিষ্ট করে যে HomeFragment দেখাতে হবে। শুরুর গন্তব্য ডাইনামিকভাবে ওভাররাইড করতে, নিম্নলিখিত ধাপগুলি অনুসরণ করুন:

  1. প্রথমে, ম্যানুয়ালি NavGraph ফুলিয়ে নিন।
  2. শুরুর গন্তব্য ওভাররাইড করুন।
  3. সবশেষে, NavController-এ ম্যানুয়ালি গ্রাফ অ্যাটাচ করুন।

Kotlin

val navHostFragment =
        supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment

val navController = navHostFragment.navController
val navGraph = navController.navInflater.inflate(R.navigation.bottom_nav_graph)
navGraph.startDestination = R.id.shop
navController.graph = navGraph
binding.bottomNavView.setupWithNavController(navController)

জাভা

NavHostFragment navHostFragment = (NavHostFragment) getSupportFragmentManager()
        .findFragmentById(R.id.nav_host_fragment);

NavController navController = navHostFragment.getNavController();
NavGraph navGraph = navController.getNavInflater().inflate(R.navigation.bottom_nav_graph);
navGraph.setStartDestination(R.id.shop);
navController.setGraph(navGraph);
NavigationUI.setupWithNavController(binding.bottomNavView, navController);

এখন আপনার অ্যাপ চালু হলে, HomeFragment-এর পরিবর্তে ShopFragment দেখানো হয়।

ডিপ লিঙ্ক ব্যবহার করার সময়, NavController ডিপ লিঙ্কের গন্তব্যের জন্য অটোমেটিক ব্যাক স্ট্যাক তৈরি করে। ব্যবহারকারী ডিপ লিঙ্কে নেভিগেট করে আবার আগের পৃষ্ঠায় ফিরে গেলে, কোনও এক সময় তিনি শুরুর ডেস্টিনেশনে পৌঁছে যাবেন। আগের উদাহরণে দেখানো টেকনিক ব্যবহার করে শুরু করার ডেস্টিনেশন ওভাররাইড করলে, তৈরি করা ব্যাক স্ট্যাকে সঠিক শুরু করার ডেস্টিনেশন যোগ করা হয়েছে কিনা তা নিশ্চিত করা যায়।

মনে রাখবেন যে এই টেকনিকের মাধ্যমে প্রয়োজন অনুযায়ী NavGraph-এর অন্যান্য দিকও ওভাররাইড করা যায়। ডিপ লিঙ্ক ম্যানেজ করা, স্ট্যাটাস রিস্টোর করা এবং আপনার গ্রাফের শুরু করার গন্তব্যে যাওয়ার সময় সঠিক স্ট্রাকচার ব্যবহার করা হয়েছে কিনা তা নিশ্চিত করতে, setGraph()-এ কল করার আগে গ্রাফে সব পরিবর্তন করতে হবে।