3

I'm currently migrating to android navigation component and cannot get the fragment transitions to work when a new item is being selected in the BottomNavigationView. I followed the instructions in the official documentation and I did not found any issues why the selected fragment is not being displayed.

In the activity in the onCreate method im setting the nav controller:

NavController navController = Navigation.findNavController(this, R.id.fragment_main_layout_nav_host);
NavigationUI.setupActionBarWithNavController(this, navController);
NavigationUI.setupWithNavController(binding.includedAppbarMain.bottomNavigationViewMainAppbar, navController);

The layout contains the nav host fragment and the BottomNavigationView:

<fragment
        android:id="@+id/fragment_main_layout_nav_host"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:name="androidx.navigation.fragment.NavHostFragment"
        app:navGraph="@navigation/activity_main"
        app:defaultNavHost="true"/>

<com.google.android.material.bottomnavigation.BottomNavigationView
        android:id="@+id/bottom_navigation_view_main_appbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:menu="@menu/activity_main_bottom_navigation" />

Menu for the BottomNavigationView:

<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:id="@+id/action_activity_main_home"
        android:title="Home"
        android:enabled="true"
        android:icon="@drawable/ic_home_24dp"/>
    <item
        android:id="@+id/action_activity_main_notebooks"
        android:title="Notebooks"
        android:enabled="true"
        android:icon="@drawable/ic_file_24dp"/>
    <item
        android:id="@+id/action_activity_main_search"
        android:title="Search"
        android:enabled="true"
        android:icon="@drawable/ic_search_24dp"/>
</menu>

Navigation:

<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/activity_main"
    app:startDestination="@id/action_activity_main_home">

    <fragment
        android:id="@+id/action_activity_main_home"
        android:name="com.inknotes.view.fragment.MainHomeFragment"
        android:label="@string/main_vertical_navigation_home"
        tools:layout="@layout/fragment_main_home" />
    <fragment
        android:id="@+id/action_activity_main_notebooks"
        android:name="com.inknotes.view.fragment.MainNotebookFragment"
        android:label="@string/main_vertical_navigation_notebooks"
        tools:layout="@layout/fragment_main_notebook" />
    <fragment
        android:id="@+id/action_activity_main_search"
        android:name="com.inknotes.view.fragment.MainSearchFragment"
        android:label="@string/main_vertical_navigation_search"
        tools:layout="@layout/fragment_main_search" />
</navigation>

The ids of the menu items and fragments are also matching and i'm running out of ideas why the new fragment is not being displayed when i select another item in the BottomNavigationView.

Edit 1:

I did some more testing and found out that popping the fragments from the backstack also does not work, maybe I have some generel issues with my nav controller?

Edit 2:

MainActivity (I removed some stuff because it would be too long):

