How to intercept MainActivity's BottomNavigationView menu clicks from fragment












2














My app works like a Wizard. I have an Activity that serves as a centralization for all my fragments. There is a BottomNavigationView with a "Next" button that appears in each fragment to drive the wizard.



When I want to go next I call "action_next" from BottomNavMenu and it navigate to the next fragment.



But I need to perform some action's when the user presses the Next Button in the context of that fragment (like store the data inputted). Further, I need to cancel the navigation if there is any problem with data inputted by the user.



At first try i did this in my fragment:



val controller = NavHostFragment.findNavController(this)

controller.addOnNavigatedListener { controller, navDestination: NavDestination ->

when (navDestination.id) {
R.id.destination_setup_tournment -> {
proceedNavigation(controller, navDestination)

}

}
}


private fun proceedNavigation(controller: NavController, navDestination: NavDestination) {
val teams = teamAdapter.getItems()
if (validateSubmit(teams)){
presenter.saveTeams(teams)
}else{
//how to cancel navigation and stay on this fragment?
}
}


But it does not look good or even correct for me and I don't know how to cancel the navigation if something is wrong.



Here is the App's related files:





MainActivity:





<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?colorPrimary"
android:theme="@style/ToolbarTheme" />

<fragment
android:id="@+id/nav_host_fragment"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:name="androidx.navigation.fragment.NavHostFragment"
app:navGraph="@navigation/navigation"
app:defaultNavHost="true" />
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/bottom_nav_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
app:menu="@menu/bottom_nav"
style="@style/Widget.MaterialComponents.BottomNavigationView"
>


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














bottom_nav.xml:



<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/action_next" android:title="@string/tornment_mode" android:icon="@drawable/ic_navigate_next_black_24dp" />

</menu>









share|improve this question


















  • 1




    Why are you using a BottomNavigationView for a wizard like flow? BottomNavigationView is for global navigation between separate parts of your app.
    – ianhanniballake
    Nov 21 at 1:34










  • my intentation was to use something common to all fragments related to this activity, just changing the command executed by the button in the context of each fragment, but keeping the navigation flow.
    – alexpfx
    Nov 21 at 2:47
















2














My app works like a Wizard. I have an Activity that serves as a centralization for all my fragments. There is a BottomNavigationView with a "Next" button that appears in each fragment to drive the wizard.



When I want to go next I call "action_next" from BottomNavMenu and it navigate to the next fragment.



But I need to perform some action's when the user presses the Next Button in the context of that fragment (like store the data inputted). Further, I need to cancel the navigation if there is any problem with data inputted by the user.



At first try i did this in my fragment:



val controller = NavHostFragment.findNavController(this)

controller.addOnNavigatedListener { controller, navDestination: NavDestination ->

when (navDestination.id) {
R.id.destination_setup_tournment -> {
proceedNavigation(controller, navDestination)

}

}
}


private fun proceedNavigation(controller: NavController, navDestination: NavDestination) {
val teams = teamAdapter.getItems()
if (validateSubmit(teams)){
presenter.saveTeams(teams)
}else{
//how to cancel navigation and stay on this fragment?
}
}


But it does not look good or even correct for me and I don't know how to cancel the navigation if something is wrong.



Here is the App's related files:





MainActivity:





<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?colorPrimary"
android:theme="@style/ToolbarTheme" />

<fragment
android:id="@+id/nav_host_fragment"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:name="androidx.navigation.fragment.NavHostFragment"
app:navGraph="@navigation/navigation"
app:defaultNavHost="true" />
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/bottom_nav_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
app:menu="@menu/bottom_nav"
style="@style/Widget.MaterialComponents.BottomNavigationView"
>


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














bottom_nav.xml:



<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/action_next" android:title="@string/tornment_mode" android:icon="@drawable/ic_navigate_next_black_24dp" />

</menu>









share|improve this question


















  • 1




    Why are you using a BottomNavigationView for a wizard like flow? BottomNavigationView is for global navigation between separate parts of your app.
    – ianhanniballake
    Nov 21 at 1:34










  • my intentation was to use something common to all fragments related to this activity, just changing the command executed by the button in the context of each fragment, but keeping the navigation flow.
    – alexpfx
    Nov 21 at 2:47














2












2








2







