0

I have a strange problem regarding a BottomNavigationBar which I could not solve altough having spent a huge amount of time into it. When I use it in the 'recommended' way (from many tutorials) it just does not navigate.

So what do I mean by 'recommended' way: I have a single acticity with a navHostFragment called 'MainActivity'. This main activity has a XML layout file in which I put the BottomNavigationBar. The BottomNavigationBar also has a XML layout file. Now I have a Fragment called 'FR_Menu' with a Java file and a XML layout file. I also have a NavGraph. In the XML layout of the Fragment 'FR_Menu' I do not use the BottomNavigationBar and in the Java class I do not instantiate the BottomNavigationBar as I have already done this in the main Activity. But using this approach the Navigation does not work. Altough the BottomNavigationBar is correctly displayd in the Fragment 'FR_Menu', when clicking on the Bottom just nothing happens.

Now here come the strange thing. When I use the code as posted, but not put the BottomNavigationBar in the XML layout file of the main activity and instead put it separately in every XML-layout file of all Fragments (in this example the fragment 'FR_Menu') and when I also instantiate the BottomNavigationBar in every Java file of each Fragment (in this example the fragment 'FR_Menu'), then the Navigation works perfectly. So with this approach I have to put the BottomNavigationBar in every XML layout file of the fragments and also I have to instantiate the BottomNavigationBar in every Java file of the Fragments. I know that normally using the Jetpack Navigation components, this should not be the case and instead I should only have to instantiate once in the MainActivity as the BottomNavigationBar should only be added to the XML layout file of the MainActivity.

Does anyone have an idea what goes wrong when I am trying to implement the 'recommended' approach? The names are all correct (maybe there is a small error in my shown example because I had to simplify and adjust it a little bit), because when using the second approach (putting the BottomNavigationBar in every XML-layout file and in every Java file of the fragments) the navigation works perfectly (I also tried it with multiple items and destinations).

I have really spent quite much time on that and I could not figure out what my mistake it. Because of this I would highly appreciate every comment and would be quite thankful for your help.

Does anyone have an idea what the reason for this strange behaviour might be?

Java code of the 'MainActivity':

package com.example.td.bapp;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.databinding.DataBindingUtil;
import androidx.navigation.NavController;
import androidx.navigation.Navigation;
import androidx.navigation.ui.NavigationUI;

import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import com.example.td.bapp.databinding.ActivityMainBinding;


public class MainActivity extends AppCompatActivity  {

   
    private  ActivityMainBinding binding;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
       


    }

    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {

        binding = ActivityMainBinding.inflate(inflater, container, false);
        NavController navController = Navigation.findNavController(this, R.id.navHostfragment);
        NavigationUI.setupWithNavController(binding.bottomNavigation,navController );
        setContentView(binding.getRoot());
        return binding.getRoot();
    }

}

XML layout file of the 'MainActivity':

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity"
    tools:ignore="ExtraText">


    <fragment
        android:id="@+id/navHostfragment"
        android:name="androidx.navigation.fragment.NavHostFragment"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:defaultNavHost="true"
        app:layout_constraintBottom_toTopOf="@+id/bottom_navigation"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:navGraph="@navigation/nav_graph" />

    <com.google.android.material.bottomnavigation.BottomNavigationView
        android:id="@+id/bottom_navigation"
        app:labelVisibilityMode="labeled"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@color/colorGreen"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:menu="@menu/bottom_navigation"
        app:itemIconTint="@color/colorPrimaryDark"
        app:itemTextColor="@color/colorAccent"
        />


</androidx.constraintlayout.widget.ConstraintLayout>

XML-layout file of BottomNavigationBar:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">


    <item
        android:id="@+id/FR_LanguageSelection"
        android:icon = "@drawable/ic_add_circle_full"
        android:title = "Language" />





</menu>

Java file of the Fragment 'FR_Menu'

package com.example.td.bapp;

import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.navigation.NavController;
import androidx.navigation.Navigation;
import androidx.navigation.ui.NavigationUI;

import com.example.td.bapp.databinding.ActivityMainBinding;
import com.example.td.bapp.databinding.FragmentMenuBinding;


public class FR_Menu extends Fragment implements View.OnClickListener {


    // TODO: Rename and change types of parameters



    public FR_Menu() {

    }