@MainActivityScope
public class MainActivity extends AppCompatActivity implements ComponentCallbacks2, MainActivityHandler,
        SpeedDialView.OnActionSelectedListener, BottomNavigationView.OnNavigationItemSelectedListener {
    // Static variables
    public static final String EXTRA_PATH = "com.inknotes.EXTRA_PATH";

    // Injected objects
    @Inject MainHomeFragment mainHomeFragment;
    @Inject MainFolderFragment mainFolderFragment;
    @Inject MainNotebookFragment mainNotebookFragment;
    @Inject MainSearchFragment mainSearchFragment;
    @Inject MainFolderAddDialog mainFolderAddDialog;
    @Inject MainNotebookAddDialog mainNotebookAddDialog;
    @Inject MainNotebookActionModeCallback mainNotebookActionModeCallback;
    @Inject MainFolderActionModeCallback mainFolderActionModeCallback;
    @Inject FileHelper fileHelper;
    @Inject ClipboardHelper clipboardHelper;
    @Inject ViewModelProvider.Factory viewModelFactory;
    @Inject MainVerticalNavigationAdapter mainVerticalNavigationAdapter;
    @Inject XmlParser<OptionItem> xmlParser;

    // Objects
    public MainActivityComponent daggerMainActivityComponent;
    private ActivityMainBinding binding;
    private MainViewModel mainViewModel;
    private MainFolderViewModel mainFolderViewModel;
    private MainNotebookViewModel mainNotebookViewModel;
    private GestureDetectorCompat gestureDetectorCompat;
    private MenuItem searchMenuItem;
    private SelectionTracker<Long> verticalNavigationSelectionTracker;
    private NavController navController;

    // Variables
    private boolean isBackPressed = false;

    // =============================================================================================
    //region Base methods

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        binding = DataBindingUtil.setContentView(
                this,
                R.layout.activity_main,
                new BindingComponent(this));
        binding.setLifecycleOwner(this);

        // Set the main dagger component
        daggerMainActivityComponent = ((InkNotesApplication) getApplication())
                .component()
                .mainActivityComponentFactory()
                .create(this);
        daggerMainActivityComponent.inject(this);

        // Get all viewModels
        mainViewModel = new ViewModelProvider(this, viewModelFactory).get(MainViewModel.class);
        mainFolderViewModel = new ViewModelProvider(this, viewModelFactory).get(MainFolderViewModel.class);
        mainNotebookViewModel = new ViewModelProvider(this, viewModelFactory).get(MainNotebookViewModel.class);

        // Set the default file
        mainViewModel.setDefaultFile(getExternalFilesDir("notes"));
        mainViewModel.setCurrentFile(getExternalFilesDir("notes"));

        gestureDetectorCompat = new GestureDetectorCompat(this, new GestureListener());

        // Set variables of binding
        binding.setHandler(this);
        binding.setViewModel(mainViewModel);

        // Setup main toolbar
        setSupportActionBar(binding.includedAppbarMain.materialToolbarMainAppbar);
        Objects.requireNonNull(getSupportActionBar()).setDisplayHomeAsUpEnabled(false);
        getSupportActionBar().setHomeAsUpIndicator(R.drawable.ic_menu_24dp);

        // Setup explorer toolbar
        binding.includedAppbarFolder.materialToolbarFolder.setOnMenuItemClickListener(this::onOptionsItemSelected);

        // Setup drawer and navigation layout
        binding.navigationViewMainFolder.setVisibility(View.GONE);
        if (binding.drawerLayoutMain != null) {
            binding.drawerLayoutMain.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED);
            binding.drawerLayoutMain.addDrawerListener(new DrawerLayout.SimpleDrawerListener() {
                @Override
                public void onDrawerSlide(View drawerView, float slideOffset) {
                    if (mainFolderActionModeCallback != null) {
                        mainFolderActionModeCallback.finish();
                    }
                }
            });
        }

        // Setup navigation
        navController = Navigation.findNavController(this, R.id.fragment_main_layout_nav_host);
        AppBarConfiguration appBarConfiguration = new AppBarConfiguration.Builder(
            navController.getGraph()
        ).build();
        NavigationUI.setupActionBarWithNavController(this, navController, appBarConfiguration);
        if (binding.includedAppbarMain.bottomNavigationViewMainAppbar != null) {
           NavigationUI.setupWithNavController(binding.includedAppbarMain.bottomNavigationViewMainAppbar, navController);
        }

        // Set the explorer and file card fragment
        /*
        getSupportFragmentManager().beginTransaction()
                .replace(R.id.frame_layout_main_layout_fragment_container, mainHomeFragment).commit();*/
        getSupportFragmentManager().beginTransaction()
                .replace(R.id.frame_layout_main_folder_container, mainFolderFragment).commit();

        // Setup the floating action button
        binding.includedAppbarMain.speedDialViewMainAppbar.inflate(R.menu.activity_main_fab);
        binding.includedAppbarMain.speedDialViewMainAppbar.setOnActionSelectedListener(this);

        // Setup bottom navigation view
        if (binding.includedAppbarMain.bottomNavigationViewMainAppbar != null) {
            binding.includedAppbarMain.bottomNavigationViewMainAppbar.setOnNavigationItemSelectedListener(this);
        }
        // Setup the vertical navigation view
        if (binding.recyclerViewMainVerticalNavigation != null) {
            binding.recyclerViewMainVerticalNavigation.setAdapter(mainVerticalNavigationAdapter);
            List<OptionItem> items = xmlParser.parse(getResources().getXml(R.xml.menu_main_vertical_navigation), OptionItem.class);
            // Create the selection tracker
            // Add observer to the selection tracker
            mainVerticalNavigationAdapter.setSelectionTracker(verticalNavigationSelectionTracker);
            verticalNavigationSelectionTracker.select((long) R.id.action_activity_main_vertical_navigation_home);
            mainVerticalNavigationAdapter.submitList(items);

            binding.includedAppbarMain.speedDialViewMainAppbar.setVisibility(View.INVISIBLE);
            toggleFolderNavigationView(View.VISIBLE);
        }
    }

    @Override
    public void onBackPressed() {
        if (binding.drawerLayoutMain != null) {
            if (binding.drawerLayoutMain.isDrawerOpen(GravityCompat.START)) {
                binding.drawerLayoutMain.closeDrawer(GravityCompat.START);
            }
        }
        super.onBackPressed();
    }

    @Override
    public boolean onSupportNavigateUp() {
        if (binding.drawerLayoutMain != null) {
            return NavigationUI.navigateUp(navController, binding.drawerLayoutMain);
        }
        return super.onSupportNavigateUp();
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        gestureDetectorCompat.onTouchEvent(event);
        return super.onTouchEvent(event);
    }

    @Override
    public boolean dispatchTouchEvent(@NonNull MotionEvent event) {
        super.dispatchTouchEvent(event);
        return gestureDetectorCompat.onTouchEvent(event);
    }

    @Override
    public boolean onNavigationItemSelected(@NonNull MenuItem item) {
        selectNavigationItem(item.getItemId());
        return true;
    }
    //endregion

    // =============================================================================================
    //region Custom methods

    private void selectNavigationItem(int id) {
        switch (id) {
            case R.id.action_activity_main_home:
            case R.id.action_activity_main_vertical_navigation_home:
                //navController.navigate(R.id.action_activity_main_home);

                if (binding.drawerLayoutMain != null) {
                    binding.drawerLayoutMain.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED);
                }
                Objects.requireNonNull(getSupportActionBar()).setDisplayHomeAsUpEnabled(false);
                if (searchMenuItem != null) {
                    searchMenuItem.collapseActionView();
                }
                binding.includedAppbarMain.appBarLayoutMainAppbar.setExpanded(true);
                binding.includedAppbarMain.speedDialViewMainAppbar.setVisibility(View.INVISIBLE);
                toggleFolderNavigationView(View.VISIBLE);
                break;
            case R.id.action_activity_main_notebooks:
            case R.id.action_activity_main_vertical_navigation_notebooks:
                //navController.navigate(R.id.action_activity_main_notebooks);

                if (binding.drawerLayoutMain != null) {
                    binding.drawerLayoutMain.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED);
                }
                Objects.requireNonNull(getSupportActionBar()).setDisplayHomeAsUpEnabled(true);
                if (searchMenuItem != null) {
                    searchMenuItem.collapseActionView();
                }
                binding.includedAppbarMain.appBarLayoutMainAppbar.setExpanded(true);
                binding.includedAppbarMain.speedDialViewMainAppbar.setVisibility(View.VISIBLE);
                toggleFolderNavigationView(View.GONE);
                break;
            case R.id.action_activity_main_search:
            case R.id.action_activity_main_vertical_navigation_search:
                //navController.navigate(R.id.action_activity_main_search);

                if (binding.drawerLayoutMain != null) {
                    binding.drawerLayoutMain.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED);
                }
                Objects.requireNonNull(getSupportActionBar()).setDisplayHomeAsUpEnabled(false);
                if (searchMenuItem != null) {
                    searchMenuItem.expandActionView();
                }
                binding.includedAppbarMain.appBarLayoutMainAppbar.setExpanded(false);
                binding.includedAppbarMain.speedDialViewMainAppbar.setVisibility(View.INVISIBLE);
                toggleFolderNavigationView(View.VISIBLE);
                break;
        }
        isBackPressed = false;
    }