My app works like a Wizard. I have an Activity that serves as a centralization for all my fragments. There is a BottomNavigationView with a "Next" button that appears in each fragment to drive the wizard.



When I want to go next I call "action_next" from BottomNavMenu and it navigate to the next fragment.



But I need to perform some action's when the user presses the Next Button in the context of that fragment (like store the data inputted). Further, I need to cancel the navigation if there is any problem with data inputted by the user.



At first try i did this in my fragment:



val controller = NavHostFragment.findNavController(this)

controller.addOnNavigatedListener { controller, navDestination: NavDestination ->

when (navDestination.id) {
R.id.destination_setup_tournment -> {
proceedNavigation(controller, navDestination)

}

}
}


private fun proceedNavigation(controller: NavController, navDestination: NavDestination) {
val teams = teamAdapter.getItems()
if (validateSubmit(teams)){
presenter.saveTeams(teams)
}else{
//how to cancel navigation and stay on this fragment?
}
}


But it does not look good or even correct for me and I don't know how to cancel the navigation if something is wrong.



Here is the App's related files:





MainActivity:





<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?colorPrimary"
android:theme="@style/ToolbarTheme" />

<fragment
android:id="@+id/nav_host_fragment"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:name="androidx.navigation.fragment.NavHostFragment"
app:navGraph="@navigation/navigation"
app:defaultNavHost="true" />
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/bottom_nav_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
app:menu="@menu/bottom_nav"
style="@style/Widget.MaterialComponents.BottomNavigationView"
>


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














bottom_nav.xml:



<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/action_next" android:title="@string/tornment_mode" android:icon="@drawable/ic_navigate_next_black_24dp" />

</menu>









share|improve this question













My app works like a Wizard. I have an Activity that serves as a centralization for all my fragments. There is a BottomNavigationView with a "Next" button that appears in each fragment to drive the wizard.



When I want to go next I call "action_next" from BottomNavMenu and it navigate to the next fragment.



But I need to perform some action's when the user presses the Next Button in the context of that fragment (like store the data inputted). Further, I need to cancel the navigation if there is any problem with data inputted by the user.



At first try i did this in my fragment:



val controller = NavHostFragment.findNavController(this)

controller.addOnNavigatedListener { controller, navDestination: NavDestination ->

when (navDestination.id) {
R.id.destination_setup_tournment -> {
proceedNavigation(controller, navDestination)

}

}
}


private fun proceedNavigation(controller: NavController, navDestination: NavDestination) {
val teams = teamAdapter.getItems()
if (validateSubmit(teams)){
presenter.saveTeams(teams)
}else{
//how to cancel navigation and stay on this fragment?
}
}


But it does not look good or even correct for me and I don't know how to cancel the navigation if something is wrong.



Here is the App's related files:





MainActivity:





<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?colorPrimary"
android:theme="@style/ToolbarTheme" />

<fragment
android:id="@+id/nav_host_fragment"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:name="androidx.navigation.fragment.NavHostFragment"
app:navGraph="@navigation/navigation"
app:defaultNavHost="true" />
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/bottom_nav_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
app:menu="@menu/bottom_nav"
style="@style/Widget.MaterialComponents.BottomNavigationView"
>


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














bottom_nav.xml:



<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/action_next" android:title="@string/tornment_mode" android:icon="@drawable/ic_navigate_next_black_24dp" />

</menu>






android kotlin bottomnavigationview android-jetpack android-navigation-component






share|improve this question













share|improve this question











share|improve this question




share|improve this question










asked Nov 21 at 0:35









alexpfx

1,68831841




1,68831841








  • 1




    Why are you using a BottomNavigationView for a wizard like flow? BottomNavigationView is for global navigation between separate parts of your app.
    – ianhanniballake
    Nov 21 at 1:34










  • my intentation was to use something common to all fragments related to this activity, just changing the command executed by the button in the context of each fragment, but keeping the navigation flow.
    – alexpfx
    Nov 21 at 2:47














  • 1




    Why are you using a BottomNavigationView for a wizard like flow? BottomNavigationView is for global navigation between separate parts of your app.
    – ianhanniballake
    Nov 21 at 1:34










  • my intentation was to use something common to all fragments related to this activity, just changing the command executed by the button in the context of each fragment, but keeping the navigation flow.
    – alexpfx
    Nov 21 at 2:47








