DrawerLayout的基本使用

本文介绍了如何在Android应用中使用DrawerLayout组件来创建侧拉抽屉,包括其基本概念、配置方法、与Actionbar的交互以及优化技巧。通过实例演示了如何合理布局主内容和抽屉内容,以及如何利用DrawerLayout.DrawerListener监听抽屉的打开和关闭事件,以提升用户体验。

DrawerLayout

介绍

DrawerLayout作为最顶部的View容器提供侧拉抽屉的功能.他依靠子View的android:layout_gravity属性来决定侧拉抽屉放置的方向.(记得使用start/end来支持不同的布局方向)

要使用DrawerLayout,将其作为你的根布局,然后主内容作为第一个子View添加进来,并且使用match_parent指定高度和宽度,接着侧拉抽屉的内容作为第二个子View添加进来,使用layout_gravity来控制侧拉抽屉放置的方向.一般侧拉抽屉使用match_parent作为高度,并且指定一个确定的宽度.

DrawerLayout.DrawerListener可以用来监听抽屉的动作和状态,避免在抽屉动画的时候执行layout动作等费时的操作,否则会导致卡顿,尽量在STATE_IDLE状态执行这些费时的操作.另外DrawerLayout.SimpleDrawerListener提供了默认的实现,你可以继承他来挑你感兴趣的进行监听.

根据安卓设计规范,任何在左边的抽屉都应该执行导航的操作,任何在右边的抽屉都应该执行对当前内容的一些操作,这个和Action Bar的逻辑相呼应.

例子

创建布局

首先将android.support.v4.widget.DrawerLayout添加到根布局,然后添加主内容(在你抽屉隐藏时需要显示的内容),接着添加抽屉.

<android.support.v4.widget.DrawerLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/drawer_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <!-- The main content view -->
    <FrameLayout
        android:id="@+id/content_frame"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
    <!-- The navigation drawer -->
    <ListView android:id="@+id/left_drawer"
        android:layout_width="240dp"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        android:choiceMode="singleChoice"
        android:divider="@android:color/transparent"
        android:dividerHeight="0dp"
        android:background="#111"/>
</android.support.v4.widget.DrawerLayout>
  • 主内容必须是第一个DrawerLayout的子View,因为抽屉要显示在主内容的上方.
  • 主内容View必须设置宽高为match_parent,因为当抽屉隐藏时它代表你整个的UI内容.
  • 抽屉View必须设置androidLlayout_gravity属性为水平方向的值left/right或者为了支持其他水平布局的语言start/end
  • 抽屉View应当指定高度为match_parent,宽度为不大于320dp的值,这样可以保证抽屉显示时也可以看到主内容的一部分.

初始化抽屉的内容

就在Activity的onCreate方法初始化抽屉的内容就可以了,并不需要特殊处理.

public class MainActivity extends Activity {
    private String[] mPlanetTitles;
    private DrawerLayout mDrawerLayout;
    private ListView mDrawerList;
    ...

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mPlanetTitles = getResources().getStringArray(R.array.planets_array);
        mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
        mDrawerList = (ListView) findViewById(R.id.left_drawer);

        mDrawerList.setAdapter(new ArrayAdapter<String>(this,
                R.layout.drawer_list_item, mPlanetTitles));

        mDrawerList.setOnItemClickListener(new DrawerItemClickListener());

        ...
    }
}

监听抽屉的打开和关闭事件

通过你的DrawerLayout对象的setDrawerListener()设置一个DrawerLayout.DrawerListener来监听.
如果你使用Actionbar,那么你可以继承ActionBarDrawerToggle类.这个类也实现了DrawerLayout.DrawerListener接口,所以你还可以通过复写那些回调方法,而且,那个类也自动的帮你处理了action bar图标和抽屉的交互.

根据抽屉使用规范,你应该在抽屉显示的时候改变action bar的内容,例如改变title或者移除相对于主内容上下文关系的action.以下代码展示了你可以通过一个ActionBarDrawerToggle实例来复写DrawerLayout.DrawerListener的回调方法.

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

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ...

        mTitle = mDrawerTitle = getTitle();
        mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
        mDrawerToggle = new ActionBarDrawerToggle(this, mDrawerLayout,
                R.drawable.ic_drawer, R.string.drawer_open, R.string.drawer_close) {

            /** 当抽屉完成关闭的时候调用. */
            public void onDrawerClosed(View view) {
                super.onDrawerClosed(view);
                getActionBar().setTitle(mTitle);
                invalidateOptionsMenu(); 
            }

            /** 当抽屉彻底打开的时候调用. */
            public void onDrawerOpened(View drawerView) {
                super.onDrawerOpened(drawerView);
                getActionBar().setTitle(mDrawerTitle);
                invalidateOptionsMenu(); 
            }
        };

        // 把drawer toggle当做DrawerListener一样使用
        mDrawerLayout.setDrawerListener(mDrawerToggle);
    }

    /* 当我们调用invalidateOptionsMenu()时调用 */
    @Override
    public boolean onPrepareOptionsMenu(Menu menu) {
        // 当抽屉打开或者隐藏时,加载对应的action items
        boolean drawerOpen = mDrawerLayout.isDrawerOpen(mDrawerList);
        menu.findItem(R.id.action_websearch).setVisible(!drawerOpen);
        return super.onPrepareOptionsMenu(menu);
    }
}