In the selectNavigationItem method you can also see that I tested to navigate manually with navController.navigate(R.id.action_activity_main_notebooks);, that worked but popping the backstack also didn't work. But setting the BottomNavigationView with NavigationUI.setupWithNavController(binding.includedAppbarMain.bottomNavigationViewMainAppbar, navController); should make it unnecessary to call navigate.

HomeFragment:

@MainActivityScope
public class MainHomeFragment extends Fragment {
    public final static String NAME = "MainHomeFragment";

    @Inject
    public MainHomeFragment() {
    }

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

    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_main_home, container, false);
    }
}

NotebookFragment:

@MainActivityScope
public class MainNotebookFragment extends Fragment {
    // Static variables
    public final static String NAME = "MainNotebookFragment";
    private static final String ARG_COLUMN_COUNT = "column-count";

    // Injected objects
    @Inject ViewModelProvider.Factory viewModelFactory;
    @Inject MainNotebookAdapter mainNotebookAdapter;
    @Inject MainNotebookItemTouchHelperCallback mainNotebookItemTouchHelperCallback;

    // Objects
    private RecyclerView recyclerView;
    private MainNotebookViewModel mainNotebookViewModel;

    // Variables
    private int columnCount = 4;

    @Inject
    public MainNotebookFragment() {
    }

