How to display my Navigation bar contents without interruption?
Asked Answered
O

2

3

I built a customizable navigation drawer from scratch(didn't make use of the default drawer provided by Android Studio). In my weather app's navigation bar menu https://i.sstatic.net/SIjdx.jpg, whenever I select an option on the menu(say settings), it displays the contents of the option along with the bottom navigation view and my Activity's Toolbar contents which comprises of the nav hamburger icon, the edittext and the search button(the activity hosting my 3 fragments) which spoils the app and makes it look very ugly i.e. https://i.sstatic.net/gxj5n.jpg (From that screenshot, the entire content should be empty if implemented well). The case is the same for the other bar menu options. All I want is an empty space to work on, I want the app to only display the navigation bar contents without the rest. Example; https://i.sstatic.net/3Jtga.png Please how should I do this?

The view of the Navigation Menu is controlled by this code(on line 185):

@Override
    public boolean onNavigationItemSelected(@NonNull MenuItem item) {
        switch (item.getItemId()) {
            case R.id.settings_id:
                getSupportFragmentManager().beginTransaction().replace(R.id.fragment,
                        new Settings()).commit();
                break;
            case R.id.ads_upgrade_id:
                getSupportFragmentManager().beginTransaction().replace(R.id.fragment,
                        new Upgrade()).commit();
                break;
            case R.id.privacy_policy_id:
                getSupportFragmentManager().beginTransaction().replace(R.id.fragment,
                        new Privacy_Policy()).commit();
                break;
        }
        drawer.closeDrawer(GravityCompat.START);

        return true;
    }

"fragment" there represents that I'm currently using my fragment's container view on my activity to display the Nav menu contents which I know is wrong for sure, so what should I use in replace? I lack strong experience as it's my first time building an app and I've tirelessly spent 3 hours on my own trying to figure out the issue which proved abortive.

Here is my Activity code:

public class HomeActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener {
    private DrawerLayout drawer;
    // Last update time, click sound, search button, search panel.
    TextView timeField;
    MediaPlayer player;
    ImageView Search;
    EditText textfield;
    // For scheduling background image change(using constraint layout, start counting from dubai, down to statue of liberty.
    ConstraintLayout constraintLayout;
    public static int count = 0;
    int[] drawable = new int[]{R.drawable.dubai, R.drawable.norway, R.drawable.eiffel_tower, R.drawable.hong_kong, R.drawable.statue_of_liberty,
            R.drawable.beijing, R.drawable.chicago, R.drawable.colombia, R.drawable.vienna,R.drawable.tokyo};
    Timer _t;

    private WeatherDataViewModel viewModel;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_home);
        // use home activity layout.

        Toolbar toolbar = findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);
        // Allow activity to make use of the toolbar

        drawer = findViewById(R.id.drawer_layout);
        NavigationView navigationView = findViewById(R.id.nav_view);
        navigationView.setNavigationItemSelectedListener(this);

        viewModel = new ViewModelProvider(this).get(WeatherDataViewModel.class);

        // Trigger action to open & close navigation drawer
        ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(this, drawer, toolbar
                , R.string.navigation_drawer_open, R.string.navigation_drawer_close);
        drawer.addDrawerListener(toggle);
        toggle.syncState();

        timeField = findViewById(R.id.textView9);
        Search = findViewById(R.id.imageView4);
        textfield = findViewById(R.id.textfield);
        //  find the id's of specific variables.

        BottomNavigationView bottomNavigationView = findViewById(R.id.bottomNavigationView);
        // host 3 fragments along with bottom navigation.
        final NavHostFragment navHostFragment = (NavHostFragment) getSupportFragmentManager().findFragmentById(R.id.fragment);
        assert navHostFragment != null;
        final NavController navController = navHostFragment.getNavController();
        NavigationUI.setupWithNavController(bottomNavigationView, navController);

        // Make hourly & daily tab unusable
        bottomNavigationView.setOnNavigationItemSelectedListener(item -> {

            if (getSupportFragmentManager().getBackStackEntryCount() > 0) {
                getSupportFragmentManager().popBackStack();
            }
            return false;
        });

        navController.addOnDestinationChangedListener((controller, destination, arguments) -> navController.popBackStack(destination.getId(), false));

        // For scheduling background image change
        constraintLayout = findViewById(R.id.layout);
        constraintLayout.setBackgroundResource(R.drawable.dubai);
        _t = new Timer();
        _t.scheduleAtFixedRate(new TimerTask() {
            @Override
            public void run() {
                // run on ui thread
                runOnUiThread(() -> {
                    if (count < drawable.length) {

                        constraintLayout.setBackgroundResource(drawable[count]);
                        count = (count + 1) % drawable.length;
                    }
                });
            }
        }, 5000, 5000);

        Search.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                // make click sound when search button is clicked.
                player = MediaPlayer.create(HomeActivity.this, R.raw.click);
                player.start();

                getWeatherData(textfield.getText().toString().trim());
                // make use of some fragment's data

                Fragment currentFragment = navHostFragment.getChildFragmentManager().getFragments().get(0);
                if (currentFragment instanceof FirstFragment) {
                    FirstFragment firstFragment = (FirstFragment) currentFragment;
                    firstFragment.getWeatherData(textfield.getText().toString().trim());
                } else if (currentFragment instanceof SecondFragment) {
                    SecondFragment secondFragment = (SecondFragment) currentFragment;
                    secondFragment.getWeatherData(textfield.getText().toString().trim());
                } else if (currentFragment instanceof ThirdFragment) {
                    ThirdFragment thirdFragment = (ThirdFragment) currentFragment;
                    thirdFragment.getWeatherData(textfield.getText().toString().trim());
                }
            }

            private void getWeatherData(String name) {

                ApiInterface apiInterface = ApiClient.getClient().create(ApiInterface.class);

                Call<Example> call = apiInterface.getWeatherData(name);

                call.enqueue(new Callback<Example>() {
                    @Override
                    public void onResponse(@NonNull Call<Example> call, @NonNull Response<Example> response) {

                        try {
                            assert response.body() != null;
                            timeField.setVisibility(View.VISIBLE);
                            timeField.setText("First Updated:" + " " + response.body().getDt());
                        } catch (Exception e) {
                            timeField.setVisibility(View.GONE);
                            timeField.setText("First Updated: Unknown");
                            Log.e("TAG", "No City found");
                            Toast.makeText(HomeActivity.this, "No City found", Toast.LENGTH_SHORT).show();
                        }
                    }

                    @Override
                    public void onFailure(@NotNull Call<Example> call, @NotNull Throwable t) {
                        t.printStackTrace();
                    }

                });
            }

        });
    }

    @Override
    public boolean onNavigationItemSelected(@NonNull MenuItem item) {
        switch (item.getItemId()) {
            case R.id.settings_id:
                getSupportFragmentManager().beginTransaction().replace(R.id.fragment,
                        new Settings()).commit();
                break;
            case R.id.ads_upgrade_id:
                getSupportFragmentManager().beginTransaction().replace(R.id.fragment,
                        new Upgrade()).commit();
                break;
            case R.id.privacy_policy_id:
                getSupportFragmentManager().beginTransaction().replace(R.id.fragment,
                        new Privacy_Policy()).commit();
                break;
        }
        drawer.closeDrawer(GravityCompat.START);

        return true;
    }

    @Override
    public void onBackPressed() {
        if (drawer.isDrawerOpen(GravityCompat.START)) {
            drawer.closeDrawer(GravityCompat.START);
        } else {
            super.onBackPressed();
            // Open/close drawer animation
        }
    }
}