    public static FR_Menu newInstance(String param1, String param2) {
        FR_Menu fragment = new FR_Menu();
        Bundle args = new Bundle();
        fragment.setArguments(args);
        return fragment;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);


    }

    private FragmentMenuBinding binding;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // return inflater.inflate(R.layout.fragment_menu, container, false);
        binding = FragmentMenuBinding.inflate(inflater, container, false);


        /*
       // IMPORTANT REMARK: When I use the following code in the second option this in the second option,
       with the BottomNavigationBar in the XML document, the navigation works well


        NavController navController = Navigation.findNavController(getActivity(), R.id.navHostfragment);
        NavigationUI.setupWithNavController(binding.bottomNavigation,navController );
    */

        return binding.getRoot();
    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        binding.imageButton_A.setOnClickListener(this);
        binding.imageButton_B.setOnClickListener(this);


    }

    public void onDestroyView() {
        super.onDestroyView();
        binding = null;
    }


    @Override
    public void onClick(View view) {



        if(view.getId() == R.id.imageButton_A) {
            String argument = DataBaseEntries.A;

            FR_MenuDirections.ActionFRMenuToFRGenericD action =
                    FR_MenuDirections.actionFRMenuToFRGenericD(argument);
            Navigation.findNavController(view).navigate(action);
        }


        if(view.getId() == R.id.imageButton_B) {
            String argument = DataBaseEntries.B;

            FR_MenuDirections.ActionFRMenuToFRGenericD action =
                    FR_MenuDirections.actionFRMenuToFRGenericD(argument);
            Navigation.findNavController(view).navigate(action);
        }







    }
}

Here is the XML layout file of the fragment 'FR_Menu'

<?xml version="1.0" encoding="utf-8"?>


<androidx.constraintlayout.widget.ConstraintLayout xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">



    <androidx.appcompat.widget.Toolbar
        android:id="@+id/toolbar_mainActivity"
        android:layout_width="match_parent"
        android:layout_height="135dp"
        android:background="#435cb53f"
        android:theme="@style/ThemeOverlay.AppCompat.ActionBar"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.0"
        app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
        app:titleTextColor="@android:color/holo_green_light">

        <TextView
            android:id="@+id/tv"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginBottom="8dp"
            android:layout_marginEnd="8dp"
            android:layout_marginLeft="8dp"
            android:layout_marginRight="8dp"
            android:layout_marginStart="8dp"
            android:layout_marginTop="8dp"
            android:gravity="center"
            android:layout_gravity="center"
            android:textColor="@android:color/white"
            android:textSize="24sp"
            android:text="Menu" />
    </androidx.appcompat.widget.Toolbar>

    <ScrollView
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:layout_marginBottom="10dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/toolbar_mainActivity">

        <androidx.constraintlayout.widget.ConstraintLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            tools:context=".MainActivity"
            tools:ignore="ExtraText">


            <ImageButton
                android:id="@+id/imageButton_A"
                android:layout_width="0dp"
                android:layout_height="128dp"
                android:background="#00000000"
                android:scaleType="fitCenter"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintEnd_toStartOf="@id/imageButton_B"
                app:layout_constraintHorizontal_chainStyle="spread"
                app:layout_constraintHorizontal_weight="1"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toTopOf="parent"
                app:srcCompat="@drawable/menu_A" />

            <ImageButton
                android:id="@+id/imageButton_B"
                android:layout_width="0dp"
                android:layout_height="128dp"
                android:layout_marginTop="12dp"
                android:background="#00000000"
                android:scaleType="fitCenter"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintHorizontal_weight="1"
                app:layout_constraintStart_toEndOf="@id/imageButton_A"
                app:layout_constraintTop_toTopOf="parent"
                app:srcCompat="@drawable/menu_B" />






        </androidx.constraintlayout.widget.ConstraintLayout>
    </ScrollView>




</androidx.constraintlayout.widget.ConstraintLayout>

XML code of the 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/FR_LanguageSelection">

    <fragment
        android:id="@+id/FR_Menu"
        android:name="com.example.td.bapp.FR_Menu"
        android:label="FR_Menu"
        tools:layout="@layout/fragment_menu" >
        <action
            android:id="@+id/action_FR_Menu_to_FR_LanguageSelection"
            app:destination="@id/FR_LanguageSelection" />
    </fragment>

    <fragment
        android:id="@+id/FR_LanguageSelection"
        android:name="com.example.td.bapp.FR_LanguageSelection"
        android:label="FR_LanguageSelection" >
        <action
            android:id="@+id/action_FR_LanguageSelection_to_FR_Menu"
            app:destination="@id/FR_Menu" />
    </fragment>