    // =============================================================================================
    //region Base methods

    @Override
    public void onCreate(Bundle savedInstanceState) {
        if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
            columnCount = getResources().getInteger(R.integer.column_count_portrait);
        } else if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
            columnCount = getResources().getInteger(R.integer.column_count_landscape);
        }
        super.onCreate(savedInstanceState);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_main_notebook, container, false);

        ((MainActivity) requireActivity()).daggerMainActivityComponent.inject(this);
        mainNotebookViewModel = new ViewModelProvider(requireActivity(), viewModelFactory).get(MainNotebookViewModel.class);
        if (getArguments() != null) {
            columnCount = getArguments().getInt(ARG_COLUMN_COUNT);
        }

        // Set the mainNotebookAdapter
        if (view instanceof RecyclerView) {
            Context context = view.getContext();
            recyclerView = (RecyclerView) view;
            if (columnCount <= 1) {
                recyclerView.setLayoutManager(new LinearLayoutManager(context));
            } else {
                recyclerView.setLayoutManager(new GridLayoutManager(context, columnCount));
            }
            recyclerView.setAdapter(mainNotebookAdapter);
        }

        // Set the item touch helper
        ItemTouchHelper itemTouchHelper = new ItemTouchHelper(mainNotebookItemTouchHelperCallback);
        itemTouchHelper.attachToRecyclerView(recyclerView);

        return view;
    }

    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        mainNotebookViewModel.getItems().observe(
                getViewLifecycleOwner(),
                fileCardItems -> mainNotebookAdapter.setItems(fileCardItems)
        );

        mainNotebookViewModel.getSelectedItems().observe(
                getViewLifecycleOwner(),
                selectedExplorerItems -> mainNotebookAdapter.setSelectedItems(selectedExplorerItems)
        );

        mainNotebookViewModel.getQueryText().observe(
                getViewLifecycleOwner(),
                queryText -> mainNotebookAdapter.getFilter().filter(queryText)
        );
    }

    @Override
    public void onResume() {
        super.onResume();
        if (!EventBus.getDefault().isRegistered(mainNotebookAdapter)) {
            EventBus.getDefault().register(mainNotebookAdapter);
        }
    }

    @Override
    public void onPause() {
        super.onPause();
        EventBus.getDefault().unregister(mainNotebookAdapter);
    }
    //endregion
}

SearchFragment:

@MainActivityScope
public class MainSearchFragment extends Fragment {
    public final static String NAME = "MainSearchFragment";

    @Inject
    public MainSearchFragment() {
    }

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

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_main_search, container, false);
    }
}
Timo S.
  • 113
  • 1
  • 9
  • Hi Timo, what the `includedAppbarMain` var refer to? – Zain Aug 03 '20 at 14:59
  • Hi Zain, I have split my layout into multiple files. So basically I have my `activity_main` layout that includes with the includes tag the `appbar_main` (so that what you have asked for) which contains the appbar, toolbar and bottomnavigatinview. Within that file im including the `layout_main` that contains the navhostfragment itself. So in short: `activity_main` -> `appbar_main` -> `layout_main`. And in case you want to ask, yes the condition `binding.includedAppbarMain.bottomNavigationViewMainAppbar != null` ist true. – Timo S. Aug 03 '20 at 15:13

5 Answers5

4

I finally found the solution, the problem was the onNavigationItemSelected function. So either removing those two parts from the MainActivity resolves the issue:

if (binding.includedAppbarMain.bottomNavigationViewMainAppbar != null) {    
 binding.includedAppbarMain.bottomNavigationViewMainAppbar.setOnNavigationItemSelectedListener(this);
}
...
@Override
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
    selectNavigationItem(item.getItemId());
    return true;
}

Or what I did, because I stil need the function, return NavigationUI.onNavDestinationSelected(item, navController) instead of true.

