Google官方Demo NavigationDrawer 侧边导航源码分析

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/kbkaaa/article/details/69955176

源码获取方式

主要功能

实现侧边导航栏,平时是隐藏的,通过点击特定按钮或者从屏幕边缘滑动可以打开导航抽屉。

效果图:
这里写图片描述

UI布局

根视图是一个DrawerLayout。
子视图第一个必须是界面的主内容(即导航隐藏时显示的内容),这是因为导航栏在打开时是要遮盖主内容的,所以主内容必须放在导航栏的前面。Demo中是一个FragmentLayout,高宽都设为match_parent,已达到填充整个页面的效果。
其后可以包含一到两个视图,对应左右两侧的导航栏。Demo中只有一个左侧导航栏视图,用的是RecyclerView。Google 建议导航栏的宽度不超过320dp,已保证导航打开时主内容还是部分可见的,Demo中设置为240dp 。这里必须指定android:layout_gravity属性,设置为start代码从左向右滑出,end则代表从右向左滑出。

Activity代码如下:

<!-- A DrawerLayout is intended to be used as the top-level content view using match_parent for both width and height to consume the full space available. -->
<android.support.v4.widget.DrawerLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/drawer_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:openDrawer="start" >

    <!-- As the main content view, the view below consumes the entire
         space available using match_parent in both dimensions. -->
    <FrameLayout
        android:id="@+id/content_frame"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    <!-- android:layout_gravity="start" tells DrawerLayout to treat
         this as a sliding drawer on the left side for left-to-right
         languages and on the right side for right-to-left languages.
         The drawer is given a fixed width in dp and extends the full height of
         the container. A solid background is used for contrast
         with the content view. -->
    <android.support.v7.widget.RecyclerView
        android:id="@+id/left_drawer"
        android:scrollbars="vertical"
        android:layout_width="240dp"
        android:layout_height="match_parent"
        android:layout_gravity="left|start"
        android:choiceMode="singleChoice"
        android:divider="@null"
        app:layoutManager="LinearLayoutManager"
        />
</android.support.v4.widget.DrawerLayout>

原理分析

Demo中DrawerLayout相关的功能主要有:
* 通过顶部工具栏按钮打开或关闭导航抽屉
* 通过侧滑打开或关闭导航抽屉
* 通过监听导航栏状态,变更顶部工具栏的标题,显示或隐藏搜索按钮

通过按钮开关抽屉

NavigationDrawerActivity继承自Activity,所以自带一个ActionBar。
首先使能ActionBar上的App图标,使其显示并能被点击:

// enable ActionBar app icon to behave as action to toggle nav drawer
getActionBar().setDisplayHomeAsUpEnabled(true);
getActionBar().setHomeButtonEnabled(true);

通过创建ActionBarDrawerToggle建立ActionBar和Drawer的关联,ActionBarDrawerToggle的构造函数包含5个参数,分别是主Activity、DrawerLayout对象、工具栏图标替换、打开描述、关闭描述。
创建后还要通过mDrawerLayout.setDrawerListener(mDrawerToggle)进行绑定。
代码如下:

// ActionBarDrawerToggle ties together the the proper interactions
    // between the sliding drawer and the action bar app icon
    mDrawerToggle = new ActionBarDrawerToggle(
            this,                  /* host Activity */
            mDrawerLayout,         /* DrawerLayout object */
            R.drawable.ic_drawer,  /* nav drawer image to replace 'Up' caret */
            R.string.drawer_open,  /* "open drawer" description for accessibility */
            R.string.drawer_close  /* "close drawer" description for accessibility */
    ) {
        public void onDrawerClosed(View view) {
            getActionBar().setTitle(mTitle);
            invalidateOptionsMenu(); // creates call to onPrepareOptionsMenu()
        }

        public void onDrawerOpened(View drawerView) {
            getActionBar().setTitle(mDrawerTitle);
            invalidateOptionsMenu(); // creates call to onPrepareOptionsMenu()
        }
    };
    mDrawerLayout.setDrawerListener(mDrawerToggle);

    if (savedInstanceState == null) {
        selectItem(0);
    }
}

