本篇我们使用 Material Design来实现滑动菜单。
首先在我们的应用程序中添加DrawerLayout依赖:
要在我们的程序中使用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: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:layout_gravity属性即可。比如我们一个left,一个right:
通常我们只需要一个滑动菜单,所以第一个一般使用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中我们可以通过NavigationView来实现相应的效果。首先我们的导入依赖库:
接下来我们使用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" />
此时我们的菜单还什么都没有。
接下来我们需要利用 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"
剩下就是添加我们的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"
同样的,如果想为这些菜单添加点击事件,那么我们需要实现下面的方法:
@Override
protected void initFragmentListener() {
navigationView.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() {
@Override
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
Log.d(TAG, "onNavigationItemSelected: " + item.getTitle());
return false;
}
});
}
当然了,如果还需要处理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");
}
});