In case you require any other code to look into the issue, please let me know. I'm just trying to avoid posting too much

EDIT:

My old bottomtabs nav graph:

<?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/my_nav"
    app:startDestination="@id/firstFragment">

    <fragment
        android:id="@+id/firstFragment"
        android:name="com.viz.lightweatherforecast.FirstFragment"
        android:label="fragment_first"
        tools:layout="@layout/fragment_first" />
    <fragment
        android:id="@+id/secondFragment"
        android:name="com.viz.lightpreciseweatherforecast.SecondFragment"
        android:label="fragment_second"
        tools:layout="@layout/fragment_second" />
    <fragment
        android:id="@+id/thirdFragment"
        android:name="com.viz.lightpreciseweatherforecast.ThirdFragment"
        android:label="fragment_third"
        tools:layout="@layout/fragment_third" />
</navigation>

My new nav bar graph:

<?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/bar_nav"
    app:startDestination="@id/firstFragment">

    <fragment
        android:id="@+id/firstFragment"
        android:name="com.viz.lightweatherforecast.FirstFragment"
        android:label="fragment_first"
        tools:layout="@layout/fragment_first" />
    <fragment
        android:id="@+id/settings_id"
        android:name="com.viz.lightweatherforecast.Settings"
        android:label="@string/settings"
        tools:layout="@layout/settings" />
    <fragment
        android:id="@+id/ads_upgrade_id"
        android:name="com.viz.lightweatherforecast.Upgrade"
        android:label="@string/upgrade_to_remove_ads"
        tools:layout="@layout/upgrade" />
    <fragment
        android:id="@+id/privacy_policy_id"
        android:name="com.viz.lightweatherforecast.Privacy_Policy"
        android:label="@string/privacy_policy"
        tools:layout="@layout/privacy_policy"/>

