android_基础_实现仿qq侧滑菜单

121 篇文章 1 订阅

Material Design 系列之 DrawerLayout + NavigationView 开发详解

原文

原创 Jaynm [码农专栏](javascript:void(0)😉

前言

DrawerLayout 是 Support Library 包中实现了侧滑菜单效果的控件,可以说 DrawerLayout 是因为第三方控件如 MenuDrawer 等的出现之后,google 借鉴而出现的产物。DrawerLayout 分为侧边菜单和主内容区两部分,侧边菜单可以根据手势展开与隐藏(DrawerLayout 自身特性),主内容区的内容可以随着菜单的点击而变化。

在这里插入图片描述

一、DrawerLayout 基础使用


DrawerLayout 其实是一个布局控件,继承 ViewGroup,与 LinearLayout 等控件是一种东西,属于同级控件。但是 DrawerLayout 带有滑动的功能。只要按照 DrawerLayout 的规定布局方式写完布局,就能有侧滑的效果。

DrawerLayout 最简单的使用方式,添加 2 个子布局,分别代表 APP 主页面和侧滑菜单页面。

`<?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/drawerLayout"  
    android:layout_width="match_parent"  
    android:layout_height="match_parent">  
    <!--主页面-->  
    <ImageView  
        android:layout_width="match_parent"  
        android:layout_height="match_parent"  
        android:scaleType="centerCrop"  
        android:src="@mipmap/meizi_2" />  
    <!--侧滑菜单页面-->  
    <ImageView  
        android:layout_width="match_parent"  
        android:layout_height="match_parent"  
        android:scaleType="centerCrop"  
        android:src="@mipmap/pangzi" />  
</androidx.drawerlayout.widget.DrawerLayout>  

完成上面的布局文件,就可以实现如下效果:

在这里插入图片描述

二、DrawerLayout + ToolBar 使用

日常开发中,每个界面都会有一个 ToolBar,并且侧滑出来的内容都会在 ToolBar 的底部。最常见的就是 ToolBar 左侧会有一个小图标(号称三道杠),在侧滑菜单展示时,会加载一个动画变成返回按钮,完成这个效果不需要在 ToolBar 上自己添加 Icon,只需要借助 ActionBarDrawerToggle 类就可实现。

ActionBarDrawerToggle 效果就是一个“三“ 然后点击变”←“

ActionBarDrawerToggle 的作用:

  1. 改变 android.R.id.home 返回图标

  2. DrawerLayout 拉出、隐藏,带有 android.R.id.home 动画效果

  3. 监听 DrawerLayout 拉出、隐藏

因为要实现侧滑菜单在 ToolBar 底部,修改 XML 文件,将 ToolBar 放在 DrawerLayout 布局外层。

<?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"  
    xmlns:tools="http://schemas.android.com/tools"  
    android:layout_width="match_parent"  
    android:layout_height="match_parent"  
    android:orientation="vertical">  
  
    <androidx.appcompat.widget.Toolbar  
        android:id="@+id/toolbar"  
        android:layout_width="match_parent"  
        android:layout_height="?attr/actionBarSize"  
        android:background="?attr/colorPrimary"  
        app:layout_scrollFlags="scroll|enterAlways"  
        app:title="DrawerLayout"  
        tools:ignore="MissingConstraints" />  
  
    <androidx.drawerlayout.widget.DrawerLayout  
        android:id="@+id/drawerLayout"  
        android:layout_width="match_parent"  
        android:layout_height="match_parent">  
  
        <ImageView  
            android:layout_width="match_parent"  
            android:layout_height="match_parent"  
            android:scaleType="centerCrop"  
            android:src="@mipmap/meizi_2" />  
  
        <ImageView  
            android:layout_width="match_parent"  
            android:layout_height="match_parent"  
            android:scaleType="centerCrop"  
            android:src="@mipmap/pangzi" />  
  
    </androidx.drawerlayout.widget.DrawerLayout>  
</LinearLayout>  

// 设置左上角图标[“三” —— “←”]效果

ActionBarDrawerToggle actionBarDrawerToggle = new ActionBarDrawerToggle(this, drawerLayout, toolbar, R.string.drawer_open, R.string.drawer_close);  
actionBarDrawerToggle.syncState();  
drawerLayout.addDrawerListener(actionBarDrawerToggle);  

在这里插入图片描述

三、NavigationView 介绍

NavigationView 是 Google 在侧滑的 Material Design 的一种规范,所以提出了一个新的空间,用来规范侧滑菜单的基本样式。

对于抽屉式菜单界面很多 APP 都有应用,此前写抽屉式界面都需要自定义。现在谷歌提供的导航视图 NavigationView + DrawerLayout 结合使用,能提供很好的侧滑交互体验。

四、NavigationView 常用方法

在这里插入图片描述

五、NavigationView 基础使用

DrawerLayout + NavigationView + ToolBar 结合使用是项目中最常见的效果,通常有 2 种效果:

  • 侧滑菜单在 ToolBar 底部

  • 侧滑菜单沉浸式覆盖 ToolBar 展示

第一种效果实际上就是在 XML 布局中,将 ToolBar 布局放到 DrawerLayout 外部,第二种效果是将 ToolBar 放到 DrawerLayout 内部主页面布局里面,然后实现沉浸式效果。本文源码中有沉浸式实现的工具类,感兴趣的朋友可以下载源码参考

1、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/drawerLayout"  
    android:layout_width="match_parent"  
    android:layout_height="match_parent"  
    android:fitsSystemWindows="true">  
  
    <LinearLayout  
        android:layout_width="match_parent"  
        android:layout_height="match_parent"  
        android:orientation="vertical">  
  
        <androidx.appcompat.widget.Toolbar  
            android:id="@+id/toolbar"  
            android:layout_width="match_parent"  
            android:layout_height="?attr/actionBarSize"  
            android:background="?attr/colorPrimary"  
            app:layout_scrollFlags="scroll|enterAlways"  
            app:title="DrawerLayout"  
            app:titleTextColor="#FFF"  
            tools:ignore="MissingConstraints" />  
  
        <ImageView  
            android:layout_width="match_parent"  
            android:layout_height="match_parent"  
            android:scaleType="centerCrop"  
            android:src="@mipmap/meizi_2" />  
  
    </LinearLayout>  
  
    <com.google.android.material.navigation.NavigationView  
        android:id="@+id/navigationView"  
        android:layout_width="wrap_content"  
        android:layout_height="match_parent"  
        android:layout_gravity="start"  
        app:headerLayout="@layout/nav_header_main"  
        app:insetForeground="@android:color/transparent"  
        app:menu="@menu/activity_main_drawer" />  
  
</androidx.drawerlayout.widget.DrawerLayout>  

在这里插入图片描述

只需在 DrawerLayout 中添加 NavigationView 控件即可,其中介绍两个属性(也就是上图中红黄蓝三个位置效果):

  • app:insetForeground="@android:color/transparent" NavigationView 沉浸式展示

  • app:headerLayout="@layout/nav_header_main" 在 NavigationView 上添加一个 Header 布局

  • app:menu="@menu/activity_main_drawer" NavigationView 添加标签 Item 的菜单

2、HeaderLayout 布局文件

<?xml version="1.0" encoding="utf-8"?>  
<FrameLayout 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="200dp"  
    android:background="?attr/colorPrimary"  
    android:gravity="bottom"  
    android:theme="@style/ThemeOverlay.AppCompat.Dark">  
  
    <com.caobo.slideviewdemo.drawerlayout.MovingImageView  
        android:id="@+id/movingImageView"  
        android:layout_width="match_parent"  
        android:layout_height="250dp"  
        android:scaleType="centerCrop"  
        android:src="@mipmap/menu_header_background"  
        app:miv_load_on_create="false"  
        app:miv_max_relative_size="3.0"  
        app:miv_min_relative_offset="0.2"  
        app:miv_repetitions="-1"  
        app:miv_speed="100"  
        app:miv_start_delay="100" />  
  
    <de.hdodenhof.circleimageview.CircleImageView  
        android:layout_width="100dp"  
        android:layout_height="100dp"  
        android:layout_marginLeft="16dp"  
        android:layout_marginTop="30dp"  
        android:paddingTop="16dp"  
        android:src="@mipmap/header_icon"  
        app:civ_border_color="@color/colorWhite"  
        app:civ_border_width="2dp" />  
  
    <TextView  
        android:id="@+id/tv_nick"  
        android:layout_width="match_parent"  
        android:layout_height="wrap_content"  
        android:layout_gravity="bottom"  
        android:layout_marginLeft="16dp"  
        android:layout_marginTop="10dp"  
        android:layout_marginBottom="16dp"  
        android:paddingLeft="5dp"  
        android:text="Learn and live."  
        android:textAppearance="@style/TextAppearance.AppCompat.Body1"  
        android:textSize="18sp" />  
</FrameLayout>  
  

3、Menu 菜单文件

这里的 icon 图标全部使用 vector 矢量图展示,如果还不会使用 Vector 矢量图的朋友,可以参考:

Android Material Design Icon Genenerator 插件为个人开发者提供 Icon 图标大全

<?xml version="1.0" encoding="utf-8"?>  
<menu xmlns:android="http://schemas.android.com/apk/res/android">  
    <group android:checkableBehavior="single">  
        <item  
            android:id="@+id/group_item_github"  
            android:icon="@drawable/ic_vector_github_grey"  
            android:title="项目主页" />  
        <item  
            android:id="@+id/group_item_more"  
            android:icon="@drawable/ic_vector_more"  
            android:title="更多内容" />  
        <item  
            android:id="@+id/group_item_qr_code"  
            android:icon="@drawable/ic_vector_qr_code"  
            android:title="二维码" />  
        <item  
            android:id="@+id/group_item_share_project"  
            android:icon="@drawable/ic_vector_share"  
            android:title="分享项目" />  
    </group>  
    <item android:title="选项">  
        <menu>  
            <item  
                android:id="@+id/item_model"  
                android:icon="@drawable/ic_vetor_setting"  
                android:title="夜间模式" />  
            <item  
                android:id="@+id/item_about"  
                android:icon="@drawable/ic_vector_about"  
                android:title="关于" />  
        </menu>  
    </item>  
</menu>  
  

4、Activity 代码实现

NavigationView 的使用基本上都是 XML 文件中完成的,Activity 中不需要做太多处理,只需要 Activity 中添加 Menu 的监听。本项目中使用了一个带动画的 ImageView,所以侧滑菜单后,底部图片会有动画效果,感兴趣的可以在底部下载源码学习。

public class DrawerLayoutActivity extends BaseActivity {  
  
    @BindView(R.id.toolbar)  
    Toolbar toolbar;  
    @BindView(R.id.drawerLayout)  
    DrawerLayout drawerLayout;  
    @BindView(R.id.navigationView)  
    NavigationView navigationView;  
    MovingImageView movingImageView;  
    private ActionBarDrawerToggle actionBarDrawerToggle;  
  
    @Override  
    protected void initView() {  
        movingImageView = navigationView.getHeaderView(0).findViewById(R.id.movingImageView);  
        // 设置左上角图标["三" —— "←"]效果  
        actionBarDrawerToggle = new ActionBarDrawerToggle(this, drawerLayout, toolbar, R.string.drawer_open, R.string.drawer_close);  
        actionBarDrawerToggle.syncState();  
        drawerLayout.addDrawerListener(actionBarDrawerToggle);  
  
        // 设置不允许 NavigationMenuView 滚动  
        NavigationMenuView navigationMenuView = (NavigationMenuView) navigationView.getChildAt(0);  
        if (navigationMenuView != null) {  
            navigationMenuView.setVerticalScrollBarEnabled(false);  
        }  
        // NavigationView 监听  
        navigationView.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() {  
            @Override  
            public boolean onNavigationItemSelected(@NonNull MenuItem item) {  
                switch (item.getItemId()) {  
                    case R.id.group_item_github:  
                        Toast.makeText(DrawerLayoutActivity.this,"项目主页",Toast.LENGTH_SHORT).show();  
                        break;  
                    case R.id.group_item_more:  
                        Toast.makeText(DrawerLayoutActivity.this,"更多内容",Toast.LENGTH_SHORT).show();  
                        break;  
                    case R.id.group_item_qr_code:  
                        Toast.makeText(DrawerLayoutActivity.this,"二维码",Toast.LENGTH_SHORT).show();  
                        break;  
                    case R.id.group_item_share_project:  
                        Toast.makeText(DrawerLayoutActivity.this,"分享项目",Toast.LENGTH_SHORT).show();  
                        break;  
                    case R.id.item_model:  
                        Toast.makeText(DrawerLayoutActivity.this,"夜间模式",Toast.LENGTH_SHORT).show();  
                        break;  
                    case R.id.item_about:  
                        Toast.makeText(DrawerLayoutActivity.this,"关于",Toast.LENGTH_SHORT).show();  
                        break;  
                }  
                item.setCheckable(false);  
                drawerLayout.closeDrawer(GravityCompat.START);  
                return true;  
            }  
        });  
    }  
  
    @Override  
    protected int getLayoutResID() {  
        return R.layout.activity_drawerlayout;  
    }  
}  

在这里插入图片描述

六、NavigationView 全屏效果


1、设置全屏显示

NavigationView 设置 android:layout_width="match_parent" 发现仍然无法实现全屏展示效果,如果想要 NavigationView 实现全屏展示,代码中按照如下设置:

ViewGroup.LayoutParams mLayoutParams = navigationView.getLayoutParams();  
int width = getResources().getDisplayMetrics().widthPixels;  
mLayoutParams.width = width;  
navigationView.setLayoutParams(mLayoutParams);  

2、仿 QQ 侧滑菜单效果

根据 NavigationView 全屏效果,模仿 QQ 个人中心侧滑菜单效果,其实我们发现 QQ 这么牛逼的软件,也是使用 Google 原生控件实现,可想而知 Material Design 系列控件之强大。QQ 侧滑菜单实现由以下两点组成:

  1. QQ 侧滑菜单铺满全屏

  2. 主页面跟随菜单一起滑动

第一点我们已经实现,只需要上面 4 行代码就可以设置 NavigationView 全屏效果,接下来只需要将主页面跟随菜单一起滑动就可以实现效果,我们回想前面讲到的 DrawerLayout.addDrawerListener() 方法,可以监听事件,其中有 4 个回调方法:

  • onDrawerSlide(View drawerView, float slideOffset) 当抽屉的位置改变时调用

  • onDrawerOpened(View drawerView) 打开侧滑界面触发

  • onDrawerClosed(View drawerView) 关闭侧滑界面触发

  • onDrawerStateChanged(int newState) 状态改变时触发

根据上面 4 个回调方法,要实现 QQ 效果,需要在 onDrawerSlide(View drawerView, float slideOffset) 方法中进行处理,其中 slideOffset 返回的是抽屉菜单从隐藏到打开的偏移,取值 0~1,drawerView 就是侧边菜单布局,具体实现代码如下:

<?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"  
    android:id="@+id/drawerLayout"  
    android:layout_width="match_parent"  
    android:layout_height="match_parent">  
  
    <ImageView  
        android:layout_width="match_parent"  
        android:layout_height="match_parent"  
        android:scaleType="centerCrop"  
        android:src="@mipmap/qq_1" />  
  
    <com.google.android.material.navigation.NavigationView  
        android:id="@+id/navigationView"  
        android:layout_width="match_parent"  
        android:layout_height="match_parent"  
        android:layout_gravity="start"  
        app:headerLayout="@layout/nav_header_main"  
        app:insetForeground="@android:color/transparent"  
        app:menu="@menu/activity_main_drawer" />  
  
</androidx.drawerlayout.widget.DrawerLayout>  
drawerLayout.addDrawerListener(new DrawerLayout.DrawerListener() {  
      @Override  
      public void onDrawerSlide(@NonNull View drawerView, float slideOffset) {  
          // 主页内容  
          View contentView = drawerLayout.getChildAt(0);  
          // 侧边栏  
          View menuView = drawerView;  
          // slideOffset 值默认是0~1  
          contentView.setTranslationX(menuView.getMeasuredWidth() * slideOffset);  
      }  
  
      @Override  
      public void onDrawerOpened(@NonNull View drawerView) {}  
  
      @Override  
      public void onDrawerClosed(@NonNull View drawerView) {}  
  
      @Override  
      public void onDrawerStateChanged(int newState) {}  
  });  

在这里插入图片描述

提示:仿 QQ 侧滑菜单效果,这里没有使用 ToolBar,直接截取了 2 张图片展示。

七、手动控制菜单

默认手势侧滑就可以 Open 菜单,点击空白处 Close。但是避免需要点击事件触发菜单状态,DrawerLayout 设计非常人性化,提供了以下方法来实现:

  1. openDrawer(View drawerView)

    打开指定的折叠项视图,将其动画到视图中。

  2. closeDrawer(View drawerView)

    关闭指定的折叠项视图,将其动画到视图中。

  3. closeDrawers()

    关闭所有当前打开的抽屉视图,通过动画他们的视野。

使用举例:

// 打开左边菜单  
mDrawerLayout.openDrawer(GravityCompat.START);  
// 打开右边菜单  
mDrawerLayout.openDrawer(GravityCompat.END);  
// 关闭左边菜单6  
mDrawerLayout.closeDrawer(GravityCompat.START);  
// 关闭所有菜单  
mDrawerLayout.closeDrawers();  

八、总结

Android Material Design Library 推出了很长时间,越来越多的 APP 使用了符合 Library 包的控件,DrawerLayout 绝对是热门之一,Material Design 定义了一个抽屉导航应该有何种外观和感受,统一了侧滑菜单和样式。在 Android 原生手机上对 DrawerLayout+NavigationView 更是使用到了极致。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值