sq_Hayden's blog

Material Design系列实践(一)

2018/06/04 Share

ToolBar+DrawerLayout+NavigationView实现滑动菜单效果

  • 引言:Google新推出的界面设计语言–Material Design使得我们在针对android的界面UI开发上省力了不少,它涵盖了许许多多的优秀控件以及设计,本篇博客介绍了其中的几个控件组合实现滑动菜单栏的效果,下面就一起来看看吧。

1.ToolBar的相关设置

1.ToolBar的概念

  Toolbar是Material Design所建议使用的顶部导航栏控件,用以取代以前的ActionBar。相对于ActionBar而言,ToolBar在使用上更加的灵活,不仅可以实现与ActionBar完全相似的效果,还可以使我们对顶部导航栏进行统一的设计与管理,从而定制一些个性化的导航栏,大大增加了灵活性。那么接下来就让我们看看如何去使用它。

2.ToolBar的引入

  在引入ToolBar之前,需要对我们的Activity主题进行设置。在默认的情况下,我们的Activity是采用的ActionBar来作为导航栏布局的,那么我们去到项目的资源路径下,找到style.xml查看此时所采用的默认布局,下面是我的默认布局:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
    <resources>

<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>

</resources>
```

  那么,为了替换为ToolBar控件,就需要禁用掉相关的ActionBar,所以我们改变系统的默认主题,从而完成对ActionBar的禁用操作。替换主题如下:

```java
<resources>

<!-- Base application theme. -->
//只需要替换为"NoActionBar即可"
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>

</resources>

  接下来,我们就可以在主Activity布局中,引入ToolBar控件,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
//这里为了和网页的白色背景作区分,将主视图背景设置为灰色
android:background="@color/gray"
>

<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
//将ToolBar设置为深色系
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
//将菜单项等子项设置为浅色系
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
android:layout_alignParentTop="true"
>
</android.support.v7.widget.Toolbar>

</RelativeLayout>

  到了这一步,我们的导航栏控件就引入成功了,那么,上述的ToolBar控件中的相关参数究竟都代表了什么意思呢?
  height设置的意思是ToolBar的高度与系统的ActionBar的默认高度一致
  background设置了toolbar的背景色为系统的主色调
  在上面的时候,我们设置的主题是浅色系的风格,在这种风格下,ToolBar中的内容为了与背景作区分,将会显示为深色系,例如字体变黑色,显得比较难看,为了使其显示出与我们的总体布局相符合的状态,我们需要对其进行单独设置。一来要将ToolBar设置为深色系,二来将其中的内容(比如菜单项)设置为浅色系,这就是上述的两行主题设置的原因。
  显示的效果如下:
  

toolBar

2.DrawerLayout的相关设置

1.DrawerLayout的概念

  DrawerLayout是谷歌推出的一款滑动布局,利用它我们可以很轻松地实现非常炫酷的滑动菜单效果。在以前,要实现滑动菜单效果,我们不仅需要去设计布局,更要计算菜单滑动出来的距离以及检测手势控制,可谓十分麻烦。而现在,这些都不需要了,我们只需要引入DrawerLayout布局,就能够完成滑动效果。那么,接下来我们看看如何去实现。

2.DrawerLayout的实现

   //这里使用DrawerLayout作为根布局
   <android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@+id/drawer_layout"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    tools:openDrawer="start">
    //这是主视图布局
    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@color/gray"
        >
        //引入ToolBar
        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
            app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
            android:layout_alignParentTop="true"
            >
        </android.support.v7.widget.Toolbar>

    </RelativeLayout>
    //这是侧滑栏布局
    <TextView
        android:layout_width="400dp"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        android:text="这里是侧滑栏中的内容"
        android:textSize="25sp"
        android:background="@color/colorAccent"
        />