</navigation>
Overshine answered 31/12, 2021 at 17:6 Comment(15)
You could just toggle the visibility of the bottom bar when you get into a fragment you don't want to show it, unless I'm misunderstanding the requirement?Schnitzler
@jakelee even if I toggle the visibility of the bottom bar, my activity's contents are still showing. I need to hide the activity's content tooOvershine
I just want it to be viewed on a fresh spaceOvershine
You are using naviagation architecture components, so the navController is the one that should control fragment transactions in the navHostFragment; but in the navDrawer you are do the transaction through getSupportFragmentManager().beginTransaction() which should be done through the navController insteadReiners
@zain Okay, I can see. I knew the way I set it up was totally wrong because I fully built it following a youtube tutorial which was a basic sample of a different kind of app youtu.be/fGcMLu1GJEc but regardless, I'm open to your further suggestions. Thanks for showing upOvershine
@zain got a clue yet?Overshine
It's a bit tricky , but almost got what you want check this outReiners
@zain I'm really sorry for showing up late, tried to use my pc since 4 hours ago but my mouse started acting up so had to spend over 2hrs30mins before I could get it right. So I've looked over the sample/preview you shared and I must admit that it is a good one.Overshine
The settings type is exactly how I want it, except that those 3 left horizontal small buttons there isn't needed, then I noticed that the slideshow type doesn't have its top back button and can only be navigated from down, I'd have preferred the same to settings but if you have no other way on it, I'll have to use it only that users may not find it easy. It also has the 3 left horizontal small buttons too which isn't needed. But thanks a lot for your help, I'm ready to work on it whenever you send the answer.Overshine
I also saw your answer to my previous question https://mcmap.net/q/1628385/-how-to-properly-implement-the-top-back-button-in-navigation-drawer thanks a lot, I would accept it when we're done with this question because they work together as there is no way I can implement the back button without fixing this current issue.Overshine
@RichardWilson I'll customize it to yours. it was for testing, Do all drawer items ads_upgrade_id, privacy_policy_id have the same back button behaviour?Reiners
@zain Okay, is see. All the drawer items currently look like this i.sstatic.net/gxj5n.jpg so all I need is to implement them to be exactly like the settings sample you sent and yes they currently exit the app when clicking backOvershine
@RichardWilson Can you share your navigation graphs; I think you have a single graph right?Reiners
@zain I've shared my nav graph, yeah I currently have a single nav graph. The firstFragment represents the today, 2nd - hourly and 3rd - daily. Are you saying I should change this graph to the one you're currently suggesting?Overshine
@zain you know the nav graph I'm using is for my other weather tabs but the one you're suggesting is for the navbar menu, should I replace yours with mine or just create a new one for your suggestion?Overshine
R
1

