Android DrawerLayout

DrawerLayout

DrawerLayout,官方给我们提供的一个侧滑菜单
控件,3.0以后引入。为了兼容低版本,可以使用v4兼容包里的DrawerLayout。随着Material Design设计规范的发布,越来越多的应用用到了侧滑菜单的动画效果。Toolbar和DrawerLayout的组合可以实现非常好的效果。

首先来看下总体的效果,网易云音乐App:

网易云音乐.png

DrawerLayout基本使用

一般情况下,DrawerLayout第一个子视图就是主内容布局,也就是打开App第一眼看到的布局;而第二个子视图就是侧滑菜单布局。区分这两个布局的关键属性android:layout_gravity.设置了这个属性的布局,就是侧滑菜单布局,而它的值决定了侧滑的方向。常规规范在编写布局文件时,第一个子布局是内容布局。

当android:gravity值为start|left时,侧滑菜单侧滑方式是左侧滑出;相反值为end|right时,侧滑菜单侧滑方式为右侧滑出。在编写主内容布局文件时,高宽必须是match_parent否则会报错

DrawerLayout activity布局文件

<?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"
    android:id="@+id/id_drawer_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <android.support.v7.widget.Toolbar
            android:id="@+id/id_drawer_layout_toolbar"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="@color/light_menu_header"
            android:minHeight="?android:actionBarSize"
            android:paddingTop="@dimen/padding_top"
            android:gravity="center_vertical"
            android:elevation="4dp"
            app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
            app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
            app:title="@string/zhuhu_name"/>

        <FrameLayout
            android:id="@+id/id_content"
            android:layout_width="match_parent"
            android:layout_height="match_parent"></FrameLayout>
    </LinearLayout>

    <include
        layout="@layout/draw_menu_layout"/>
</android.support.v4.widget.DrawerLayout>

Drawerlayout Menu布局文件

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="280dp"
    android:layout_gravity="start"
    android:layout_height="match_parent"
    android:clickable="true"
    android:orientation="vertical">

    <LinearLayout
        android:id="@+id/id_draw_menu_header"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@color/light_menu_header"
        android:orientation="vertical"
        android:paddingBottom="10dp"
        android:paddingTop="@dimen/padding_top">

        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="15dp"
            android:layout_marginRight="15dp"
            android:orientation="horizontal">

            <ImageView
                android:layout_width="36dp"
                android:layout_height="36dp"
                android:background="@drawable/ic_account_circle_white_24dp"/>

            <TextView
                android:id="@+id/id_draw_menu_item_login_tv"
                android:layout_width="match_parent"
                android:layout_height="36dp"
                android:layout_marginLeft="10dp"
                android:gravity="center_vertical"
                android:text="@string/draw_menu_item_login_tv"
                android:textColor="@android:color/white"
                android:textSize="18sp"/>
        </LinearLayout>

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="15dp"
            android:orientation="horizontal">

            <TextView
                android:id="@+id/id_draw_menu_item_backup"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:drawableLeft="@drawable/ic_star_white_24dp"
                android:gravity="center"
                android:text="@string/draw_menu_item_backup"
                android:textColor="@android:color/white"
                android:textSize="15sp"/>

            <TextView
                android:id="@+id/id_draw_menu_item_download"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_marginLeft="20dp"
                android:layout_weight="1"
                android:drawableLeft="@drawable/ic_file_download_white_24dp"
                android:gravity="center"
                android:text="@string/draw_menu_item_download"
                android:textColor="@android:color/white"
                android:textSize="15sp"/>
        </LinearLayout>
    </LinearLayout>

    <TextView
        android:id="@+id/id_draw_menu_item_main_tv"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@color/light_menu_index_background"
        android:paddingBottom="10dp"
        android:paddingLeft="80dp"
        android:paddingTop="10dp"
        android:text="@string/draw_menu_item_main"
        android:textColor="@android:color/holo_blue_dark"
        android:textSize="18sp"
        />

    <ListView
        android:id="@+id/id_draw_menu_item_list_select"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@android:color/white"
        android:divider="@android:color/transparent"
        android:scrollbars="none"></ListView>
</LinearLayout>

上述布局文件中有两个关键属性:android:clickableandroid:layout_width。将android:clickable设置为true是为了消费点击在DrawerLayout Menu Header布局上的事件(很多应用menu头部是一个图片,本身不能消费事件。这样事件就传递到了Menu布局下方的内容布局,会导致误操作)。android:layout_width属性决定了侧滑菜单的宽度,官方建议一般小于320dp。

DrawerLayout clickable false.gif

Activity中初始化

package android.example.com.toolbarusage;

import android.content.res.TypedArray;
import android.graphics.Color;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.content.ContextCompat;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.ActionBarDrawerToggle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.Menu;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;

/**
 * Created by syt on 16/10/16.
 */