并且onPostCreate和onConfigurationChanged中添加语句,代码如下:

/**
 * When using the ActionBarDrawerToggle, you must call it during
 * onPostCreate() and onConfigurationChanged()...
 */

@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);
    // Pass any configuration change to the drawer toggls
    mDrawerToggle.onConfigurationChanged(newConfig);
}

这样就实现了更改ActionBar中的图标,并且点击图标可以打开或关闭导航抽屉。

另外Demo中为实现ActionBar中搜索按钮的功能,重写了onOptionsItemSelected函数,因此需要作特殊处理,已保证导航栏打开关闭功能的正常。
在onOptionsItemSelected中,通过if (mDrawerToggle.onOptionsItemSelected(item))判断如果是点击的是App图标,则直接返回。
代码如下:

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    // The action bar home/up action should open or close the drawer.
    // ActionBarDrawerToggle will take care of this.
    if (mDrawerToggle.onOptionsItemSelected(item)) {
        return true;
    }
    // Handle action buttons
    switch (item.getItemId()) {
        case R.id.action_websearch:
            // create intent to perform web search for this planet
            Intent intent = new Intent(Intent.ACTION_WEB_SEARCH);
            intent.putExtra(SearchManager.QUERY, getActionBar().getTitle());
            // catch event that there's no activity to handle intent
            if (intent.resolveActivity(getPackageManager()) != null) {
                startActivity(intent);
            } else {
                Toast.makeText(this, R.string.app_not_available, Toast.LENGTH_LONG).show();
            }
            return true;
        default:
            return super.onOptionsItemSelected(item);
    }
}

通过侧滑打开或关闭导航

当导航抽屉位于左侧时,在左侧屏幕边缘向右滑动,导航抽屉会打开,打开后向左滑动或者点击主内容未被遮挡部分,导航抽屉会关闭。当导航抽屉位于右侧时则相反。
这些功能全部在DrawerLayout类中实现,使用时不需要做特殊处理。
通过抽屉视图的gravity来设置导航的位置和打开方向,设置为start则导航在左,从左到右打开,设置为end则相反。

android:layout_gravity="start"

也可以打开或关闭手势滑动

mDrawer_layout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED); //关闭手势滑动

mDrawer_layout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED); //打开手势滑动

监听导航栏状态

Demo中会监听导航栏是开启还是关闭,实现了以下效果:打开导航时,标题设置为Navigation Drawer,隐藏搜索按钮;关闭导航时,设置标题为选中的item,显示搜索按钮。
标题更改在ActionBarDrawerToggle的构造函数中通过重写onDrawerClosed(View view)和onDrawerOpened(View drawerView)实现:

public void onDrawerClosed(View view) {
    getActionBar().setTitle(mTitle);
    invalidateOptionsMenu(); // creates call to onPrepareOptionsMenu()
}

public void onDrawerOpened(View drawerView) {
    getActionBar().setTitle(mDrawerTitle);
    invalidateOptionsMenu(); // creates call to onPrepareOptionsMenu()
}

搜索按钮的显示和隐藏通过重写onPrepareOptionsMenu(Menu menu)实现:

public boolean onPrepareOptionsMenu(Menu menu) {
    // If the nav drawer is open, hide action items related to the content view
    boolean drawerOpen = mDrawerLayout.isDrawerOpen(mDrawerList);
    menu.findItem(R.id.action_websearch).setVisible(!drawerOpen);
    return super.onPrepareOptionsMenu(menu);
}

扩展

Google这个Demo的原始代码中的部分类已经被废弃了,我对Demo进行了少量修改,将v4.app.ActionBarDrawerToggle 替换为 v7.app.ActionBarDrawerToggle。 同时增加了一个右侧抽屉,并且在toolbar增加两个按钮来控制左右抽屉。
下载地址:
https://github.com/zhongchenyu/DrawerDemo

参考

Creating a Navigation Drawer:
https://developer.android.com/training/implementing-navigation/nav-drawer.html#Init

DrawerLayout API文档:
https://developer.android.com/reference/android/support/v4/widget/DrawerLayout.html

猜你喜欢

转载自blog.csdn.net/kbkaaa/article/details/69955176
今日推荐