Android Studio自带导航侧滑实现解析以及导航头部控件事件处理实现

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:设置该组所有菜单项上显示的选择组件(CheckBoxRadio 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="[email protected]"
/>
</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.xmlOK,界面搭建完毕-->
    
<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);

这句话就是获得ImageViewfind之前默认有一个thisview.find.....,那么这个View就是我们刚才设置的ContentView,很明显,这句话没能返回一个ImageView

我们在去看activity_main.xml布局文件,可以看到悬浮窗是被include进来的,可以直接获得,于是我们就需要考虑NavLayoutHeaderLayout内部控件的事件处理。基本的处理过程如下。

10、解决问题

通过navView获得LinearLayout,并通过LinearLayout获得ImageViewOK

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) {
    }
});

但是出现了奇葩的一幕:

 

出现了两个头部,主要是因为我么分别的xmljava中加载了一次,我们可以选择去掉app:headerLayout="@layout/nav_header_main"  或者在java中使用View headerView = navigationView.getHeaderView(0);  OK

猜你喜欢

转载自blog.csdn.net/dzjin1234/article/details/79008269