Android Studio自带侧滑栏的解析以及导航头部控件事件处理实现
1、使用Android Sdudio新建工程,在选择MainActivity布局时,选择Navigation Drawer Activity;
2、应用中用于实现侧滑栏功能的文件主要有一下几个:
(1) MainActivity.java
(2) activity_main_drawer.xml
(3) nav_header_main.xml
(4) content_main.xml
(5) app_bar_main.xml
(6) activity_main.xml
下面我们一一说明以上几个文件,先从最简单的开始。
3、activity_main_drawer.xml
这个文件位于res-menu文件夹下,看位置就知道这是一个菜单文件,其中包含有一些菜单项,用来实现侧滑栏的菜单,显示如下所示:
下面我们来看内部的代码(说明全部写在代码中间了~~~)
<?xml version="1.0"encoding="utf-8"?><menuxmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
tools:showIn="navigation_view">
<!--checkableBehavior:设置该组所有菜单项上显示的选择组件(CheckBox或Radio Button)。
如果将该属性值设为all,显示CheckBox组件;如果设为single,显示Radio Button组件;
如果设为none,显示正常的菜单项(不显示任何选择组件)。-->
<groupandroid:checkableBehavior="single">
<!--item代表菜单项,id用于标识,icon是菜单显示的图标,title是显示的标题-->
<item
android:id="@+id/nav_camera"
android:icon="@drawable/ic_menu_camera"
android:title="Import"/>
<item
android:id="@+id/nav_gallery"
android:icon="@drawable/ic_menu_gallery"
android:title="Gallery"/>
<item
android:id="@+id/nav_slideshow"
android:icon="@drawable/ic_menu_slideshow"
android:title="Slideshow"/>
<item
android:id="@+id/nav_manage"
android:icon="@drawable/ic_menu_manage"
android:title="Tools"/>
</group>
<!--item中也可以嵌套menu,表示子菜单,但是item中不能再嵌套item-->
<itemandroid:title="Communicate">
<!--menu表示菜单,其中包含菜单项item-->
<menu>
<item
android:id="@+id/nav_share"
android:icon="@drawable/ic_menu_share"
android:title="Share"/>
<item
android:id="@+id/nav_send"
android:icon="@drawable/ic_menu_send"
android:title="Send"/>
</menu>
</item>
</menu>
以上就是关于menu的说明;
4、nav_header_main.xml
这个文件位于res-layout下,是导航侧栏头部的布局文件,显示如下:
下面来看内部的代码,因为这一块比较简单,就在此稍微说明一下,不在代码中间详细介绍了。这个界面由一个垂直的线性布局构成,从上到下分别是ImageView和两个TextView,在实际开发的时候,可以在此进行自己的个性化设计,非常方便。代码贴在下面:
<?xml version="1.0"encoding="utf-8"?><LinearLayoutxmlns: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="@dimen/nav_header_height"
android:background="@drawable/side_nav_bar"
android:gravity="bottom"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:theme="@style/ThemeOverlay.AppCompat.Dark">
<ImageView
android:id="@+id/imageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingTop="@dimen/nav_header_vertical_spacing"
app:srcCompat="@mipmap/ic_launcher_round"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="@dimen/nav_header_vertical_spacing"
android:text="Android Studio"
android:textAppearance="@style/TextAppearance.AppCompat.Body1"/>
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="android.studio@android.com"/>
</LinearLayout>
5、content_main.xml
这个布局文件构成主界面显示的内容,默认的情况下是一个TextView,当然在此可以进行个性化设计,默认的效果如下(除了右下角的悬浮按钮,一会儿说明);
下面对文件内部代码进行说明:
<?xml version="1.0"encoding="utf-8"?><android.support.constraint.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"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
tools:context="com.dzjin.drawer.myapplication.MainActivity"
tools:showIn="@layout/app_bar_main">
<!--外层是一个弹性约束布局,跟相对布局有点儿像,这不是我们的重点,此处不展开说明-->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
</android.support.constraint.ConstraintLayout>
这个界面也比较简单,不过多解释说明。
6、app_bar_main.xml
这是一个比较复杂的界面,虽然看起来和前一个界面基本上一样,但是内部的代码完全不同,此处重点解释内部的代码:
<?xml version="1.0"encoding="utf-8"?><android.support.design.widget.CoordinatorLayout
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="com.dzjin.drawer.myapplication.MainActivity">
<!--CoordinatorLayout主要有两个功能,作为顶层控件和协调子控件之间的关系,其中FloatingActionButton就是
一个很好的应用-->
<!--标题栏,其中点击标题栏左侧的图标可以实现侧滑,不再展开叙述-->
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/AppTheme.AppBarOverlay">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="@style/AppTheme.PopupOverlay"/>
</android.support.design.widget.AppBarLayout>
<!--include是一个很重要的标签,这里用来把content_main布局文件引进来-->
<includelayout="@layout/content_main"/>
<!--这是悬浮按钮,可以通过设置layout_gravity属性设置按钮的位置,这里设置的是位于底部右侧-->
<android.support.design.widget.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="@dimen/fab_margin"
app:srcCompat="@android:drawable/ic_dialog_email"/>
</android.support.design.widget.CoordinatorLayout>
通过这个布局,我们把标题栏和内容面板以及悬浮按钮集合在一起,但是主界面中除了这些还应该有侧滑栏,那么下一个布局文件我们就将上一个布局和侧滑栏整合在一起,构成主界。
7、activity_main.xml
这是最后整合前面所有布局的布局,首先来看一下实现的效果:
是不是感觉很复杂又很有成就感啊,下面我们来一一说明这个布局:
<?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"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:openDrawer="start">
<!--DrawerLayout抽屉布局,实现侧滑最重要的内容,通过对它的属性进行设置,可以实现侧滑甚至上滑,下滑,都没有问题。-->
<!--首先把界面的主要内容包含进来,就是前面刚整合的app_bar_main.xml-->
<include
layout="@layout/app_bar_main"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<!--把是导航视图,就侧滑栏的内容加载进来,其中有两个比较重要德属性,headerLayout,这是导航视图的头布局,也就是前面说明的nav_header_main.xml
,另一个是menu菜单,就是前面讲的activity_main_drawer.xml,OK,界面搭建完毕-->
<android.support.design.widget.NavigationView
android:id="@+id/nav_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
android:fitsSystemWindows="true"
app:headerLayout="@layout/nav_header_main"
app:menu="@menu/activity_main_drawer"/>
</android.support.v4.widget.DrawerLayout>
8、下面说明MainActivity.java文件,说明还是写在代码中间:
packagecom.dzjin.drawer.myapplication;
importandroid.os.Bundle;
importandroid.support.design.widget.FloatingActionButton;
importandroid.support.design.widget.Snackbar;
importandroid.view.View;
importandroid.support.design.widget.NavigationView;
importandroid.support.v4.view.GravityCompat;
importandroid.support.v4.widget.DrawerLayout;
importandroid.support.v7.app.ActionBarDrawerToggle;
importandroid.support.v7.app.AppCompatActivity;
importandroid.support.v7.widget.Toolbar;
importandroid.view.MenuItem;
/**
* 这个Activity要实现一个接口,用于响应菜单的单击事件,做出处理
*/public classMainActivityextendsAppCompatActivity
implementsNavigationView.OnNavigationItemSelectedListener {
@Override
protected voidonCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//获取并设置标题栏
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
//获取悬浮按钮并设置监听
FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setOnClickListener(newView.OnClickListener() {
@Override
public voidonClick(View view) {
Snackbar.make(view,"Replace with your own action", Snackbar.LENGTH_LONG)
.setAction("Action",null).show();
}
});
//获取抽屉布局并设置监听
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
//ActionBarDrawerToggle 是 DrawerLayout.DrawerListener实现,和 NavigationDrawer搭配使用,推荐用这个方法,符合Android design规范。
ActionBarDrawerToggle toggle =new ActionBarDrawerToggle(this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
drawer.addDrawerListener(toggle);
toggle.syncState();
//获取导航视图并设置菜单监听,对应上面实现的接口
NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view);
navigationView.setNavigationItemSelectedListener(this);
}
@Override
public voidonBackPressed() {//当按下返回键的时候,对抽屉进行设置,这个地方要知道如何关闭和打开抽屉
/**
* drawer.closeDrawer(GravityCompat.START);从开始关闭抽屉
* drawer.openDrawer();打开抽屉,方向自选
*/
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
if(drawer.isDrawerOpen(GravityCompat.START)) {
drawer.closeDrawer(GravityCompat.START);
} else {
super.onBackPressed();
}
}
@SuppressWarnings("StatementWithEmptyBody")
@Override
public booleanonNavigationItemSelected(MenuItem item) {//当导时航栏菜单被单击时,根据ID判断并给出响应
// Handle navigation view item clicks here.
intid = item.getItemId();
if(id == R.id.nav_camera) {
// Handle the camera action
}else if(id == R.id.nav_gallery) {
} else if (id == R.id.nav_slideshow) {
} else if (id == R.id.nav_manage) {
} else if (id == R.id.nav_share) {
} else if (id == R.id.nav_send) {
}
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
drawer.closeDrawer(GravityCompat.START);
return true;
}
}
至此,侧滑已经完全实现,下面我们考虑一个问题,如果我们要点击导航的头部中的ImageView,并进行响应,比如说更换头像,沃恩应该怎么进行获得ImageView并对其设置监听呢?按照之前对悬浮按钮的设置方式,对ImageView做如下设置:
ImageView imageView=(ImageView)findViewById(R.id.imageView);
imageView.setOnClickListener(newView.OnClickListener() {
@Override
public voidonClick(View view) {
}
});
但是,非常不幸的出现了以下一幕:
查看输出:
java.lang.RuntimeException:Unabletostartactivity ComponentInfo{com.dzjin.drawer.myapplication/com.dzjin.drawer.myapplication.MainActivity}: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.view.View.setOnClickListener(android.view.View$OnClickListener)' on a null object reference
空指针异常。。。。。。可是悬浮窗明明用的好好的,这个ImageView就不行了呢?下面我们来一起探讨一下这个问题。
9、问题探讨
我们先来看一下这句话:
ImageView imageView=(ImageView)findViewById(R.id.imageView);
这句话就是获得ImageView,find之前默认有一个this,view.find.....,那么这个View就是我们刚才设置的ContentView,很明显,这句话没能返回一个ImageView。
我们在去看activity_main.xml布局文件,可以看到悬浮窗是被include进来的,可以直接获得,于是我们就需要考虑NavLayout中HeaderLayout内部控件的事件处理。基本的处理过程如下。
10、解决问题
通过navView获得LinearLayout,并通过LinearLayout获得ImageView,OK。
LinearLayout linearLayout=(LinearLayout) navigationView.inflateHeaderView(R.layout.nav_header_main);
ImageView imageView=(ImageView)linearLayout.findViewById(R.id.imageView);
imageView.setOnClickListener(newView.OnClickListener() {
@Override
public voidonClick(View view) {
}
});
但是出现了奇葩的一幕:
出现了两个头部,主要是因为我么分别的xml和java中加载了一次,我们可以选择去掉app:headerLayout="@layout/nav_header_main" 或者在java中使用View headerView = navigationView.getHeaderView(0); OK