带有抽屉式布局转换的Android导航组件

7

使用Android导航组件打开抽屉式布局中的片段时,是否可以更改转换效果?Android文档中没有相关说明。

2个回答

3

莎拉!是的,这是可能的。您可以添加自定义侦听器以处理导航项选择并在那里添加动画效果。我自己也不得不添加一个侦听器,用于其他目的,但它绝对适合您的任务解决方案。

如何实现:

  1. 添加带有抽屉布局的布局。例如:
<?xml version="1.0" encoding="utf-8"?>
<androidx.drawerlayout.widget.DrawerLayout 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/drawer_layout"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   android:fitsSystemWindows="true"
   tools:openDrawer="start">

   <!-- Other views can be added here, or below -->

   <com.google.android.material.navigation.NavigationView
       android:id="@+id/nav_view"
       android:layout_width="wrap_content"
       android:layout_height="match_parent"
       android:layout_gravity="start"
       app:menu="@menu/menu_for_drawer" />

</androidx.drawerlayout.widget.DrawerLayout>
  1. 通过ID查找NavigationView,并将其分配给活动中的变量private NavigationView navigationView。您可以使用findViewById(R.id.nav_view)提取它。这一步是可选的。您可以在不持有对NavigationView的引用的情况下找到视图并分配导航项选择侦听器;
  2. 设置导航项选择侦听器:
navigationView.setNavigationItemSelectedListener(menuItem -> {
           @IdRes
           int id = menuItem.getItemId();
           NavOptions.Builder optionsBuilder = new NavOptions.Builder();

           switch (id) {
               case R.id.first_menu_item_id: {
                   // Lets assume for the first menu item navigation is default
                   optionsBuilder
                           .setEnterAnim(R.anim.nav_default_enter_anim)
                           .setExitAnim(R.anim.nav_default_exit_anim)
                           .setPopEnterAnim(R.anim.nav_default_pop_enter_anim)
                           .setPopExitAnim(R.anim.nav_default_pop_exit_anim);
               }
               break;
               case R.id.second_menu_item_id: {
                   // Lets assume for the second menu item navigation is missing
                   // empty here
               }
               break;
               case R.id.thrid_menu_item_id: {
                   // Lets assume for the third menu item navigation is custom
                   optionsBuilder
                           .setEnterAnim(R.anim.slide_in_right)
                           .setExitAnim(R.anim.slide_out_left)
                           .setPopEnterAnim(R.anim.slide_in_left)
                           .setPopExitAnim(R.anim.slide_out_right);
               }
               break;
           }

           navController.navigate(id, null, optionsBuilder.build());
           
           // Do not forget to close the drawer
           // drawer.closeDrawers();
           return true;
       });

这会有所帮助!欢迎提出您的想法和问题!
如果您对示例中提到的动画感兴趣:
  • slide_in_left.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">

    <translate android:fromXDelta="-100%" android:toXDelta="0%"
        android:fromYDelta="0%" android:toYDelta="0%"
        android:duration="300"/>
</set>
  • slide_in_right.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">

    <translate android:fromXDelta="100%" android:toXDelta="0%"
        android:fromYDelta="0%" android:toYDelta="0%"
        android:duration="300"/>
</set>
  • slide_out_left.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">

    <translate android:fromXDelta="0%" android:toXDelta="-100%"
        android:fromYDelta="0%" android:toYDelta="0%"
        android:duration="300"/>
</set>
  • slide_out_right.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">

    <translate android:fromXDelta="0%" android:toXDelta="100%"
        android:fromYDelta="0%" android:toYDelta="0%"
        android:duration="300"/>
</set>

更新(2020年3月28日)

如果这种方法无法与抽屉菜单一起使用,请尝试使用以下代码结合使用。

请注意,由于NavigationUI在某些情况下设置自己的监听器,因此我将setNavigationItemSelectedListener调用放在方法的末尾。这也可能是问题的原因。

NavigationUI函数背后有很多事情发生,如果某些功能无法正常工作,请查看您使用的函数的实现。