You are using navigation architecture components, so the navController is the one that should control fragment transactions, you are doing that right with BottomNavigationView.

But within the navDrawer you are doing the transaction through the supportFragmentManager which should be done through the navController instead as both handle the navigation differently.

whenever I select an option on the menu(say settings), it displays the contents of the option along with the bottom navigation view

That is because the BottomNavView is a part of the activity, and you need to move it to a fragment; this requires to change the navigation design of your app; to do that change your app navigation like the below:

Main navigation:

<navigation
 ..... >

    <fragment
        android:name="......HomeFragment"/>

    <fragment
        android:name="......SettingFragment"/>

    <fragment
        android:name="......AdsUpgradeFragment"/>
        
    <fragment
        android:name="......PrivacyPolicyFragment"/>        
        
        
</navigation>

The HomeFragment is the fragment that should hold the BottomNaviagtionView instead of the activity; and when you navigate to the SettingFragment, the navConroller will replace the entire fragment in the navHostFragment, and therefore the BottomNaviagtionView won't be shown.

my Activity's Toolbar contents which comprises of the nav hamburger icon, the edittext and the search button(the activity hosting my 3 fragments) which spoils the app and makes it look very ugly

Unlike the BottomNaviagtionView, you can't do that with your toolBar that is used as the supportActionBar, because setting supportActionBar more than once in order to change its look; will duplicates it; so you have to accept a single toolbar; but instead you can hide/show the layout that holds the search button & the EditText whenever the destination changes:

navController.addOnDestinationChangedListener((controller, destination, arguments) -> {
    LinearLayout searchBar = findViewById(R.id.searchbar); // change searchbar according to the layout id that holds the search button and the EditText
    if (destination.getId() == R.id.nav_home) {
        searchBar.setVisibility(View.VISIBLE);

    } else {
        searchBar.setVisibility(View.GONE);
    }

});

and yes they currently exit the app when clicking back

To exit the app whenever, the bottom back button is pressed in any fragment use OnBackPressedDispatcher() within onCreateView() of those fragment (in your case SettingFragment, PrivacyPolicyFragment, & AdsUpgradeFragment):

And make sure that appBarConfiguration doesn't reference those fragments so, that the UP button can be shown instead of the burger.

requireActivity().getOnBackPressedDispatcher().addCallback(getViewLifecycleOwner(), new OnBackPressedCallback(true) {
        @Override
        public void handleOnBackPressed() {
            // Exit the app when back is pressed
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
                requireActivity().finishAndRemoveTask();
            else requireActivity().finish();
        }
    });

Also, make sure that your setup the DrawerLayout & its navView in the navController with:

NavigationView navView = findViewById(....);

appBarConfiguration = new AppBarConfiguration.Builder(
        R.id.nav_home) // remove up button from all these fragments >> Keep the up/back button in R.id.settings_id, R.id.settings_id, ads_upgrade_id, privacy_policy_id
        .setOpenableLayout(drawer)
        .build();

NavigationUI.setupActionBarWithNavController(this, navController, appBarConfiguration);
NavigationUI.setupWithNavController(navView, navController);

And to make the Home fragment hidden by default in the navDrawer:

navView.getMenu().findItem(R.id.nav_home).setVisible(false); // adjust R.id.nav_home to yours

UPDATE

I currently have a single nav graph. The firstFragment represents the today, 2nd - hourly and 3rd I'm using is for my other weather tabs but the one you're suggesting is for the navbar menu, should I replace yours with mine or just create a new one for your suggestion?

You should use two navGraphs, the first is for the main navigation which I'd suggested; and the second is for the BottomNavigationView navigation which you already use; that is because we transferred the BottomNavigationView from the activity layout to the main/home fragment layout; and it' recommended the BottomNavigationView should have a separate navGraph;

