Android NavigationDrawer(侧滑导航栏 DrawerLayout + NavigationView)

    xiaoxiao2022-07-13  187

    Android NavigationDrawer(侧滑导航栏 DrawerLayout + NavigationView)


    本文由 Luzhuo 编写,转发请保留该信息. 原文: https://blog.csdn.net/Rozol/article/details/90487113


    Google官方出品的侧滑导航栏, 可替换长期未维护的SlidingMenu. 除此之外, 还有个第三方开源库MaterialDrawer可供选择

    Navigation Drawer 是 Material Design 中的侧滑导航栏, 由抽屉布局 DrawerLayout 和 导航View NavigationView 组成.

    快速创建

    新建项目时: 新建项目时, 最后一页"Add an Activity to Mobile" 选择 Navigation Drawer Activity 新建Activity时: 在已有的项目, 创建Activity选择 Navigation Drawer Activity

    手撸实现

    抽屉布局 DrawerLayout

    <?xml version="1.0" encoding="utf-8"?> <android.support.v4.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"> <include layout="@layout/app_bar_base" android:layout_width="match_parent" android:layout_height="match_parent" /> <android.support.design.widget.NavigationView android:id="@+id/nav_view" android:layout_width="wrap_content" android:layout_height="match_parent" android:layout_gravity="start" android:fitsSystemWindows="true" app:headerLayout="@layout/nav_header_base" app:menu="@menu/activity_base_drawer" /> </android.support.v4.widget.DrawerLayout>

    其中 NavigationView 的 android:layout_gravity="start"属性控制左右滑方向.

    DrawerLayout 与 Toolbar 的结合

    private DrawerLayout drawer; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_base); initDrawerLayout(); } private void initDrawerLayout() { // --- DrawerLayout 与 Toolbar 的结合 --- Toolbar toolbar = findViewById(R.id.toolbar); setSupportActionBar(toolbar); drawer = findViewById(R.id.drawer_layout); ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close); drawer.addDrawerListener(toggle); toggle.syncState(); }

    返回键的处理

    @Override public void onBackPressed() { if (drawer == null) return; // 返回键: 侧滑开着就将其关闭, 关着则退出应用 if (drawer.isDrawerOpen(GravityCompat.START)) { drawer.closeDrawer(GravityCompat.START); } else { super.onBackPressed(); } }

    打开 / 关闭 / 锁定

    // 打开 drawer.openDrawer(GravityCompat.START); drawer.openDrawer(GravityCompat.END); drawer.openDrawer(GravityCompat.START, true); // 默认true, 执行动画 drawer.openDrawer(navigationView); // 打开指定的 navigationView // 关闭 drawer.closeDrawer(GravityCompat.START); drawer.closeDrawers(); // 关闭所有 // 打开并锁定 drawer.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_OPEN, GravityCompat.START); 参数DescriptionGravityCompat.START左侧打开GravityCompat.END右侧打开——DrawerLayout.LOCK_MODE_LOCKED_CLOSED关闭锁 (锁着, 不让手势打开)DrawerLayout.LOCK_MODE_LOCKED_OPEN开启锁 (开着, 不让手势关闭)DrawerLayout.LOCK_MODE_UNDEFINED默认锁 (默认是不上锁)DrawerLayout.LOCK_MODE_UNLOCKED不锁 (不上锁)

    注意: 使用方向(如: GravityCompat.START) 参数时, 子布局必须有指定layout_gravity属性(如: NavigationView 的 android:layout_gravity="start")与之相匹配, 否则会找不到.

    阴影的颜色

    // (被遮挡部分的)阴影部分的颜色 drawer.setScrimColor(Color.parseColor("#66666666"));

    监听滑动事件

    drawer.addDrawerListener(drawerListener); private DrawerLayout.DrawerListener drawerListener = new DrawerLayout.DrawerListener() { @Override public void onDrawerSlide(@NonNull View view, float v) { Log.i(TAG, "onDrawerSlide: 滑动时:" + v); } @Override public void onDrawerOpened(@NonNull View view) { Log.i(TAG, "onDrawerOpened: 打开后"); } @Override public void onDrawerClosed(@NonNull View view) { Log.i(TAG, "onDrawerClosed: 关闭后"); } @Override public void onDrawerStateChanged(int i) { // 滑动状态 switch (i){ case DrawerLayout.STATE_DRAGGING: Log.i(TAG, "onDrawerStateChanged: 滑动状态"); break; case DrawerLayout.STATE_IDLE: Log.i(TAG, "onDrawerStateChanged: 静止状态"); break; case DrawerLayout.STATE_SETTLING: // 设置状态在静止状态之前调用, 表示正在调整到最终位置 Log.i(TAG, "onDrawerStateChanged: 设置状态"); break; default: break; } } };

    导航View NavigationView

    标准的导航菜单, NavigationView 通常放在 DrawerLayout 中使用.

    上部分是Header, 下部分是Menu, 当然这些都是可选的. Menu具有不错的默认样式: 选中效果 / 分组 / 分组子标题 / Header 等.

    DrawerLayout是v4包下的, 而 NavigationView 则是design包下的.

    implementation 'com.android.support:design:28.0.0'

    添加Header和Menu布局

    通过app:headerLayout属性添加Header; 通过app:menu属性添加Menu.

    <android.support.design.widget.NavigationView android:id="@+id/nav_view" android:layout_width="wrap_content" android:layout_height="match_parent" android:layout_gravity="start" android:fitsSystemWindows="true" app:headerLayout="@layout/nav_header_base" app:menu="@menu/activity_base_drawer" />

    编写Menu布局

    Header就是普通的布局没什么好讲的, 这里讲下Menu的编写.

    文件放在 res -> menu 文件夹下.

    <?xml version="1.0" encoding="utf-8"?> <menu xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" tools:showIn="navigation_view"> // 有选中效果的菜单组 <group android:checkableBehavior="single"> <item // 菜单条目 android:id="@+id/nav_camera" android:icon="@drawable/ic_menu_camera" // 图标 android:title="Import" /> // 名称 <item android:id="@+id/nav_gallery" android:icon="@drawable/ic_menu_gallery" android:title="Gallery" /> <item android:id="@+id/nav_slideshow" android:icon="@drawable/ic_menu_slideshow" android:title="Slideshow" /> <item android:id="@+id/nav_manage" android:icon="@drawable/ic_menu_manage" android:title="Tools" /> </group> // 没有选中效果的普通菜单条目, title子标题 <item android:title="Communicate"> <menu> // 子菜单 <item android:id="@+id/nav_share" android:icon="@drawable/ic_menu_share" // 图标 android:title="Share" /> // 标题 <item android:id="@+id/nav_send" android:icon="@drawable/ic_menu_send" android:title="Send" /> </menu> </item> </menu>

    设置Menu的选择监听

    但用户选择完之后需要手动将侧滑导航栏关闭.

    private void intiDrawerView() { navigationView = findViewById(R.id.nav_view); navigationView.setNavigationItemSelectedListener(this); } @SuppressWarnings("StatementWithEmptyBody") @Override public boolean onNavigationItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.nav_camera: break; case R.id.nav_gallery: break; case R.id.nav_slideshow: break; case R.id.nav_manage: break; case R.id.nav_share: break; case R.id.nav_send: break; default: break; } // 关闭侧滑导航栏 drawer.closeDrawer(GravityCompat.START); return true; }

    默认的情况下, 用户不点, Menu是没有选中的Item的, 可以通过setCheckedItem(int id)方法设置选中. 且 onNavigationItemSelected是不会回调的.

    // 设置第一个选中 navigationView.setCheckedItem(R.id.nav_camera);

    关于Menu图标的颜色

    我选了张彩色的图标:

    默认显示的图标:

    去除图标颜色显示规则, 显示为原色:

    // 去除图标颜色显示规则, 显示为原色 navigationView.setItemIconTintList(null);

    指定图标颜色显示规则(显示不同颜色): 在drawable下创建nav_menu_icon_color选择器资源文件.

    // 有-为未选中的颜色, 没有-为选中的颜色 int[][] states = new int[][]{new int[]{-android.R.attr.state_checked},new int[]{android.R.attr.state_checked} }; int[] colors = new int[]{getResources().getColor(R.color.colorPrimaryDark), getResources().getColor(R.color.colorAccent) }; ColorStateList colorStateList = new ColorStateList(states, colors); navigationView.setItemIconTintList(colorStateList);

    指定图标的显示规则(显示不同图标): // 未找到相关方法, 目前不支持

    虽然系统未提供相关方法, 都是你可以通过修改图标自行实现, 就是麻烦些.

    // 修改图标 navigationView.getMenu().findItem(R.id.nav_camera).setIcon(R.mipmap.menu_caomei);

    关于Menu文字的颜色

    设置的设置方法与图标是一样的, 这样不展开讲了.

    navigationView.setItemTextColor(colorStateList);

    2. 在真实场景中的应用 NavigationDrawer + Navigation

    有读者评论说 NavigationDrawer 没有提供切换Fragment的方法, 它没有提供是因为这本来就不是它的职责范围, 它已经提供了菜单按钮的点击回调了 该案例使用 Navigation 来切换 Fragment, 当然你也可以用其他的, 比如 FrameLayout

    1. 创建 Navigation

    1. 创建Fragment (略)

    2. 创建 Navigation 路由文件

    在 res 目录下创建 navigation 文件夹在 navigation 文件夹中创建 use_navigation.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" android:id="@+id/mobile_navigation" app:startDestination="@id/nav_first"> <fragment android:id="@+id/nav_first" android:name="me.luzhuo.navigationdrawerdemo.use.fragment.FirstFragment" android:label="First" /> <fragment android:id="@+id/nav_second" android:name="me.luzhuo.navigationdrawerdemo.use.fragment.SecondFragment" android:label="Second" /> <fragment android:id="@+id/nav_three" android:name="me.luzhuo.navigationdrawerdemo.use.fragment.ThreeFragment" android:label="Three" /> </navigation> 标记第一个Fragment为起始Fragment

    3. 配置 NavHostFragment

    创建 app_bar_use.xml 文件 (名字随意) 这里是主页显示的内容, 根据不同的需求进行修改如果不想使用 Navigation 实现切换 Framgent 的方式, 那就把这里的内容换掉比如想用 FrameLayout 实现切换 Fragment, 那就把这里的 fragment 换成 FrameLayout <?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" app:layout_behavior="@string/appbar_scrolling_view_behavior"> <fragment android:id="@+id/nav_host_fragment" android:name="androidx.navigation.fragment.NavHostFragment" android:layout_width="match_parent" android:layout_height="match_parent" app:defaultNavHost="true" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" app:navGraph="@navigation/use_navigation" /> </androidx.constraintlayout.widget.ConstraintLayout>

    2. 创建 NavigationDrawer

    1. 创建布局文件 activity_use.xml (名字随意)

    <?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"> <include layout="@layout/app_bar_use" android:layout_width="match_parent" android:layout_height="match_parent" /> <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" android:fitsSystemWindows="true" app:headerLayout="@layout/nav_header_use" app:menu="@menu/activity_use_drawer" /> </androidx.drawerlayout.widget.DrawerLayout>

    2. 创建菜单头布局文件 nav_header_use.xml

    <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="@dimen/nav_header_height" android:background="@drawable/side_nav_bar" android:gravity="bottom" android:orientation="vertical" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingBottom="@dimen/activity_vertical_margin" android:theme="@style/ThemeOverlay.AppCompat.Dark"> <ImageView android:id="@+id/imageView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:contentDescription="@string/nav_header_desc" android:paddingTop="@dimen/nav_header_vertical_spacing" app:srcCompat="@mipmap/ic_launcher_round" /> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:paddingTop="@dimen/nav_header_vertical_spacing" android:text="@string/nav_header_title" android:textAppearance="@style/TextAppearance.AppCompat.Body1" /> <TextView android:id="@+id/textView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/nav_header_subtitle" /> </LinearLayout>

    3. 创建配置文件 activity_use_drawer.xml

    注意: item 的 id 要和 Navigation 配置的路由 id 一致 <?xml version="1.0" encoding="utf-8"?> <menu xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" tools:showIn="navigation_view"> <group android:checkableBehavior="single"> <item android:id="@+id/nav_first" android:icon="@mipmap/menu_bangbing" android:title="First" /> <item android:id="@+id/nav_second" android:icon="@drawable/ic_menu_gallery" android:title="Second" /> <item android:id="@+id/nav_three" android:icon="@drawable/ic_menu_slideshow" android:title="Three" /> </group> </menu>

    3. 将 Navigation 和 NavigationDrawer 组合起来使用

    public class UseActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener { private DrawerLayout drawer; private NavigationView navigationView; private NavController navController; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_use); drawer = findViewById(R.id.drawer_layout); navigationView = findViewById(R.id.nav_view); ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(this, drawer, null, R.string.navigation_drawer_open, R.string.navigation_drawer_close); drawer.addDrawerListener(toggle); toggle.syncState(); navController = Navigation.findNavController(this, R.id.nav_host_fragment); // navController.navigate(R.id.nav_first); navigationView.setCheckedItem(R.id.nav_first); navigationView.setNavigationItemSelectedListener(this); } @Override public boolean onNavigationItemSelected(@NonNull MenuItem item) { navController.navigate(item.getItemId()); // 切换Fragment navigationView.setCheckedItem(item); // 选中菜单 drawer.closeDrawer(GravityCompat.START); // 关闭Drawer return true; } @Override public void onBackPressed() { // 返回键处理, 如果是打开状态, 则将其关闭; 若非打开状态, 则退出应用 if (drawer == null && drawer.isDrawerOpen(GravityCompat.START)) drawer.closeDrawer(GravityCompat.START); else super.onBackPressed(); } } <string name="navigation_drawer_open">Open navigation drawer</string> <string name="navigation_drawer_close">Close navigation drawer</string>
    最新回复(0)