Material Design系列教程(9) - DrawLayout

简介

抽屉式导航栏是一个面板,它将应用的主要导航选项显示在屏幕左边缘。大多数情况下,它处于隐藏状态,但是如果用户从屏幕左边缘滑动手指,同时在应用顶层触摸操作栏中的应用图标,它将会显示出来。

在 Android 中,DrawerLayout 就是一种 抽屉式导航栏,是 Support Library 包中实现了具备 侧滑菜单 效果的控件。

DrawerLayout 不仅节省了屏幕空间,同时也实现了非常好的动画效果。

下面来看下 DrawerLayout 的官网文档:

《Material Design系列教程(9) - DrawLayout》 DrawerLayout

为了兼容低版本,我们这里使用 v4 兼容包里的 DrawerLayout

从文档中,我们可以看到,DrawerLayout 是一个 ViewGroup,且是具备 侧滑菜单 效果的父控件,因此,它的布局一般只包含两个子布局:
一个 内容布局(通常作为第一个子View)和 一个 侧滑菜单布局(通常作为第二个子View),这两种布局的区分在于android:layout_gravity属性的设置,若作为 侧滑菜单布局,可以通过设置android:layout_gravity="start",让子布局作为左侧侧滑菜单;反之,android:layout_gravity="end"则让子布局作为右侧侧滑菜单;内容布局 不能设置该属性。

DrawerLayout 简单使用

要添加抽屉式导航栏,一般将 DrawerLayout 作为用户界面的根布局。在 DrawerLayout 内,添加一个包含屏幕主内容(当抽屉式导航栏处于隐藏状态时为主要布局)的视图和另一个包含抽屉式导航栏内容的视图。

下面的布局中,主布局为一个TextView,具备左侧和右侧侧滑菜单,侧滑菜单内容都是一个TextView

<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <!--主内容布局-->
    <TextView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center"
        android:text="Main Content"
        android:textSize="40sp" />

    <!--左侧菜单栏-->
    <TextView
        android:layout_width="320dp"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        android:background="@color/colorPrimary"
        android:gravity="center"
        android:text="slide left menu bar"
        android:textColor="#00FF00"
        android:textSize="30sp" />

    <!--右侧菜单栏-->
    <TextView
        android:layout_width="320dp"
        android:layout_height="match_parent"
        android:layout_gravity="end"
        android:background="@color/colorPrimary"
        android:gravity="center"
        android:text="slide right menu bar"
        android:textColor="#00FF00"
        android:textSize="30sp" />
</android.support.v4.widget.DrawerLayout>

DrawerLayout 的使用就是这么的简单!你甚至都无需写代码,仅仅上面这个布局,就可以实现侧滑菜单栏功能。

效果如下:

《Material Design系列教程(9) - DrawLayout》 DrawerLayout_simple_usage

