androidAndroid 开发技术分享Android实战

Android DrawerLayout

2016-10-16  本文已影响1868人  狮_子歌歌

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 结合

上一篇下一篇

猜你喜欢

热点阅读