</android.support.v4.widget.DrawerLayout>

  代码如上所示:可以看到在这里我们将DrawerLayout作为了根布局进行使用,这是因为如果它作为子布局的话,可能会出现一些滑动上面的问题,因此,建议作为根布局使用。在它的里面,其实分为了两大块,一个是我们进入后所看到的主界面布局(即代码中的RelativeLayout布局),一个是我们的侧滑栏中的布局(TextView布局)。而这两块的区分,是依据我们在布局文件中的控件顺序决定的(先调用的将作为页面的主体布局)。下面针对DrawerLayout布局中的特殊属性作一个说明:
  DrawerLayout根布局中的openDrawer属性,表明了我们想要让侧滑栏从哪边划出,可设置为左、右以及自动,自动表明由系统判断,中文左划,需要从右读起的语言则会默认右划。
  另一个地方就是我们第二个控件布局(即作为侧滑栏的控件)需要设置一下划出方式,设置的方式是设定其layout_gravity属性,”start“表示左划,以此类推。
  此时,我们的侧滑栏就已经可以展现出来了,效果如下图所示:
   

drawerLayout

  现在还有两个问题,一是我们发现左边是否有侧滑栏我们虽然知道,但是用户却并不知情,为了让用户了解到我们的界面拥有侧滑效果,我们需要在我们的ToolBar上,显示一个图标并与我们的侧滑栏绑定起来,这样用户就可以知道我们是带有侧滑栏的;二是我们虽然实现了侧滑的效果,但是可以看到,侧滑栏中的界面是很丑陋的,为此,我们需要引入NavigationVIew来填充我们的DrawLayout,从而使其变得漂亮美观。那么,接下来,我们就先解决第一个问题。

3.DrawerLayout与ToolBar的绑定

  ToolBar导航栏的最左侧,有一个名为HomeAsUp的控件,它的默认图标是一个小箭头,作用是用来返回到上一层活动,我们的思路就是将它的HomeAsUp控件图标和功能都进行改写,从而让它与NavigationView关联起来。具体做法如下,上代码:

 /**
 * 设置视图
 */
private void initView() {
    //获取并设置Toolbar
    Toolbar toolbar = findViewById(R.id.toolbar);
    setSupportActionBar(toolbar);
    //获取总视图
    mDrawerLayout = findViewById(R.id.drawer_layout);
    ActionBar actionBar = getSupportActionBar();
    actionBar.setTitle("我是ToolBar");
    if(actionBar!=null){
        //设置HomeAsUp控件显示
        actionBar.setDisplayHomeAsUpEnabled(true);
        //变更HomeAsUp控件的图片
        actionBar.setHomeAsUpIndicator(R.mipmap.icon);
    }
}

/**
 * 监测ToolBar的点击事件
 */
@Override
public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()){
        //android.R.id.Home作为HomeAsUp控件的id是不变的
        case android.R.id.home:
            //监测到点击事件时打开DrawerLayout
            mDrawerLayout.openDrawer(GravityCompat.START);
            break;
    }
    return true;
}

  此时,我们就实现了点击ToolBar左上角的提示图标,展开DrawerLayout的操作。如下图所示:
  

HomeAsUp

3.NavigationView的相关设置

1.NavigationView的概念

  NavigationView即导航布局,其使用场景大多就是结合DrawerLayout来实现比较炫酷的导航菜单栏的效果,我们还需要结合菜单布局,头布局,来一起完成最终的展示。不过不用着急,接下来我们就用代码去慢慢实现这个过程。

2.NavigationView的实现

  首先是布局的引入,如下面所示,此时一定得注意,我们的这个布局是作为侧滑栏中的布局去填充的,因此,需要将其放在主布局的下面,作为第二个布局去使用。主布局代码如下:

  //根布局自然是DrawerLayout了
  <android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@+id/drawer_layout"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    tools:openDrawer="start">
    //主布局相关视图
    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@color/gray"
        >
        //这里是我们定义的ToolBar
        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
            app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
            android:layout_alignParentTop="true"
            >
        </android.support.v7.widget.Toolbar>

    </RelativeLayout>
    //引入到DrawerLayout中去的NavigationView视图控件
    <android.support.design.widget.NavigationView
        android:id="@+id/nav_view"
        android:layout_width="400dp"
        android:layout_height="match_parent"
        android:layout_gravity = "start"
        //这里需要引入菜单布局
        app:menu="@menu/nav_menu"
        //这里引入头布局实现头布局效果
        app:headerLayout="@layout/nav_header"
        >
    </android.support.design.widget.NavigationView>

</android.support.v4.widget.DrawerLayout>

  主布局的任务到这里就结束了,下来是我们的头布局,主要作用是用来给侧滑栏单独定制一个头部的布局,我们看看布局代码nav_header.xml:

<?xml version="1.0" encoding="utf-8"?>
 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="180dp"
     android:padding="10dp"
     android:background="?attr/colorPrimary"
     >
     <de.hdodenhof.circleimageview.CircleImageView
         android:id="@+id/icon_image"
         android:layout_width="70dp"
         android:layout_height="70dp"
         android:src="@mipmap/cat_head"
         android:layout_centerInParent="true"
         />
     <TextView
         android:id="@+id/head_name"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_below="@id/icon_image"
         android:layout_marginTop="10dp"
         android:layout_centerInParent="true"
         android:textColor="#FFF"
         android:textSize="20sp"
         android:text="小猫"
         />
 </RelativeLayout>

  头布局里导入了一个图片处理库CircleImageView,其作用就是实现了将图片的形状转换为圆形的、类似头像的效果(最好是正方形的图)。
  再就是最后一个,也就是我们的菜单栏布局:nav_menu.xml,其实现了菜单栏中的各项数据的图片信息以及名称等,如下所示:

   <menu xmlns:android="http://schemas.android.com/apk/res/android">
     //此属性为了保证菜单栏各个Item子项均处于单选状态
     <group android:checkableBehavior="single">
         <!--订单-->
         <item android:id="@+id/nav_order"
             android:icon="@mipmap/order"
             android:title="我的订单"/>
         <!--个人信息-->
         <item android:id="@+id/nav_identity"
             android:icon="@mipmap/identity"
             android:title="个人信息"/>
         <!--设置-->
         <item android:id="@+id/nav_setting"
             android:icon="@mipmap/setting"
             android:title="相关设置"/>
     </group>
</menu>

  在这里我们需要注意,建立菜单栏的布局文件时,我们首先需要在res资源文件夹下新创建menu文件夹,这个文件夹不能通过New→Directory去建立,而是应该通过New→Android Resource Directory→Resource Type→menu去建立一个menu的资源文件夹,然后在里面建立资源型xml文件nav_menu.xml这样才能成功创建。
  最后,让我们在项目模块的build.gradle中,实现对图片处理库的引用,以及对NavigationView的引用:

//NavigationView控件相关包
compile 'com.android.support:design:26.1.0'
compile 'com.android.support:appcompat-v7:26.1.0'
//头像圆角化相关
compile 'de.hdodenhof:circleimageview:2.1.0'

  大功告成,让我们看看现在的效果吧:
   

header

  到这里,我们的功能算是实现了。最后,有两个小的知识点需要总结一下:
  1.如何实现对NavigationView中的子项Item添加监听:

//设置菜单选中监听器,关闭侧滑栏
//获取侧滑栏
NavigationView navView = findViewById(R.id.nav_view);
navView.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() {
    @Override
    public boolean onNavigationItemSelected(@NonNull MenuItem item) {
        mDrawerLayout.closeDrawers();
        switch (item.getItemId()){
            //监听菜单中各子项定义的ID即可
            case R.id.nav_order:
                break;
            case R.id.nav_identity:
                break;
        }
        return true;
    }
});

2.如何实现对头布局中的控件进行监听(注:若在此处刷出头布局,则在布局文件中无需设置对头布局文件的引用):

//用NavView去得到其头布局视图
View view = navView.inflateHeaderView(R.layout.nav_header);
//获取姓名控件
naviName = view.findViewById(R.id.head_name);

  好了,这篇博客就写到这里,后期会继续总结一些Material Design中比较炫酷的控件实现,敬请期待。

  

CATALOG
  1. 1. ToolBar+DrawerLayout+NavigationView实现滑动菜单效果
    1. 1.1. 1.ToolBar的相关设置
      1. 1.1.1. 1.ToolBar的概念
      2. 1.1.2. 2.ToolBar的引入
    2. 1.2. 2.DrawerLayout的相关设置
      1. 1.2.1. 1.DrawerLayout的概念
      2. 1.2.2. 2.DrawerLayout的实现
      3. 1.2.3. 3.DrawerLayout与ToolBar的绑定
    3. 1.3. 3.NavigationView的相关设置
      1. 1.3.1. 1.NavigationView的概念
      2. 1.3.2. 2.NavigationView的实现