但是 DrawerLayout 的布局还是有一些需要注意的事项:

  • DrawerLayout 中,主内容视图(上面布局中的第一个TextView必须是第一个子视图,因为 XML 顺序意味着按 z 序(层叠顺序)排序,并且抽屉式导航栏必须位于内容顶部。 实际测试发现,主内容视图无需成为第一个子视图。甚至可以出现多个子布局作为主视图(效果类似FrameLayout,其子视图会重叠在一起)。

  • 主内容视图设置为匹配父视图的宽度和高度(match_parent), 因为在抽屉式导航栏处于隐藏状态时, 它代表整个 UI。

  • 抽屉式导航栏视图(左/右侧滑菜单栏)必须使用 android:layout_gravity 属性指定其水平重力。要支持左侧侧滑,请使用 "start"(而非 "left");要支持右侧侧滑,请使用 "end"

  • 抽屉式导航栏视图以 dp 为单位指定其宽度, 且高度与父视图相匹配。抽屉式导航栏的宽度不应超过 320dp,从而用户始终可以看到部分主内容。

ActionBarDrawerToggle

如果我们想监听侧滑菜单事件,那么可以通过覆写 DrawerLayout.DrawerListener 相应的回调方法,实现监听相应的侧滑菜单栏状态的事件。

《Material Design系列教程(9) - DrawLayout》 DrawerListener

但是,如果你的Activity包括 标题栏(ActionBar),那么建议使用 ActionBarDrawerToggle 监听抽屉式导航栏的打开和关闭事件。

我们先来看下 ActionBarDrawerToggle 官方文档:

《Material Design系列教程(9) - DrawLayout》 ActionBarDrawerToggle

从文档中可以看出,ActionBarDrawerToggle 实现了 DrawerLayout.DrawerListener,因此你仍然可以替代这些回调,但使用 ActionBarDrawerToggle 还有助于在标题栏图标与抽屉式导航栏之间正确交互。

因为在抽屉式导航栏设计指南中指出,您应在抽屉式导航栏可见时修改标题栏的内容,例如,更改标题和移除与主内容有关的操作项目。

一个常见的场景就是:用户可以通过远离或朝向屏幕的左边缘滑动手势打开和关闭抽屉式导航栏,但是如果您正在使用标题栏,还应允许用户通过触摸应用图标来打开和关闭它。 而且,应用图标还应通过特殊图标说明抽屉式导航栏的存在(即打开和关闭侧滑菜单栏时,标题栏的图标会随之改变,用以提示)。 使用 ActionBarDrawerToggle 就可以很方便地来实现所有这些行为。

总结一下,使用 ActionBarDrawerToggle 的好处就是可以方便且正确的实现 DrawerLayout 与 标题栏(ActionBar)之间的交互。

在举例之前,先来了解下 ActionBarDrawerToggle 的一些使用注意事项:

  • 要让 ActionBarDrawerToggle 正常运行,使用构造函数创建其实例时,需要以下参数:
     1. 托管抽屉式导航栏的 Activity
     2. DrawerLayout 实例
     3. 用作抽屉式导航栏指示器的可绘制对象资源。下载操作栏图标包中提供了标准抽屉式导航栏图标
     4. 用于描述“打开抽屉式导航栏”操作的字符串资源(用于无障碍功能)
     5. 用于描述“关闭抽屉式导航栏”操作的字符串资源(用于无障碍功能)

然后,无论你是否已创建 ActionBarDrawerToggle 的子类作为抽屉式导航栏监听器,在整个Activity生命周期中,你都需要在几个位置调用 ActionBarDrawerToggle

public class MainActivity extends Activity {
    private DrawerLayout mDrawerLayout;
    private ActionBarDrawerToggle mDrawerToggle;
    ...

    public void onCreate(Bundle savedInstanceState) {
        ...

        mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
        mDrawerToggle = new ActionBarDrawerToggle(
                this,                  /* host Activity */
                mDrawerLayout,         /* DrawerLayout object */
                R.drawable.ic_drawer,  /* nav drawer icon to replace 'Up' caret */
                R.string.drawer_open,  /* "open drawer" description */
                R.string.drawer_close  /* "close drawer" description */
                ) {

            /** Called when a drawer has settled in a completely closed state. */
            public void onDrawerClosed(View view) {
                super.onDrawerClosed(view);
                getActionBar().setTitle(mTitle);
            }

            /** Called when a drawer has settled in a completely open state. */
            public void onDrawerOpened(View drawerView) {
                super.onDrawerOpened(drawerView);
                getActionBar().setTitle(mDrawerTitle);
            }
        };

        // Set the drawer toggle as the DrawerListener
        mDrawerLayout.setDrawerListener(mDrawerToggle);

        getActionBar().setDisplayHomeAsUpEnabled(true);
        getActionBar().setHomeButtonEnabled(true);
    }

    @Override
    protected void onPostCreate(Bundle savedInstanceState) {
        super.onPostCreate(savedInstanceState);
        // Sync the toggle state after onRestoreInstanceState has occurred.
        mDrawerToggle.syncState();
    }

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        mDrawerToggle.onConfigurationChanged(newConfig);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Pass the event to ActionBarDrawerToggle, if it returns
        // true, then it has handled the app icon touch event
        if (mDrawerToggle.onOptionsItemSelected(item)) {
          return true;
        }
        // Handle your other action bar items...

        return super.onOptionsItemSelected(item);
    }

    ...
}

其实主要就是要注意下Activity的三个方法:onConfigurationChangedonPostCreateonOptionsItemSelected,在这些方法中让 ActionBarDrawerToggle 处理一下 。

