【问题标题】:Android Navigation Component with Transition from Drawerlayout带有 Drawerlayout 转换的 Android 导航组件
【发布时间】:2018-10-09 14:15:09
【问题描述】:

是否可以通过 android 导航组件从抽屉布局中打开片段来更改过渡效果。 android 文档中没有任何内容。

【问题讨论】:

    标签: java android kotlin android-transitions android-jetpack


    【解决方案1】:

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

    如何:

    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,我想setNavigationItemSelectedListener 没有被调用,因为导航组件要么不知道抽屉,要么导航的设置方式导致了这个问题。我将使用我的项目中使用的配置来扩展答案。
    【解决方案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 主题中完美运行:选定的抽屉项目仍然突出显示,并且应用栏动画仍然有效(与其他一些方法不同)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多