public class ZhuHuDrawerActivity extends AppCompatActivity {
    private Toolbar mToolbar;
    private DrawerLayout mDrawerlayout;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        supportRequestWindowFeature(Window.FEATURE_NO_TITLE);
        translucentStatusBar();
        setContentView(R.layout.activity_drawer_layout);
        initView();
    }

    private void initView() {
        mToolbar = (Toolbar) findViewById(R.id.id_drawer_layout_toolbar);
        //mToolbar.setMinimumHeight(calculateToolbarHeight());
        //mToolbar.inflateMenu(R.menu.draw_layout_menu);
        setSupportActionBar(mToolbar);
        mToolbar.setNavigationIcon(R.mipmap.ic_drawer_home);
        mDrawerlayout = (DrawerLayout) findViewById(R.id.id_drawer_layout);
        //DrawerLayout监听器
        ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
                this,
                mDrawerlayout,
                mToolbar,
                R.string.app_name,
                R.string.app_name
        );
        mDrawerlayout.addDrawerListener(toggle);
        toggle.syncState();
    }

    private int calculateToolbarHeight() {
        int idStatusBar = getResources().getIdentifier("status_bar_height", "dimen", "android");
        int statusBarHeight = getResources().getDimensionPixelSize(idStatusBar);
        TypedArray ta = obtainStyledAttributes(new int[]{android.R.attr.actionBarSize});
        int actionBarHeight = ta.getDimensionPixelSize(0, 0);
        return statusBarHeight + actionBarHeight;
    }

    /**
     * 实现5.0以上状态栏透明(默认状态是半透明)
     */
    private void translucentStatusBar() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            ViewGroup decorView = (ViewGroup) getWindow().getDecorView();
            int option =
                    View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
            decorView.setSystemUiVisibility(option);
            getWindow().setStatusBarColor(ContextCompat.getColor(
                    getApplicationContext(),
                    R.color.light_menu_header
            )
            );
        }

    }

    /**
     * 当使用setSupportActionBar()时,调用Toolbar.inflateMenu()时无效果,必须用此方法实现
     * @param menu
     * @return
     */
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.draw_layout_menu, menu);
        return true;
    }
}

代码分析:先去除AppCompatActivity自带的ActionBar,通过findVewiById()获取到Toolbar空间对象后,调用setSupportActionBar()方法替换掉默认的ActionBar。这样操作之后又一个问题,直接调用Toolbar.inflateMenu()后Menu并没有显示出来,而是要通过onCreateOptionsMenu()去加载menu布局文件才可以实现效果。最后创建ActionBarDrawerToggle监听器,并且绑定DrawerLayout对象,实现监听侧滑菜单的显示和隐藏事件。同时适配了5.0以上的修改了状态栏颜色,保证Toolbar和DrawerLayout Menu实现状态栏的沉浸。

最终效果:

DrawerLayout.gif

ActionBarDrawerToggle

监听侧滑菜单事件可以使用DrawerLayout.DrawerListener,并重写相应的回调方法,实现监听相应的侧滑菜单栏状态的事件。那为什么用ActionBarDrawerToggle
ActionBarDrawerToggle,它是DrawerLayout.DrawerListener的具体实现类

ActionBarDrawerToggle是一个开关,用于打开/关闭DrawerLayout抽屉

ActionBarDrawerToggle 提供了一个方便的方式来配合DrawerLayout和ActionBar,以实现推荐的抽屉功能。即点击ActionBar的home按钮,即可弹出DrawerLayout抽屉。

虽然这里的demo用的是Toolbar,但是通过这个监听器,一样可以实现上述效果。我猜测原因是setSupportActionBar()方法的作用。

在Activity中的两个回调函数中使用它:
onConfigurationChanged,
onOptionsItemSelected
调用ActionBarDrawerToggle.syncState() 在Activity的onPostCreate()中:指示,ActionBarDrawerToggle与DrawerLayout的状态同步,并将ActionBarDrawerToggle中的drawer图标设置为ActionBar的Home-Button的icon。

可以通过重写ActionBarDrawerToggle的onDrawerOpened()和onDrawerClosed()以监听抽屉拉出
或隐藏事件。

问题总结

DrawerLayout.setDrawerListener()

该方法已经废弃,可以使用addDrawerListener()。

setSupportActionBar()

调用此方法以后,原先加载的menu布局不显示,需要通过onCreateOptionsMenu()方法来实现。

DrawerLayout Menu状态栏沉浸

之前为了给Toolbar实现状态栏沉浸效果,将状态栏悬浮在DecorView之上,所以当滑出侧滑菜单栏时,状态栏遮挡住了一部分显示内容。解决方案就是给DrawerLayout Menu布局文件的header布局添加一个paddingTop属性,其值为状态栏高度。

误操作

给DrawerLayout Menu根布局设置android:clickable属性,其值设置为true。是为了消费点击在DrawerLayout Menu Header布局上的事件(很多应用menu头部是一个图片,本身不能消费事件。这样事件就传递到了Menu布局下方的内容布局,会导致误操作)。

参考

Android开发:最详细的 NavigationDrawer 开发实践总结

Android基础入门教程——2.4.15 DrawerLayout(官方侧滑菜单)的简单使用

Android开发之DrawerLayout实现抽屉效果

Android ActionBarDrawerToggle、DrawerLayout、ActionBar 结合

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269

推荐阅读更多精彩内容