1




1




Why are you using a BottomNavigationView for a wizard like flow? BottomNavigationView is for global navigation between separate parts of your app.
– ianhanniballake
Nov 21 at 1:34




Why are you using a BottomNavigationView for a wizard like flow? BottomNavigationView is for global navigation between separate parts of your app.
– ianhanniballake
Nov 21 at 1:34












my intentation was to use something common to all fragments related to this activity, just changing the command executed by the button in the context of each fragment, but keeping the navigation flow.
– alexpfx
Nov 21 at 2:47




my intentation was to use something common to all fragments related to this activity, just changing the command executed by the button in the context of each fragment, but keeping the navigation flow.
– alexpfx
Nov 21 at 2:47












1 Answer
1






active

oldest

votes


















2














There's no way to 'cancel' a navigation event - you need to put your business logic on your button/listener itself and only call navigate() when you actually want to navigate.



The Creating event callbacks to the activity documentation goes over one approach for connecting your Activity and Fragment together by using onAttach(Context) to get a reference to your Activity and set a property, callback, etc.



For example, you might consider doing something like:



// In your Activity
var onNextClicked: () -> Unit = {}

// In your Activity's onCreate()
button.setOnClickListener = View.OnClickListener {
onNextClicked.invoke()
}

// In your Fragment
override fun onAttach(context: Context) {
(context as YourActivity).onNextClicked = {
val teams = teamAdapter.getItems()
if (validateSubmit(teams)){
presenter.saveTeams(teams)
findNavController().navigate(R.id.action_next)
}
}
}


Each Fragment would set what they want the next button to do, allowing you full control over what the button does.



If you're dead set on the BottomNavigationView instead of just using an actual Button, you'd want to set your own OnNavigationItemSelectedListener and have that listener call the onNextClicked lambda if you're using this example.