</navigation>
Vadim Kotov
  • 8,084
  • 8
  • 48
  • 62
VanessaF
  • 515
  • 11
  • 36
  • So just to confirm, your menu XML has `android:id="@+id/FR_LanguageSelection"`, but your navigation graph XML has a completely different ID in `android:id="@+id/FR_Menu"`? How are you expecting those two to be linked together if they don't match? – ianhanniballake Jan 22 '21 at 19:36
  • Thanks ianhanniballake for your comment. I do not kow if I understand your remark correctly. My FR_Menu XML does not have "android:id="@+id/FR_LanguageSelection" as you posted – VanessaF Jan 22 '21 at 20:56
  • When I said "menu XML", I meant the menu XML associated with your bottom navigation bar. – ianhanniballake Jan 22 '21 at 20:58
  • Ah okay, yes. There I have what you posted. But still I do not understand why this is a problem? As stated in my post, when using the approach of inserting the BottomNavigationBar in every XML and Java File of each Fragment, the navigation works – VanessaF Jan 22 '21 at 21:01
  • I am using a single activity multiple fragments approach for my app. When I put the BottomNavigationBar in the single activity, it just does not navigate. However, when putting it in every Fragment (both XML and Java file) then it navigates correctly. – VanessaF Jan 22 '21 at 21:05
  • @ianhanniballake: In my navigationGraph XML I also have the tag "android:id="@+id/FR_LanguageSelection" as an id for a fragment and as a destination from the fragment 'FR_Menu'. In the XML of the BottomNavigationBar I have exactly the same ID "android:id="@+id/FR_LanguageSelection", so bascially in my eyes the match. So I do not really get your point. Any comments on this? I'd highly appreciate it. – VanessaF Jan 23 '21 at 08:06
  • Any further comments on my last comments. I would really appreciate it as I am struggeling on using the BottomNavigationBar with the Jetpack Navigation and I don't really get your point (from your first comment) even after spending some time trying to understand it (see my last comment). – VanessaF Jan 24 '21 at 08:11
  • @ianhanniballake: Would you mind trying to explain again what you meant with your first comment about my mistake? I do not really understand what the mistake is – VanessaF Jan 25 '21 at 19:33
  • @ianhanniballake: This is the 6th (and last) time I am trying to ask you whether you could explain again what you meant with your first comment about my mistake? I'd highly appreciate every further comment from you. – VanessaF Jan 26 '21 at 18:17

1 Answers1

1

onCreateView is a method of a Fragment, not a method on Activity, so your method in your MainActivity isn't ever called by the framework.

In fact, that code isn't what you'd use in an Activity at all as per the View Binding documentation.

Therefore your MainActivity should instead look like:

public class MainActivity extends AppCompatActivity  {
   
    private  ActivityMainBinding binding;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // Inflate, and then call setContentView() on the returned view root
        binding = ActivityMainBinding.inflate(getLayoutInflater());
        setContentView(binding.getRoot());
        