接下来讨论ActionBarDrawerToggle构造函数的参数和处理action bar 图标交互的一些步骤.

随着App图标打开和关闭

用户可以通过侧拉手势打开导航抽屉,但是如果你在使用action bar,那么你也应该允许用户在点击action bar图标时打开和关闭抽屉.并且这个应用图标也应该使用一个特殊的图标来指示导航抽屉的存在.

为了使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 */
                ) {


            public void onDrawerClosed(View view) {
                super.onDrawerClosed(view);
                getActionBar().setTitle(mTitle);
            }


            public void onDrawerOpened(View drawerView) {
                super.onDrawerOpened(drawerView);
                getActionBar().setTitle(mDrawerTitle);
            }
        };

               mDrawerLayout.setDrawerListener(mDrawerToggle);

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

    @Override
    protected void onPostCreate(Bundle savedInstanceState) {
        super.onPostCreate(savedInstanceState);
        // 同步状态
        mDrawerToggle.syncState();
    }

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

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // 为了使action bar图标可以响应点击
        if (mDrawerToggle.onOptionsItemSelected(item)) {
          return true;
        }
        // Handle your other action bar items...

        return super.onOptionsItemSelected(item);
    }

    ...
}

摘录翻译自Android Developer

在2025年,`DrawerLayout`仍然是Android开发中实现侧滑菜单(Navigation Drawer)的核心组件之一。尽管Jetpack Compose等现代UI框架逐渐普及,但`DrawerLayout`及其配套的`NavigationView`或自定义视图仍然广泛用于传统XML布局项目中。 ### 使用DrawerLayout实现侧滑菜单的基本步骤 1. **添加依赖库** 确保在`build.gradle`文件中引入Material Design和AppCompat库: ```groovy implementation 'androidx.core:core-ktx:1.12.0' implementation 'androidx.appcompat:appcompat:1.6.1' implementation 'com.google.android.material:material:1.9.0' ``` 2. **在XML布局中声明DrawerLayout** 主布局通常包含一个主内容区域(如`FrameLayout`)和一个抽屉内容区域(如`NavigationView`): ```xml <androidx.drawerlayout.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/drawer_layout" android:layout_width="match_parent" android:layout_height="match_parent" android:fitsSystemWindows="true"> <!-- 主内容区域 --> <FrameLayout android:id="@+id/content_frame" android:layout_width="match_parent" android:layout_height="match_parent" /> <!-- 侧滑菜单 --> <com.google.android.material.navigation.NavigationView android:id="@+id/nav_view" android:layout_width="wrap_content" android:layout_height="match_parent" android:layout_gravity="start" app:headerLayout="@layout/nav_header" app:menu="@menu/drawer_menu" /> </androidx.drawerlayout.widget.DrawerLayout> ``` 3. **设置ActionBarDrawerToggle** 在Activity中初始化并绑定`ActionBarDrawerToggle`以启用抽屉开关动画和手势支持: ```java DrawerLayout drawerLayout = findViewById(R.id.drawer_layout); NavigationView navigationView = findViewById(R.id.nav_view); ActionBarDrawerToggle toggle = new ActionBarDrawerToggle( this, drawerLayout, toolbar, R.string.open_drawer, R.string.close_drawer); drawerLayout.addDrawerListener(toggle); toggle.syncState(); // 设置导航项点击监听器 navigationView.setNavigationItemSelectedListener(item -> { // 处理菜单项点击事件 drawerLayout.closeDrawers(); return true; }); ``` 4. **定义菜单资源文件** 创建`res/menu/drawer_menu.xml`来定义侧滑菜单的条目: ```xml <menu xmlns:android="http://schemas.android.com/apk/res/android"> <item android:id="@+id/nav_home" android:icon="@drawable/ic_home" android:title="首页" /> <item android:id="@+id/nav_settings" android:icon="@drawable/ic_settings" android:title="设置" /> </menu> ``` 5. **处理返回键与抽屉状态** 重写`onBackPressed()`方法以确保用户按下返回键时能正确关闭打开的抽屉: ```java @Override public void onBackPressed() { DrawerLayout drawer = findViewById(R.id.drawer_layout); if (drawer.isDrawerOpen(GravityCompat.START)) { drawer.closeDrawer(GravityCompat.START); } else { super.onBackPressed(); } } ``` 6. **适配深色模式与动态色彩** 在Android 13及以上版本中,建议使用Material You(动态色彩)和系统级主题适配深色/浅色模式,通过`Theme.Material3.DayNight`等主题提升用户体验。 7. **兼容性与性能优化** - 使用`android:fitsSystemWindows="true"`来避免状态栏遮挡。 - 对于大屏幕设备(如平板),可配置`DrawerLayout`为双面板布局。 - 启用硬件加速以提升动画流畅度。 ### 注意事项 - `DrawerLayout`应作为根布局,并且只能有一个子视图具有`android:layout_gravity="start"`属性用于表示抽屉内容[^1]。 - 如果使用Jetpack Compose,可以考虑使用`ModalNavigationDrawer`替代传统的`DrawerLayout`。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值