如何不间断地显示我的导航栏内容?

How to display my Navigation bar contents without interruption?

我从头开始构建了一个可自定义的导航抽屉(没有使用 Android Studio 提供的默认抽屉)。在我的天气应用程序的导航栏菜单中 https://i.stack.imgur.com/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.stack.imgur.com/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.stack.imgur.com/3Jtga.png 请问我应该怎么做?

导航菜单的视图由以下代码控制(第 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;
    }

“片段”表示我目前正在 activity 上使用片段的容器视图来显示我知道肯定是错误的导航菜单内容,那么我应该用什么来替换?我缺乏丰富的经验,因为这是我第一次构建应用程序,我自己不知疲倦地花了 3 个小时试图找出被证明失败的问题。

这是我的 Activity 代码:

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
        }
    }
}

如果您需要任何其他代码来调查问题,请告诉我。我只是想避免发布太多内容

编辑:

我的旧 bottomtabs 导航图:

<?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>

我的新导航条形图:

<?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>

您正在使用导航架构组件,因此 navController 是应该控制片段事务的组件,您正在使用 BottomNavigationView 正确地做到这一点。

但是在 navDrawer 中,您正在通过 supportFragmentManager 进行交易,而应该通过 navController 进行交易,因为两者处理导航的方式不同。

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

那是因为BottomNavView是activity的一部分,需要移动到fragment中;这需要更改应用程序的导航设计;为此,请更改您的应用导航,如下所示:

主导航:

<navigation
 ..... >

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

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

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

HomeFragment 是应该包含 BottomNaviagtionView 而不是 activity 的片段;当您导航到 SettingFragment 时,navConroller 将替换 navHostFragment 中的整个片段,因此 BottomNaviagtionView 不会显示。

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

BottomNaviagtionView 不同,您不能使用用作 supportActionBar 的工具栏来执行此操作,因为不止一次设置 supportActionBar 以更改其外观;会复制它;所以你必须接受一个工具栏;但您可以 hide/show 每当目标更改时包含搜索按钮和 EditText 的布局:

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

要退出应用程序,在任何片段中按下底部后退按钮使用 onCreateView() 中的 OnBackPressedDispatcher() 这些片段(在您的情况下为 SettingFragment、PrivacyPolicyFragment 和 AdsUpgradeFragment):

并确保 appBarConfiguration 不引用这些片段,以便可以显示向上按钮而不是汉堡。

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();
        }
    });

此外,请确保您在 navController 中设置 DrawerLayout 及其 navView

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);

并使 Home 片段在 navDrawer 中默认隐藏:

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

更新

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?

您应该使用两个 navGraph,第一个用于我建议的主导航;第二个是您已经使用的 BottomNavigationView 导航;那是因为我们将 BottomNavigationView 从 activity 布局转移到 main/home 片段布局;和 it' recommended the BottomNavigationView should have a separate navGraph

所以,您现在需要两个 FragmentContainerView;第一个在 activity 布局中,它引用了此答案中提供的 navGraph,第二个在主片段布局中,它引用了 BottomNavigationView.

的原始 navGraph

样本:

我想我非常理解你的问题(如果我没记错的话)。事情是你现在得到的行为是正常情况。您正在使用同一主机 activity 来托管 Navigation DrawerBottom Navigation 片段,因此当您尝试从同一主机导航到另一个片段时,主机的直接子视图 Bottom NavBar 正在显示。我认为您可以通过几种不同但 非常简单的合乎逻辑的方式解决这个问题

  1. (不推荐)Settings 页面使用不同的 Activity。就像 startActivity(this, <some intent>)。但这样一来,您最终会创建很多个人活动。
  2. (推荐) 使用 通用导航主机 activity 导航至独立 pages/fragments。例如:设置、联系我们等。您可以将不同的片段添加到 common_nav_graph 中,并设置一些带有或不带有导航参数的操作。只需将 common_nav_graph 作为嵌套图表添加到当前图表中,并设置一个简单的论证操作。这些参数将帮助您在不显示 bottom navigation bar.
  3. 的情况下导航所需的 pages/fragments
  4. 导航到抽屉中的某些片段时,只需隐藏底部导航。