So, you now need two FragmentContainerView; the first is in the activity layout which reference the navGraph provided in this answer, and the second is in the home fragment layout that references your original navGraph of the BottomNavigationView.

Sample:

Reiners answered 5/1, 2022 at 21:15 Comment(12)
The bottomNavigationViews still displays on the activity even after using the first code, so it still displays on the nav drawer tabs and it gives this error: The navigation graph is not referenced from any layout files(expected to find it in at least one layout file with a navhostfragment with the app:navgraph= "@navigation/bar_nav" attribute. 2. I Wrote the 2nd code in my homeactivtity, yet I still see both the search panel, edittext and the hamburger btn in my nav menu tabs though it runs without error.Overshine
3. When i used the 3rd code for onbackpressed, I got the following error in all: Unreachable statement(the entire code was on red). 4. The fourth gave this errors: Cannot resolve symbol appBarConfiguration bar_nav. Only the 5th code worked without issue, every other code had issuesOvershine
Finally, the new navgraph I created as you suggestion gave the following error: The navigation graph is not referenced from any layout files(expected to find it in at least one layout file with a navhostfragment with the app:navgraph= "@navigation/bar_nav" attribute. I've included the code of the new bar_nav navgraph I just created in my question.Overshine
Wow, I wish I could leave the bottom nav in the activity but just toggle it not to reflect in those fragments because at this point, I've gotten really confused, everything I'm doing is not working. I'll need like a day to get things together but thanks a lot for your helpOvershine
@RichardWilson Great, the BottomNavigationView should have a separate navigation graph, check this out. Feel free if you want to send you my sample project; but first give it a try so that you can learn itReiners
Good day, I think you're in the best position to handle it, I have tried all your suggestions severally to no avail and I'm sure that the only way I can handle it again is if I take several months on it so I'm pleading with you if I can share the necessary docs for you to handle it drive.google.com/drive/folders/… I made most of the code default so you can see how it was before and won't be confused when making the correctionsOvershine
The bottomNaviagtionView is currently in the activity's layout, you can copy that exact one to the first fragment's and remove it from the activity, I just left it there to avoid confusionOvershine
@RichardWilson Plz check these changes summarized in changes.txtReiners
First, this code in the activity toptal.com/developers/hastebin/ekihulocom.properties returns the error I wrote below on the code. Secondly, I removed that code to test the app to see how it runs, I noticed that immediately after my splash screen has finished playing its animation, it chills for a short while then displays a blank black screen, so the entire screen shows black immediately after the splash screen i.e. i.sstatic.net/BthYg.png and stays like that, nothing else loads after. I've used up the entire code you've sent and got no compilation errors, thanksOvershine
Let us continue this discussion in chat.Overshine
Thanks a lot man, just emailed it nowOvershine
If possible, please include the changes madeOvershine
S
0

I think I understood your problem very well (if I am not wrong). Thing is that what behavior you are getting right now is the normal scenario. You are using the same host activity to host both Navigation Drawer and Bottom Navigation fragments thus when you tried to navigate to another fragment from the same host the presence of the host's direct child view Bottom NavBar is showing. I think you can solve this problem in a few different but pretty simple logical ways.

  1. (Not recommended) Using a different Activity for the Settings page. Just like startActivity(this, <some intent>). But in this way, you will end up creating lots of individual activities.
  2. (Recommended) Using a common Navigation Host activity for navigating to independent pages/fragments. Like: Settings, Contact Us, etc. You can add different fragments into the common_nav_graph and set some actions with or without arguments for navigation. Just add the common_nav_graph as a nested graph inside your current one and set a simple argumented action. The arguments will help you to navigate desired pages/fragments without showing the bottom navigation bar.
  3. Simply hide Bottom navigation when navigating to some fragments from your drawer.
Semiprofessional answered 6/1, 2022 at 17:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.