最后,举个例子,实现的功能是监听 DrawerLayout 的打开和关闭事件,然后标题栏图标随之进行改变,用以提示。(ps:此处的标题栏我们使用 Toolbar

布局如下:

<?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="match_parent"
    android:orientation="vertical">

    <android.support.v7.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="?android:attr/actionBarSize"
        android:background="?attr/colorPrimary"
        android:theme="@style/Theme.Drawer.ArrowAnimation"
        app:title="I am Toolbar" />

    <android.support.v4.widget.DrawerLayout
        android:id="@+id/drawerlayout"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <TextView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gravity="center"
            android:text="Main Content"
            android:textSize="30sp" />>

        <TextView
            android:layout_width="300dp"
            android:layout_height="match_parent"
            android:layout_gravity="start"
            android:background="@color/colorPrimary"
            android:gravity="center"
            android:text="slide left menu bar"
            android:textColor="#00FF00"
            android:textSize="20sp" />>

        <TextView
            android:layout_width="300dp"
            android:layout_height="match_parent"
            android:layout_gravity="end"
            android:background="@color/colorPrimary"
            android:gravity="center"
            android:text="slide right menu bar"
            android:textColor="#00FF00"
            android:textSize="20sp" />>
    </android.support.v4.widget.DrawerLayout>
</LinearLayout>

布局跟我们前面示例就只增加了一个 Toolbar。其中,给 Toolbar 设置的样式源码如下,只是改变了 Toolbar 图标的颜色而已:

<!-- file: values/styles.xml -->
<style name="Theme.Drawer.ArrowAnimation" parent="AppTheme">
    <!-- 箭头 -->
    <item name="drawerArrowStyle">@style/AppTheme.DrawerArrowToggle</item>
</style>
<!-- 箭头颜色 -->
<style name="AppTheme.DrawerArrowToggle" parent="Base.Widget.AppCompat.DrawerArrowToggle">
    <item name="color">@android:color/white</item>
</style>

源码如下:

public class DrawerLayoutActivity extends AppCompatActivity {

    @BindView(R.id.toolbar)
    private Toolbar mToolbar;

    @BindView(R.id.drawerlayout)
    private DrawerLayout mDrawerLayout;

    private ActionBarDrawerToggle mDrawerToggle;

    @Override
    @ViewInject(ViewInject.ACTIVITY)
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_drawlayout);
        initView();
    }

    private void initView() {
        this.mDrawerToggle = new ActionBarDrawerToggle(this,
                this.mDrawerLayout,
                this.mToolbar,
                R.string.drawer_open,
                R.string.drawer_close) {
            @Override
            public void onDrawerOpened(View drawerView) {
                super.onDrawerOpened(drawerView);
                Toast.makeText(DrawerLayoutActivity.this, "onDrawerOpened", Toast.LENGTH_SHORT).show();
            }

            @Override
            public void onDrawerClosed(View drawerView) {
                super.onDrawerClosed(drawerView);
                Toast.makeText(DrawerLayoutActivity.this, "onDrawerClosed", Toast.LENGTH_SHORT).show();
            }
        };
        this.mDrawerLayout.addDrawerListener(this.mDrawerToggle);
    }

    @Override
    protected void onPostCreate(Bundle savedInstanceState) {
        super.onPostCreate(savedInstanceState);
        // Sync the toggle state after onRestoreInstanceState has occurred.
        mDrawerToggle.syncState();
    }

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        mDrawerToggle.onConfigurationChanged(newConfig);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Pass the event to ActionBarDrawerToggle, if it returns
        // true, then it has handled the app icon touch event
        if (mDrawerToggle.onOptionsItemSelected(item)) {
            return true;
        }
        // Handle your other action bar items...

        return super.onOptionsItemSelected(item);
    }
}

其实源码主要就是构造一个 ActionBarDrawerToggle,然后调用 DrawerLayoutaddDrawerListener方法监听 DrawerLayout 的开启与关闭。没有其他特殊的东西。

从布局文件和源码可以看到,我们根本没有对 Toolbar 进行任何配置,只是单纯的将它传递给 ActionBarDrawerToggle(在
ActionBarDrawerToggle 的构造函数中传递),Toolbar 的图标就自动响应 DrawerLayout 的打开与关闭了。

具体效果如下图所示:

《Material Design系列教程(9) - DrawLayout》 ActionBarDrawerToggle_navigation_icon

如果你对上面这种动画的效果不满意,也可以考虑一下 material-menu 的另一种实现效果。

参考

    原文作者:Whyn
    原文地址: https://www.jianshu.com/p/2f05e71769ad
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