关于android导航架构组件的问题

Posted

技术标签:

【中文标题】关于android导航架构组件的问题【英文标题】:Question about android navigation architecture component 【发布时间】:2019-10-06 07:17:41 【问题描述】:

我有一个问题。我正在使用 android 导航架构组件,使用带有单一活动的底部导航视图。如何使片段只打开一次?即使导致这个片段的按钮被点击了几次? 如何使片段只添加到回栈一次? 我创建了一个测试项目来试一试

xml 导航测试

<?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/test_nav"
    app:startDestination="@id/firstFragment">
    <fragment
        android:id="@+id/firstFragment"
        android:name="ru.artem_nr.navigation_test.FirstFragment"
        android:label="fragment_first"
        tools:layout="@layout/fragment_first" >
        <action
            android:id="@+id/action_firstFragment_to_secondFrag"
            app:destination="@id/navigation"
            app:launchSingleTop="true"
            app:popUpTo="@+id/secondFrag"
            app:popUpToInclusive="false" />
    </fragment>
    <navigation android:id="@+id/navigation"
        app:startDestination="@id/secondFrag">
        <fragment
            android:id="@+id/secondFrag"
            android:name="ru.artem_nr.navigation_test.SecondFrag"
            android:label="fragment_second"
            tools:layout="@layout/fragment_second" />
    </navigation>
</navigation>

main_activity

公共类 MainActivity 扩展 AppCompatActivity 实现 NavigationView.OnNavigationItemSelectedListener

public Toolbar toolbar;
public DrawerLayout drawerLayout;
public NavController navController;
public NavigationView navigationView;

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

    setupNavigation();


private void setupNavigation() 

    toolbar = findViewById(R.id.toolbar);
    setSupportActionBar(toolbar);
    getSupportActionBar().setDisplayHomeAsUpEnabled(true);
    getSupportActionBar().setDisplayShowHomeEnabled(true);

    drawerLayout = findViewById(R.id.drawer_layout);
    navigationView = findViewById(R.id.nav_view);
    navController = Navigation.findNavController(this, R.id.garden_nav_fragment);
    NavigationUI.setupActionBarWithNavController(this, navController, drawerLayout);
    NavigationUI.setupWithNavController(navigationView, navController);
    navigationView.setNavigationItemSelectedListener((NavigationView.OnNavigationItemSelectedListener) this);


@Override
public boolean onSupportNavigateUp() 
    return (boolean) NavigationUI.navigateUp(navController, drawerLayout);


@Override
public void onBackPressed() 
    if (drawerLayout.isDrawerOpen(GravityCompat.START)) 
        drawerLayout.closeDrawer(GravityCompat.START);
     else 
        super.onBackPressed();
    


@Override
public boolean onNavigationItemSelected(@NonNull MenuItem menuItem) 
    menuItem.setChecked(true);

    drawerLayout.closeDrawers();

    int id = menuItem.getItemId();

    switch (id) 
        case R.id.nav_home:
            navController.navigate(R.id.navigation);
            break;
    
    return true;

片段 - 只是一个空白

【问题讨论】:

您的问题是底部导航,但您的代码使用了NavigationView。您要使用的是哪个?是否正在尝试处理用户重新选择菜单中已选择的项目的情况? 我使用什么样的导航并不重要。在我的真实项目底部导航视图中,但正如我所说,我创建了一个测试项目。在抽屉布局中同样的问题。是的,你是对的,我正在努力处理它。如果用户重新选择某个片段,它将返回堆栈。如何解决? 【参考方案1】:

您需要将片段声明为“SingleTop”。

导航图编辑器视图右侧的菜单上有一个选项可以执行此操作,位于动画选项下方。

或者,您可以通过 NavOptions.Builder 类以编程方式设置它,使用 setLaunchSingleTop(true) 方法,详细信息如下链接所示:

https://developer.android.com/reference/kotlin/androidx/navigation/NavOptions.Builder#setLaunchSingleTop(kotlin.Boolean)

所以你的 on select 方法应该是这样的:

public boolean onNavigationItemSelected(@NonNull MenuItem menuItem)
NavOptions.Builder builder = new NavOptions.Builder()
.setLaunchSingleTop(true);

NavOptions options = builder.build();
navController.navigate(item.getItemId(), null, options);

【讨论】:

您的意思是您尝试使用 NavOptions.Builder 以编程方式进行操作?如果您分享您的代码,我们或许可以为您发现问题 不,我是在界面中完成的。我创建了一个测试项目来试一试 以编程方式尝试? 是的,谢谢你的回答,但它并不适用于所有情况 你只能 setLaunchSingleTop() 用于动作,不能用于片段 - 所以你不能“将片段声明为 SingleTop”,尽管我希望你能这样做!【参考方案2】:

根据this issue,BottomNavigationView 提供了一个优先于NavigationUI 设置的OnNavigationItemSelectedListenerOnNavigationItemReselectedListener

bottomNavigationView.setOnNavigationItemReselectedListener(
    new BottomNavigationView.OnNavigationReselectedListener() 
        @Override
        public void onNavigationItemReselected(MenuItem item) 
            // By doing nothing, we ignore reselection events
        
    );

NavigationView 不提供与this other issue 相同的接口,因此您需要copy the default behavior(请注意,它调用NavigationUI.onNavDestinationSelected() 而不仅仅是直接调用navigate(),与您的代码不同)和为isChecked() 添加您自己的检查以防止重新选择。

【讨论】:

那么,目前平台没有这个问题的默认解决方案吗?如果我不理解 @АртемАртемов - 正确,每个组件完全相互独立。 我尝试使用 NavOptions.Builder 进行机器人导航视图。多次单击一个片段有帮助,但对于一种情况没有帮助,当我多次依次单击不同的图标时。创建大后栈,现在我看到了一种方法 - 如您所说,创建 isChecked(),但在大项目中这不是一个好的决定,因为我们需要为每个片段创建 isChecked() 方法。 您不应该在底部导航中设置OnNavigationItemSelectedListener - 默认NavigationUI.setupWithNavController() 是正确的。 @ianhanniballake 我在使用抽屉布局时遇到了与全局操作类似的问题。我有 3 个***目的地(每个抽屉项目一个)。这些只是堆叠在一起。使用 launchSingleTop 没有任何作用。此外,进入/退出动画不适用于全局操作。这是一个错误吗?已知问题?围?如果没有,在哪里报告?

以上是关于关于android导航架构组件的问题的主要内容,如果未能解决你的问题,请参考以下文章

关于 Android 移动团队的建设及未来架构的迭代升级思考

使用导航架构组件添加(而不是替换)片段

Android 导航架构组件 - 导航架构组件是不是意味着仅使用单个活动?

关于Android组件化的深度分析篇大厂架构

关于Android组件化的深度分析篇大厂架构篇

Android架构之路--关于social(分享支付等)组件化的封装及使用总结