Android DrawerLayout

作者: 狮_子歌歌 | 来源:发表于2016-10-16 20:14 被阅读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 结合

    相关文章

      网友评论

      • 6ee72519b51d:在编写主内容布局文件时,高宽必须是match_parent否则会报错。不会报错哦,如果主内容布局不是match_parent 他会强制转成match_parent
      • 沙小树:不断加油!我也开学一个月了,希望C++能够学的更好
        狮_子歌歌:@沙小树 加油哦:clap:

      本文标题:Android DrawerLayout

      本文链接:https://www.haomeiwen.com/subject/fxhayttx.html