Android中的NavigationView

时间:2020-02-23 14:29:28  来源:igfitidea点击:

在本教程中,我们将在android应用程序中讨论和实现NavigationView。
其中我们将学习如何设置样式,使其也从右向左打开。

导航视图

在本教程中,我们已经实现了导航抽屉,并且编写代码很麻烦。

NavigationView是一种更好,更容易实现的导航抽屉替代品。
NavigationDrawer要求我们通过实现自定义适配器来使用ListView/RecyclerView实现项目。

引入NavigationView后,我们所要做的就是使用我们很快将看到的菜单资源来为项目充气。
NavigationView通常放置在DrawerLayout中。

NavigationView入门

Android Studio为我们提供了一个现成的导航抽屉活动,该活动实现了一个标准的导航菜单。
您可以从以下对话框中选择它。

了解NavigationView

NavigationView类扩展了FrameLayout。
在标记下的xml中定义为:

<android.support.design.widget.NavigationView

NavigationView本质上包含两个主要组件:

  • HeaderView:此视图通常显示在导航抽屉的顶部。
    它实际上包含个人资料图片,姓名电子邮件地址和背景封面图片。
    此视图是在一个单独的布局文件中定义的,我们将稍后介绍。
    要将布局添加到NavigationView中,请使用app:headerLayout参数
  • 菜单:此菜单显示在HeaderView下方,并以列表形式包含所有导航项。
    布局文件在menus文件夹中定义。
    要将布局添加到NavigationView中,请使用app:menus参数

用于自定义NavigationView的其他重要XML属性是:

  • app:itemTextColor:这会更改文本颜色
  • app:itemIconTint:这会更改图标颜色
  • app:itemBackground:这会更改项目背景颜色

让我们看一下内置的NavigationView模板的项目结构。

" activity_main.xml"是MainActivity的布局。

<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout xmlns:android="https://schemas.android.com/apk/res/android"
  xmlns:app="https://schemas.android.com/apk/res-auto"
  xmlns:tools="https://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">

  <include
      layout="@layout/app_bar_main"
      android:layout_width="match_parent"
      android:layout_height="match_parent" 

  <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>

注意:上面的DrawerLayout是用于保存导航抽屉内容和我们应用程序内容的布局。

app_bar_main.xml布局由一个包含工具列的CoordinatorLayout,一个FloatingActionButton和一个content_main.xml布局(显示一个基本的" Hello World" TextView)组成。
布局在下面列出。

app_bar_main.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="https://schemas.android.com/apk/res/android"
  xmlns:app="https://schemas.android.com/apk/res-auto"
  xmlns:tools="https://schemas.android.com/tools"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:fitsSystemWindows="true"
  tools:context="com.theitroad.navigationviewstyling.MainActivity">

  <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 layout="@layout/content_main" 

  <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"
      android:src="@android:drawable/ic_dialog_email" 

</android.support.design.widget.CoordinatorLayout>

content_main.xml的布局如下所示:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="https://schemas.android.com/apk/res/android"
  xmlns:app="https://schemas.android.com/apk/res-auto"
  xmlns:tools="https://schemas.android.com/tools"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:paddingBottom="@dimen/activity_vertical_margin"
  android:paddingLeft="@dimen/activity_horizontal_margin"
  android:paddingRight="@dimen/activity_horizontal_margin"
  android:paddingTop="@dimen/activity_vertical_margin"
  app:layout_behavior="@string/appbar_scrolling_view_behavior"
  tools:context="com.theitroad.navigationviewstyling.MainActivity"
  tools:showIn="@layout/app_bar_main">

  <TextView
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:text="Hello World!" 
</RelativeLayout>

下面列出了默认的headerLayout和NavigationView的菜单:

nav_header_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="https://schemas.android.com/apk/res/android"
  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"
      android:src="@android:drawable/sym_def_app_icon" 

  <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>

activity_main_drawer.xml

<menu xmlns:android="https://schemas.android.com/apk/res/android">

  <group android:checkableBehavior="single">
      <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 android:title="Communicate">
      <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>

android:checkableBehavior xml属性是为整个组定义的,它采用下面列出的三个值之一。

  • single:只能检查组中的一项
  • all:可以选中所有项目(复选框)
  • none:没有项目可检查

android:checkable属性用于设置单个项目的可检查行为。
它接受布尔值。

注意:在应用程序:菜单布局中可以使用嵌套菜单项

MainActivity.java在下面给出

package com.theitroad.navigationviewstyling;

import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.view.View;
import android.support.design.widget.NavigationView;
import android.support.v4.view.GravityCompat;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.ActionBarDrawerToggle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.Menu;
import android.view.MenuItem;