        NavController navController = Navigation.findNavController(this,
            R.id.navHostfragment);
        NavigationUI.setupWithNavController(binding.bottomNavigation, navController);
    }
}
ianhanniballake
  • 191,609
  • 30
  • 470
  • 443
  • Thanks for the answer ianhanniballake. Unfortunately my App can't be started with your suggested code. When I use your suggested code I get an error message before building the app "Cannot resolve method 'setContentView' in 'ActivityMainBinding'". – VanessaF Jan 26 '21 at 21:57
  • Because of this I replaced the databinding line - as suggested in your posted link - by "binding = ActivityMainBinding.inflate(getLayoutInflater());". Now the App can be built but when I now start the app I get immediately an error "IllegalArgumentException: ID does not reference a View inside this Activity" caused by the line " NavController navController = Navigation.findNavController(this, R.id.navHostfragment);" – VanessaF Jan 26 '21 at 21:57
  • Updated the code - it is `DataBindingUtil.setContentView`. If you only call `inflate()`, but not `setContentView()`, then yeah, the `findViewById()` that `findNavController()` depends on is going to fail since you never have a content view. – ianhanniballake Jan 26 '21 at 23:04
  • Thanks ianhanniballake for your further answer. Unfortunately your suggested code can't be built as I get the error message "Type parameter T has incompatible upper bounds: ViewDataBinding and ActivityMainBinding" – VanessaF Jan 27 '21 at 21:38
  • I searched for this error and I tried every option mentioned in this post: https://stackoverflow.com/questions/34368329/data-binding-android-type-parameter-t-has-incompatible-upper-bounds-viewdata. 1. Rename the XML file of the main activity. 2 Clean and Rebuild the project 3.Invalidate cache and restart. None of them had any effect. The error when using your suggested code still persists. – VanessaF Jan 27 '21 at 21:41
  • I've updated the code to use `inflate()` and `setContentView()`, since `inflate()` does seem to be working with your setup. – ianhanniballake Jan 28 '21 at 00:10
  • Thanks for your comment ianhanniballake. Your code obviously has errors (I knew that even without testing): Cannot resolve symbol 'inflater', Cannot resolve symbol 'container', Cannot resolve symbol 'root' – VanessaF Jan 28 '21 at 18:14
  • Here we are in an onCreate(Bundle savedInstanceState) method of an Activity and not in an onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) of a Fragment. So you suggested code can't work. – VanessaF Jan 28 '21 at 18:17
  • Yeah, there's a separate `inflate` method that just takes a `LayoutInflater` that you should be using. I've updated the link to the docs and the code. – ianhanniballake Jan 28 '21 at 19:44
  • Thanks for the answer ianhanniballake. Unfortunately I still get one error when using your new code: "Cannot resolve symbol 'root'" in "setContentView(binding.root);". I tried to rename the XML layout file, clean the project, Invalidate and Restart but nothing helped. I still get this error – VanessaF Jan 28 '21 at 20:40
  • Okay, I now found a solution to this problem. You just have to use the code: " binding = ActivityMainBinding.inflate(getLayoutInflater()); View view = binding.getRoot(); setContentView(view);" – VanessaF Jan 28 '21 at 21:15
  • Now the BottomNavigationBar navigates as it should :-). Thanks a lot ianhanniballake for your great help. I really appreciate it. I accepted and upvoted your answer – VanessaF Jan 28 '21 at 21:16
  • Ah, yeah in Kotlin code you can use `binding.root` as shorthand for `binding.getRoot()`. Using `getRoot()` is what you'd have to use if you're writing code in Java. – ianhanniballake Jan 28 '21 at 22:46
  • @ ianhanniballake: Maybe one follow up question: How can I implement a back-button in the BottomNavigationBar such that when I press it, the last fragment is being displayed. In contrary to the other botton in the BottomNavigationBar there is no predefined destination for such a back-button. – VanessaF Feb 28 '21 at 09:31
  • How can I implement a back-button in the BottomNavigationBar – VanessaF Mar 02 '21 at 17:59
  • Is there an easy an native way how I can implement a back-button using the BottomNavigationBar? – VanessaF Mar 03 '21 at 18:50
  • Navigation already does the right thing for you if you are using `defaultNavHost="true"` in your layout XML file. – ianhanniballake Mar 03 '21 at 19:46
  • Thanks ianhanniballake for your answer. But how can I implement the back-button in the ButtomNavigatonBar? In which layout shall I put your suggested code "defaultNavHost="true"? – VanessaF Mar 04 '21 at 18:30
  • The [Getting Started guide](https://developer.android.com/guide/navigation/navigation-getting-started) is a good place to start from. – ianhanniballake Mar 04 '21 at 18:50
  • Thanks ianhanniballake for your answer. Basically I had already implemented the NavGraph and the BottomNavigationBar and everything worked perfectly. Now I just want to add a back-button in the BottomNavigationBar and when pressing it the App should navigate to the last destination (like the navController.navigateUp() method). How can I implement that? – VanessaF Mar 04 '21 at 19:01
  • Normally I set in the XML file of the BottomNavigationBar something like "android:id="@+id/FR_LanguageSelection" to navigate to the FR_LanguageSelection when the corresponding button is pressed. How can I tell one button to get back to the last shown Fragment? Which android:id (or whatever) do I have to use for that? – VanessaF Mar 04 '21 at 19:04
  • Is there a command to specify the back button in the BottomNavigationBar such that it goes back natively? I read that the Jetpack Navigation should support that but I do not understand how – VanessaF Mar 05 '21 at 18:39
  • Is there a way how I can implement a back-button with the approach suggested by ianhanniballake using single-activity-multiple-fragments? I use "defaultNavHost="true" in my single activity. – VanessaF Mar 07 '21 at 08:38
  • @VanessaF - I'd strongly suggest asking your own question. The comments of a question are not the appropriate place to ask a separate question. – ianhanniballake Mar 07 '21 at 15:18