本篇我们使用 Material Design来实现滑动菜单。

首先在我们的应用程序中添加DrawerLayout依赖:

android github 抽屉布局 android 上拉抽屉_ico


要在我们的程序中使用DrawerLayout,首先我们的使用DrawerLayout为顶层布局,该布局内一般有两个组件,第一个一般是FrameLayout显示屏幕中央内容,第二个是我们的的滑动菜单内容。比如我们今天要修改的NoteListFragment。按照上面的格式我们新的布局文件如下:

<?xml version="1.0" encoding="utf-8"?>
<androidx.drawerlayout.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:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@+id/drawer_layout_list">

    <androidx.constraintlayout.widget.ConstraintLayout 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:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".fragment.NoteListFragment">

        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/note_recycle_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

        <TextView
            android:id="@+id/tv_note_list_empty"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/note_list_empty"
            android:visibility="gone"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

    </androidx.constraintlayout.widget.ConstraintLayout>

    <TextView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        android:text="this is a menu drawer"
        android:background="#FFF"/>
</androidx.drawerlayout.widget.DrawerLayout>

那么android又是如何来判断DrawerLayout内的组件哪个是我们的滑动菜单呢?

这里我们需要说明一下那就是android会通过android:layout_gravity属性进行判断。官方文档说明有第一个组件不要设置该属性,第二个组件设置对应的属性即可,如上例。

android github 抽屉布局 android 上拉抽屉_android github 抽屉布局_02


如果两个组件都设置了同样的android:layout_gravity,如下所示:

<androidx.constraintlayout.widget.ConstraintLayout 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:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        tools:context=".fragment.NoteListFragment">

    </androidx.constraintlayout.widget.ConstraintLayout>

    <TextView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        android:background="#FFF"
        android:text="this is a menu drawer" />

android github 抽屉布局 android 上拉抽屉_android_03


也就是说我们需要设置不同的android:layout_gravity属性即可。比如我们一个left,一个right:

android github 抽屉布局 android 上拉抽屉_android_04


android github 抽屉布局 android 上拉抽屉_ico_05


通常我们只需要一个滑动菜单,所以第一个一般使用FragmentLayout用来显示我们的内容页面。

当然了,只是在布局文件中设置是没有上图中那个home的图标的,这里还需要在代码中进行设置一下。修改NoteListFragment的initFragmentView方法,追加下面的代码:

ActionBar actionBar = ((AppCompatActivity) getActivity()).getSupportActionBar();
        if (actionBar != null) {
            actionBar.setDisplayHomeAsUpEnabled(true);
            actionBar.setHomeAsUpIndicator(R.drawable.ic_menu_note_home);
        }

setDisplayHomeAsUpEnabled用来设置显示左上角图标,setHomeAsUpIndicator用来指定自定义的图标。

如果我们要响应该菜单的选择事件,那么记得这个的id一定是android.R.id.home:

public boolean onOptionsItemSelected(@NonNull MenuItem item) {
        switch (item.getItemId()) {
            case android.R.id.home:
                drawerLayout.openDrawer(GravityCompat.START);
                return true;

            case R.id.menu_note_create:
                Intent intent = new Intent(getActivity(), NoteCreateActivity.class);
                startActivityForResult(intent, 2);
                return true;
            default:
                return super.onOptionsItemSelected(item);
        }
    }

让我们看一张效果图:

android github 抽屉布局 android 上拉抽屉_xml_06


这个样子得菜单似乎比我们的漂亮多了,幸运的是在android中我们可以通过NavigationView来实现相应的效果。首先我们的导入依赖库:

android github 抽屉布局 android 上拉抽屉_ico_07


android github 抽屉布局 android 上拉抽屉_ico_08


接下来我们使用NavigationView替换上面的TextView:

<com.google.android.material.navigation.NavigationView
        android:id="@+id/navigation"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout_gravity="start" />

此时我们的菜单还什么都没有。

android github 抽屉布局 android 上拉抽屉_xml_09


接下来我们需要利用 app:headerLayout和 app:menu属性设置header和menu。

创建布局文件drawer_header.xml:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@drawable/drawer_background"
    android:padding="20dp">

    <de.hdodenhof.circleimageview.CircleImageView
        android:id="@+id/img_note_logo"
        android:layout_width="70dp"
        android:layout_height="70dp"
        android:src="@drawable/logo"
        app:civ_border_color="@color/colorPrimary"
        app:civ_border_width="1dp"
        app:layout_constraintBottom_toTopOf="@id/tv_note_app_name"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/tv_note_app_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:text="@string/app_name"
        android:textColor="@color/colorPrimary"
        android:textSize="18sp"
        app:layout_constraintBottom_toTopOf="@id/tv_note_introduce"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/img_note_logo" />


    <TextView
        android:id="@+id/tv_note_introduce"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="5dp"
        android:text="@string/app_introduce"
        android:textColor="@color/colorPrimary"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/tv_note_app_name" />
</androidx.constraintlayout.widget.ConstraintLayout>

指定app:headerLayout为我们刚创建的布局文件:

app:headerLayout="@layout/drawer_header"

android github 抽屉布局 android 上拉抽屉_xml_10


剩下就是添加我们的menu了。

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <group android:checkableBehavior="single">

        <item
            android:id="@+id/menu_home"
            android:icon="@drawable/ic_menu_note_home"
            android:title="@string/note_home" />

        <item
            android:id="@+id/menu_settings"
            android:icon="@drawable/ic_menu_note_settings"
            android:title="@string/note_settings" />

        <item
            android:id="@+id/menu_about"
            android:icon="@drawable/ic_menu_note_about"
            android:title="@string/note_about" />
        <item
            android:id="@+id/menu_feedback"
            android:icon="@drawable/ic_menu_note_feedback"
            android:title="@string/note_feedback" />
    </group>
</menu>

同样别忘记设置app:menu属性:

app:menu="@menu/menu_drawer"

android github 抽屉布局 android 上拉抽屉_android_11


同样的,如果想为这些菜单添加点击事件,那么我们需要实现下面的方法:

@Override
    protected void initFragmentListener() {
        navigationView.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() {
            @Override
            public boolean onNavigationItemSelected(@NonNull MenuItem item) {
                Log.d(TAG, "onNavigationItemSelected: " + item.getTitle());
                return false;
            }
        });
    }

android github 抽屉布局 android 上拉抽屉_android github 抽屉布局_12

当然了,如果还需要处理header处的事件,可以使用getHeaderView(0)方法获取根view,然后通过findViewById方法获取到内部的控件进行设置。

ImageView imageView = navigationView.getHeaderView(0).findViewById(R.id.img_note_logo);
        TextView textView = navigationView.getHeaderView(0).findViewById(R.id.tv_note_app_name);

        imageView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.d(TAG, "onClick: imageview");
            }
        });

        textView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.d(TAG, "onClick: textview");
            }
        });