share|improve this answer





















    Your Answer






    StackExchange.ifUsing("editor", function () {
    StackExchange.using("externalEditor", function () {
    StackExchange.using("snippets", function () {
    StackExchange.snippets.init();
    });
    });
    }, "code-snippets");

    StackExchange.ready(function() {
    var channelOptions = {
    tags: "".split(" "),
    id: "1"
    };
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function() {
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled) {
    StackExchange.using("snippets", function() {
    createEditor();
    });
    }
    else {
    createEditor();
    }
    });

    function createEditor() {
    StackExchange.prepareEditor({
    heartbeatType: 'answer',
    autoActivateHeartbeat: false,
    convertImagesToLinks: true,
    noModals: true,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: 10,
    bindNavPrevention: true,
    postfix: "",
    imageUploader: {
    brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
    contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
    allowUrls: true
    },
    onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    });


    }
    });














    draft saved

    draft discarded


















    StackExchange.ready(
    function () {
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53403692%2fhow-to-intercept-mainactivitys-bottomnavigationview-menu-clicks-from-fragment%23new-answer', 'question_page');
    }
    );

    Post as a guest















    Required, but never shown

























    1 Answer
    1






    active

    oldest

    votes








    1 Answer
    1






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes









    2














    There's no way to 'cancel' a navigation event - you need to put your business logic on your button/listener itself and only call navigate() when you actually want to navigate.



    The Creating event callbacks to the activity documentation goes over one approach for connecting your Activity and Fragment together by using onAttach(Context) to get a reference to your Activity and set a property, callback, etc.



    For example, you might consider doing something like:



    // In your Activity
    var onNextClicked: () -> Unit = {}

    // In your Activity's onCreate()
    button.setOnClickListener = View.OnClickListener {
    onNextClicked.invoke()
    }

    // In your Fragment
    override fun onAttach(context: Context) {
    (context as YourActivity).onNextClicked = {
    val teams = teamAdapter.getItems()
    if (validateSubmit(teams)){
    presenter.saveTeams(teams)
    findNavController().navigate(R.id.action_next)
    }
    }
    }


    Each Fragment would set what they want the next button to do, allowing you full control over what the button does.



    If you're dead set on the BottomNavigationView instead of just using an actual Button, you'd want to set your own OnNavigationItemSelectedListener and have that listener call the onNextClicked lambda if you're using this example.






    share|improve this answer


























      2














      There's no way to 'cancel' a navigation event - you need to put your business logic on your button/listener itself and only call navigate() when you actually want to navigate.



      The Creating event callbacks to the activity documentation goes over one approach for connecting your Activity and Fragment together by using onAttach(Context) to get a reference to your Activity and set a property, callback, etc.



      For example, you might consider doing something like:



      // In your Activity
      var onNextClicked: () -> Unit = {}

      // In your Activity's onCreate()
      button.setOnClickListener = View.OnClickListener {
      onNextClicked.invoke()
      }

      // In your Fragment
      override fun onAttach(context: Context) {
      (context as YourActivity).onNextClicked = {
      val teams = teamAdapter.getItems()
      if (validateSubmit(teams)){
      presenter.saveTeams(teams)
      findNavController().navigate(R.id.action_next)
      }
      }
      }


      Each Fragment would set what they want the next button to do, allowing you full control over what the button does.



      If you're dead set on the BottomNavigationView instead of just using an actual Button, you'd want to set your own OnNavigationItemSelectedListener and have that listener call the onNextClicked lambda if you're using this example.






      share|improve this answer
























        2












        2








        2






        There's no way to 'cancel' a navigation event - you need to put your business logic on your button/listener itself and only call navigate() when you actually want to navigate.



        The Creating event callbacks to the activity documentation goes over one approach for connecting your Activity and Fragment together by using onAttach(Context) to get a reference to your Activity and set a property, callback, etc.



        For example, you might consider doing something like:



        // In your Activity
        var onNextClicked: () -> Unit = {}

        // In your Activity's onCreate()
        button.setOnClickListener = View.OnClickListener {
        onNextClicked.invoke()
        }

        // In your Fragment
        override fun onAttach(context: Context) {
        (context as YourActivity).onNextClicked = {
        val teams = teamAdapter.getItems()
        if (validateSubmit(teams)){
        presenter.saveTeams(teams)
        findNavController().navigate(R.id.action_next)
        }
        }
        }


        Each Fragment would set what they want the next button to do, allowing you full control over what the button does.



        If you're dead set on the BottomNavigationView instead of just using an actual Button, you'd want to set your own OnNavigationItemSelectedListener and have that listener call the onNextClicked lambda if you're using this example.






        share|improve this answer












        There's no way to 'cancel' a navigation event - you need to put your business logic on your button/listener itself and only call navigate() when you actually want to navigate.



        The Creating event callbacks to the activity documentation goes over one approach for connecting your Activity and Fragment together by using onAttach(Context) to get a reference to your Activity and set a property, callback, etc.



        For example, you might consider doing something like:



        // In your Activity
        var onNextClicked: () -> Unit = {}

        // In your Activity's onCreate()
        button.setOnClickListener = View.OnClickListener {
        onNextClicked.invoke()
        }

        // In your Fragment
        override fun onAttach(context: Context) {
        (context as YourActivity).onNextClicked = {
        val teams = teamAdapter.getItems()
        if (validateSubmit(teams)){
        presenter.saveTeams(teams)
        findNavController().navigate(R.id.action_next)
        }
        }
        }


        Each Fragment would set what they want the next button to do, allowing you full control over what the button does.



        If you're dead set on the BottomNavigationView instead of just using an actual Button, you'd want to set your own OnNavigationItemSelectedListener and have that listener call the onNextClicked lambda if you're using this example.







        share|improve this answer












        share|improve this answer



        share|improve this answer










        answered Nov 21 at 4:06









        ianhanniballake

        102k14214226




        102k14214226






























            draft saved

            draft discarded




















































            Thanks for contributing an answer to Stack Overflow!


            • Please be sure to answer the question. Provide details and share your research!

            But avoid



            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.


            To learn more, see our tips on writing great answers.





            Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


            Please pay close attention to the following guidance:


            • Please be sure to answer the question. Provide details and share your research!

            But avoid



            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.


            To learn more, see our tips on writing great answers.




            draft saved


            draft discarded














            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53403692%2fhow-to-intercept-mainactivitys-bottomnavigationview-menu-clicks-from-fragment%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown





















































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown

































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown







            Popular posts from this blog

            404 Error Contact Form 7 ajax form submitting

            How to know if a Active Directory user can login interactively

            TypeError: fit_transform() missing 1 required positional argument: 'X'