@Override
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
    selectNavigationItem(item.getItemId());
    return NavigationUI.onNavDestinationSelected(item, navController);
}
Timo S.
  • 113
  • 1
  • 9
1

Finally got resolved my issue , when i am using a nav graph where many fragment and actions occur for a bottom navigation component where only 5 menu available .

my issue was when i am visit any menu then it show active stag but when i visit twise which is already visit then no active stag show .. Means that no active color showing after navigating fragment.

Fixed with this solution :-

app:popUpTo="@id/homeFragment" app:popUpToInclusive="true"

<fragment
        android:id="@+id/addAccountFragment"
        android:name="app.ph7.doctor.ui.screens.home.addAccount.AddAccountFragment"
        android:label="AddAccountFragment"
        tools:layout="@layout/fragment_add_account">
        <action
            android:id="@+id/action_addAccountFragment_to_accDetailsFragment"
            app:destination="@id/accDetailsFragment" />
        <action
            android:id="@+id/action_addAccountFragment_to_notificationFragment"
            app:destination="@id/notificationFragment" />
        <action
            android:id="@+id/action_addAccountFragment_to_appointmentFragment"
            app:destination="@id/appointmentFragment"
            app:popUpTo="@id/homeFragment"
            app:popUpToInclusive="true" />
    </fragment>

Here homefragment is startDestination

<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/dashboard_navigation"
        app:startDestination="@id/homeFragment">

Hope your problem resolved ..

0

As official document says -

NavController manages app navigation within a NavHost.

Apps will generally obtain a controller directly from a host, or by using one of the utility methods on the Navigation class rather than create a controller directly.

When you are creating your navController, make sure you are assigning it the id of your navigation host fragment. This is how you are creating your navController.

NavController navController = Navigation.findNavController(this, R.id.fragment_main_layout); 
// issue here, use R.id.fragment_main_layout_nav_host

while the id of nav host fragment in your xml is -

<fragment>
    android:id="@+id/fragment_main_layout_nav_host"
    .....
</fragment>


  

Make sure you are using same id.

Happy Coding !!

Nikhil Sharma
  • 897
  • 1
  • 4
  • 18
  • I updated my question, see comment in Zahins reply, anyway thanks for the reply. – Timo S. Aug 03 '20 at 07:32
  • is there any error message in logcat, when item is being selected in the BottomNavigationView ? If so, please post it – Nikhil Sharma Aug 03 '20 at 08:19
  • I checked logcat but unfortunately could not find any errors that occur when an item is being selected. Is there a specific tag I can serach for in logcat? – Timo S. Aug 03 '20 at 08:40
  • hmm well.. there not a specific tag as such. when item is being selected it just show a click with ripple effect in BottomNavigation, nothing else ? What happen if you try to go back or click back button ? does it produce any error ? – Nikhil Sharma Aug 03 '20 at 09:54
  • and please upload the rest of the code for your activity_main_home and activity_main_notebooks ... – Nikhil Sharma Aug 03 '20 at 09:56
  • Navigating back with the back button just closes the activity, so nothing is being added to the back stack. I updated my question with the rest of my code. – Timo S. Aug 03 '20 at 10:47
0

You are using the wrong id of the host fragment when initializing the NavController

You need to replace

NavController navController = Navigation.findNavController(this, R.id.fragment_main_layout);

with

NavController navController = Navigation.findNavController(this, R.id.fragment_main_layout_nav_host);
Zain
  • 37,492
  • 7
  • 60
  • 84
  • Thanks for the reply, I just checked my code and the ids are matching, so that was just a mistake from my side while copying the code into the question, I'm sorry. I edited my question with the correct id. – Timo S. Aug 03 '20 at 06:50
0

``Did you initialize BottomNavigationView

BottomNavigationView bottomNavigationView =binding.bottomNavigationAppBar

and call bottomNavigationView in

NavigationUI.setupWithNavController(bottomNavigationView,
            navController);
ismailfarisi
  • 249
  • 1
  • 2
  • 8
  • 1
    I'm accessing the BottomNavigationView via databinding so the view is already initialized, ne need to do it again. – Timo S. Aug 03 '20 at 07:24
  • I faced the same issue. I had forced to use `findViewById(R.id.bottom_navigation)` instead of ViewBinding. Anyone have idea why this will happen? – Teo May 31 '21 at 13:16