public class MainActivity extends AppCompatActivity
      implements NavigationView.OnNavigationItemSelectedListener {

  @Override
  protected void onCreate(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(new View.OnClickListener() {
          @Override
          public void onClick(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 toggle = new ActionBarDrawerToggle(
              this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
      //drawer.setDrawerListener(toggle);
      drawer.addDrawerListener(toggle);
      toggle.syncState();

      NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view);
      navigationView.setNavigationItemSelectedListener(this);
  }

  @Override
  public void onBackPressed() {
      DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
      if (drawer.isDrawerOpen(GravityCompat.START)) {
          drawer.closeDrawer(GravityCompat.START);
      } else {
          super.onBackPressed();
      }
  }

  @Override
  public boolean onCreateOptionsMenu(Menu menu) {
      //Inflate the menu; this adds items to the action bar if it is present.
      getMenuInflater().inflate(R.menu.main, menu);
      return true;
  }

  @Override
  public boolean onOptionsItemSelected(MenuItem item) {
      //Handle action bar item clicks here. The action bar will
      //automatically handle clicks on the Home/Up button, so long
      //as you specify a parent activity in AndroidManifest.xml.
      int id = item.getItemId();

      //noinspection SimplifiableIfStatement
      if (id == R.id.action_settings) {
          return true;
      }

      return super.onOptionsItemSelected(item);
  }

  @SuppressWarnings("StatementWithEmptyBody")
  @Override
  public boolean onNavigationItemSelected(MenuItem item) {
      //Handle navigation view item clicks here.
      int id = 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;
  }
}

从上面的代码中得出的重要推论如下:

  • MainActivity实现NavigationView.OnNavigationItemSelectedListener并覆盖onNavigationItemSelected方法。
    我们在这里处理菜单项的单击,然后向左关闭抽屉。
    让我们为每个项目显示一条Toast消息,如下所示。

  • ActionBarDrawerToggle初始化为:
    ActionBarDrawerToggle与DrawerLayout一起使用,以实现导航抽屉的推荐功能。
    它具有以下用法:用作侦听器,用于打开和关闭抽屉。

  • 它在ToolBar/ActionBar中提供汉堡包图标。

  • 允许在汉堡图标和箭头之间存在动画。

Note: android.support.v4.app.ActionBarDrawerToggle is deprecated. Always use android.support.v7.app.ActionBarDrawerToggle as a replacement.- 要在DrawerLayout上添加侦听器,请使用以下方法。

drawer.addDrawerListener(toggle);
该侦听器用于通知抽屉事件。
注意:现已弃用drawer.setDrawerListener(toggle)。

  • toggle.syncState():将同步图标的状态并显示汉堡图标或者后退箭头,具体取决于抽屉是关闭还是打开。
    关闭抽屉时,省略这一行代码不会将后退箭头更改为汉堡包图标。

  • cabinet.closeDrawer(GravityCompat.START):用于通过将重力设置为START来关闭抽屉(默认为左侧)

这是默认的NavigationView在应用程序中的外观:

请注意,最后单击的项目在第一个组中始终保持突出显示状态。
要在抽屉关闭后立即移除高亮显示,请将android:checkableBehavior更改为" none"。

当前的NavigationView绘制在状态列上。
要将其置于状态列下方,请将NavigationView的android:fitsSystemWindows设置为" false"。

现在,通过设置以上属性,我们可以通过在NavigationView中设置android:layout_marginTop =""?attr/actionBarSize"并将其设置在ToolBar/ActionBar(尽管在《材料设计准则》中不建议使用此样式)来进一步设置NavigationView的样式。
android:fitsSystemWindows =" false"用于CoordinatorLayout和DrawerLayout。

完成上述自定义后,这就是输出的样子:

您在顶部看到白色的状态列吗?这是因为将CoordinatorLayout和DrawerLayout的android:fitSystemWindows设置为false。
@ color/colorPrimaryDark这样的styles.xml中的状态列样式不会改变。
我们需要更好的方法。

唯一的选择是摆脱CoordinatorLayout(我们也不使用它的动画),并将DrawerLayout和ToolBar放在LinearLayout中。

这是更新xml布局:

activity_main.xml

@Override
  public boolean onNavigationItemSelected(MenuItem item) {
      int id = item.getItemId();

      if (id == R.id.nav_camera) {
          //Handle the camera action
          Toast.makeText(getApplicationContext(), "Camera is clicked", Toast.LENGTH_SHORT).show();

      } else if (id == R.id.nav_gallery) {
          Toast.makeText(getApplicationContext(), "Gallery is clicked", Toast.LENGTH_SHORT).show();

      } else if (id == R.id.nav_slideshow) {
          Toast.makeText(getApplicationContext(), "Slideshow is clicked", Toast.LENGTH_SHORT).show();

      } else if (id == R.id.nav_manage) {
          Toast.makeText(getApplicationContext(), "Tools is clicked", Toast.LENGTH_SHORT).show();
          
      } else if (id == R.id.nav_share) {
          Toast.makeText(getApplicationContext(), "Share is clicked", Toast.LENGTH_SHORT).show();
          
      } else if (id == R.id.nav_send) {
          Toast.makeText(getApplicationContext(), "Send is clicked", Toast.LENGTH_SHORT).show();
          
      }

      DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
      drawer.closeDrawer(GravityCompat.START);
      return true;
  }

工具列中需要android:fitSystemWindows =" true"。
省略它,您将得到类似这样的结果!

注意:移除xml属性android:theme =" @ style/AppTheme.AppBarOverlay"会将ToolBar项目颜色更改为黑色。

app_bar_main.xml

ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
              this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);

这就是应用程序现在的外观。

等一下!状态列颜色与工具列相同。
本来应该是暗一点的阴影。
解决方案:只需从v-21/styles.xml中删除以下行

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="https://schemas.android.com/apk/res/android"
xmlns:app="https://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">

<!-- The toolbar -->
<android.support.v7.widget.Toolbar
  android:id="@+id/toolbar"
  android:layout_height="wrap_content"
  android:layout_width="match_parent"
  android:fitsSystemWindows="true"
  android:minHeight="?attr/actionBarSize"
  android:theme="@style/AppTheme.AppBarOverlay"
  android:background="?attr/colorPrimary" 

<android.support.v4.widget.DrawerLayout
  xmlns:android="https://schemas.android.com/apk/res/android"
  android:id="@+id/drawer_layout"
  android:layout_width="match_parent"
  android:fitsSystemWindows="true"
  android:layout_height="match_parent">

  <include
      layout="@layout/app_bar_main"
      android:layout_width="match_parent"
      android:layout_height="match_parent" 

  <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>

</LinearLayout>

让我们自定义NavigationView,使其从右向左打开!

项目结构

我们将自己的汉堡包图标png文件添加到drawable文件夹中,如下所示。

Android NavigationView示例代码

现在将activity_main.xml布局定义为

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="https://schemas.android.com/apk/res/android"
  xmlns:tools="https://schemas.android.com/tools"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  tools:context="com.theitroad.navigationviewstyling.MainActivity">
  
  <include layout="@layout/content_main" 

  <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:src="@android:drawable/ic_dialog_email"
      android:layout_alignParentBottom="true"
      android:layout_margin="@dimen/activity_horizontal_margin"
      android:layout_alignParentRight="true"
      android:layout_alignParentEnd="true" 

</RelativeLayout>

我们已将带有BarLayout的ToolBar放置在RelativeLayout内。
android:fitSystemWindows必须在所有三个中都设置为true。

DrawerLayout包含tools:openDrawer =" end""和android:layout_gravity =" end"",它们将抽屉的默认一侧更改为右侧。

理想情况下,圆形标题图像在NavigationView中看起来很漂亮。
我们将编译依赖项de.hdodenhof.circleimageview.CircleImageView,并在nav_header_main.xml文件中使用它,如下所示。

nav_header_main.xml

<item name="android:statusBarColor">@android:color/transparent</item>

其他xml布局与上面讨论的相同。

MainActivity.java在下面给出

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="https://schemas.android.com/apk/res/android"
  xmlns:app="https://schemas.android.com/apk/res-auto"
  xmlns:tools="https://schemas.android.com/tools"
  android:orientation="vertical"
  android:layout_width="match_parent"
  android:layout_height="match_parent">

  <!-- The toolbar -->
  <RelativeLayout
      android:layout_width="match_parent"
      android:fitsSystemWindows="true"
      android:layout_height="wrap_content">

      <android.support.v7.widget.Toolbar
          android:id="@+id/toolbar"
          android:layout_width="match_parent"
          android:layout_height="wrap_content"
          android:minHeight="?attr/actionBarSize"
          android:background="?attr/colorPrimary"
          android:fitsSystemWindows="true"
          android:theme="@style/AppTheme.AppBarOverlay" 

      <FrameLayout
          android:id="@+id/drawer_button"
          android:layout_width="50dp"
          android:layout_height="?attr/actionBarSize"
          android:fitsSystemWindows="true"
          android:layout_alignParentRight="true"
          android:clickable="true">

          <ImageView
              android:layout_width="wrap_content"
              android:layout_height="wrap_content"
              android:layout_gravity="center_horizontal|center_vertical"
              android:src="@drawable/ic_action_not_black" 
      </FrameLayout>

  </RelativeLayout>

  <android.support.v4.widget.DrawerLayout
      xmlns:android="https://schemas.android.com/apk/res/android"
      android:id="@+id/drawer_layout"
      android:layout_width="match_parent"
      android:fitsSystemWindows="true"
      android:layout_height="match_parent"
      tools:openDrawer="end">

      <include
          layout="@layout/app_bar_main"
          android:layout_width="match_parent"
          android:layout_height="match_parent" 

      <android.support.design.widget.NavigationView
          android:id="@+id/nav_view"
          android:layout_width="wrap_content"
          android:layout_height="match_parent"
          android:layout_gravity="end"
          android:fitsSystemWindows="true"
          app:itemTextColor="#1d3f4c"
          app:itemIconTint="#cd4312"
          app:headerLayout="@layout/nav_header_main"
          app:menu="@menu/activity_main_drawer" 

  </android.support.v4.widget.DrawerLayout>

</LinearLayout>

从以上代码得出的重要推论是:

  • toggle.setDrawerIndicatorEnabled(false);:此行用于隐藏显示在左侧的默认汉堡包图标。

  • 现在,所有GravityCompat常量都更改为END而不是START。