底部导航栏与导航组件 - 选择的片段未显示

3

我目前正在迁移到Android Navigation组件,但当在BottomNavigationView中选择新项时,无法使片段转换工作。我遵循了官方文档中的说明,但没有发现选定的片段未显示的任何问题。

在活动中,在onCreate方法中设置导航控制器:

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

布局包含导航宿主片段和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" />

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

菜单项和片段的ID也是匹配的,我已经没有任何想法为什么当我在BottomNavigationView中选择另一个项目时,新的片段没有显示。

编辑 1:

我进行了更多测试,发现从后堆栈中弹出片段也不起作用,也许我的导航控制器存在一些一般性问题?

编辑 2:

MainActivity(我删除了一些内容因为太长了):

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

在selectNavigationItem方法中,您还可以看到我尝试使用navController.navigate(R.id.action_activity_main_notebooks);手动导航,这样做是有效的,但是弹出返回栈并不起作用。但是使用NavigationUI.setupWithNavController(binding.includedAppbarMain.bottomNavigationViewMainAppbar, navController);设置BottomNavigationView应该可以避免调用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
}

搜索碎片:

@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,includedAppbarMain变量是指什么? - Zain
嗨Zain,我已将我的布局拆分为多个文件。所以基本上我有我的activity_main布局,其中包含了appbar_main(这就是你要求的),其中包含了应用栏、工具栏和底部导航视图。在该文件中,我包含了包含navhostfragment本身的layout_main。简而言之:activity_main-> appbar_main-> layout_main。如果你想问,是的,条件binding.includedAppbarMain.bottomNavigationViewMainAppbar != null是正确的。 - Timo S.
5个回答

4

我终于找到解决方案了,问题出在onNavigationItemSelected函数上。因此,从MainActivity中删除这两部分代码可以解决问题:

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

或者像我所做的那样,因为我仍然需要这个函数,返回NavigationUI.onNavDestinationSelected(item,navController)而不是true。

@Override
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
    selectNavigationItem(item.getItemId());
    return NavigationUI.onNavDestinationSelected(item, navController);
}

1

终于解决了我的问题, 当我使用导航图时,其中有许多片段和操作用于底部导航组件,只有5个菜单可用。

我的问题是当我访问任何菜单时,它会显示活动标记,但是当我访问两次已经访问的菜单时,就不会显示活动标记。 也就是说,在导航到片段后没有显示活动颜色。

使用以下解决方案进行修复:

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>

这里的homefragment是起始目的地。
<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">

希望您的问题已经得到解决。


0
正式文档中说 -

NavController 管理 NavHost 内的应用程序导航。

通常,应用程序将直接从主机获取控制器,或者使用 Navigation 类上的实用方法之一,而不是直接创建控制器。

创建 navController 时,请确保为其分配您的导航主机片段的 ID。 这是您创建 navController 的方式。
NavController navController = Navigation.findNavController(this, R.id.fragment_main_layout); 
// issue here, use R.id.fragment_main_layout_nav_host

如果你的 XML 中的 Nav Host Fragment 的 ID 是 -

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


  

请确保使用相同的ID。

编程愉快!


我更新了我的问题,请参见Zahin回复中的评论,无论如何,感谢您的回复。 - Timo S.
在 BottomNavigationView 中选择项目时,logcat 中是否有任何错误消息?如果有,请发布它。 - Nikhil Sharma
我检查了logcat,但不幸的是没有找到任何在选择项目时发生的错误。是否有特定的标签可以在logcat中搜索? - Timo S.
嗯...其实没有特定的标签。当项目被选中时,它只会在BottomNavigation中显示一个带有涟漪效果的点击,没有其他任何东西。如果您尝试返回或单击返回按钮,会发生什么?会产生任何错误吗? - Nikhil Sharma
请上传您的activity_main_home和activity_main_notebooks的其余代码。 - Nikhil Sharma
使用返回按钮导航时,只会关闭活动,因此不会将任何内容添加到后退堆栈中。我已更新我的问题并附上了我的其余代码。 - Timo S.

0

在初始化NavController时,您使用了错误的宿主片段ID。

您需要进行替换。

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

使用

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

谢谢回复,我刚刚检查了我的代码,id是匹配的,所以那只是我在将代码复制到问题中时犯的错误,非常抱歉。我已经编辑了我的问题并更正了id。 - Timo S.

0

你有初始化BottomNavigationView吗?

BottomNavigationView bottomNavigationView =binding.bottomNavigationAppBar

并在其中调用bottomNavigationView

NavigationUI.setupWithNavController(bottomNavigationView,
            navController);

1
我正在通过数据绑定访问BottomNavigationView,因此视图已经初始化,无需再次执行。 - Timo S.
我遇到了相同的问题。我不得不使用 findViewById <BottomNavigationView>(R.id.bottom_navigation)而不是ViewBinding。有人知道为什么会发生这种情况吗? - Teo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接