private void setupNavigation() {
        // Set custom toolbar as action bar
        Toolbar toolbar = findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
                this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
        drawer.addDrawerListener(toggle);
        toggle.syncState();

        // Setup navigation with toolbar and drawer
        NavController navController = Navigation.findNavController(this, R.id.main_nav_host_fragment);

        /* The set of destinations by id considered at the top level 
        of your information hierarchy. The Up button will not be displayed 
        when on these destinations. */
        Set<Integer> set = new HashSet<>(Arrays.asList(R.id.first_fragmentId_from_nav_graph, R.id.second_fragmentId_from_nav_graph, R.id.third_fragmentId_from_nav_graph)); 
        /* Configuration options for {@link NavigationUI} methods that interact with implementations of the
        app bar pattern */
        AppBarConfiguration configuration = new AppBarConfiguration.Builder(set).setDrawerLayout(drawer).build();

        NavigationUI.setupWithNavController(toolbar, navController, configuration);
        NavigationUI.setupWithNavController(navigationView, navController);
        NavigationUI.setupActionBarWithNavController(this, navController, configuration);
        
        // And here you set the listener.
        navigationView.setNavigationItemSelectedListener(...);
    }

有多个对NavigationUI函数的调用,如setupWithNavControllersetupActionBarWithNavController。原因是每个函数背后都添加了一个更多目标改变监听器到导航控制器中。关于监听器的更多信息,请参见addOnDestinationChangedListener

  1. 第一次调用添加new ToolbarOnDestinationChangedListener(toolbar, configuration)
  2. 第二次调用添加自定义实现的NavController.OnDestinationChangedListener,该监听器可以正确地在抽屉式菜单中更新您的导航视图;
  3. 第三次调用添加new ActionBarOnDestinationChangedListener(activity, configuration)

这种配置使我能够在平板电脑上使用具有主细节模式的单个应用程序,并在手机上作为具有抽屉式菜单的常规应用程序使用。

引用的字符串资源只是:

<string name="navigation_drawer_open">Open navigation drawer</string>
<string name="navigation_drawer_close">Close navigation drawer</string>

但是它们是为了无障碍性而需要的。如果您支持多种语言,翻译它们非常好。基本上,任何用于无障碍性的内容都应该被翻译。


我尝试了您的实现。这似乎正是我所需要的。我有一个抽屉式菜单,其中包含多个片段,我想添加动画过渡效果。我在我的MainActivity的onCreate方法中添加了代码。当我从抽屉式菜单中进行选择时,setNavigationItemSelectedListener没有被调用。 - tzg
@tzg,我猜测 setNavigationItemSelectedListener 没有被调用是因为导航组件要么不知道抽屉,要么导航设置的方式引起了问题。我将在我的项目中使用的配置扩展答案。 - Jenea Vranceanu

2
有一个更简单的方法。在内部,NavigationUI像这样构建NavOptions
    if (navController.getCurrentDestination().getParent().findNode(item.getItemId())
            instanceof ActivityNavigator.Destination) {
        builder.setEnterAnim(R.anim.nav_default_enter_anim)
                .setExitAnim(R.anim.nav_default_exit_anim)
                .setPopEnterAnim(R.anim.nav_default_pop_enter_anim)
                .setPopExitAnim(R.anim.nav_default_pop_exit_anim);

    } else {
        builder.setEnterAnim(R.animator.nav_default_enter_anim)
                .setExitAnim(R.animator.nav_default_exit_anim)
                .setPopEnterAnim(R.animator.nav_default_pop_enter_anim)
                .setPopExitAnim(R.animator.nav_default_pop_exit_anim);
    }

您可以自己创建这些目录并覆盖默认设置。在我的情况下,我必须在“/res/”中创建“animator”文件夹(而不是“anim”!),并在其中放置4个新的动画文件,并使用这些名称。但也许尝试两个文件夹以确保。
目前,在MaterialComponents主题中它可以完美地工作:选定的抽屉项仍会突出显示,应用栏动画也仍然有效(与其他一些方法不同)。

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