NavigationView в Android
В этом руководстве мы обсудим и реализуем NavigationView в нашем приложении для Android. Здесь мы научимся стилизовать его так, чтобы он также открывался справа налево.
НавигацияВью
Мы уже реализовали навигационный ящик в RecyclerView, внедрив собственный адаптер. С введением NavigationView все, что нам нужно, — это раздувать элементы, используя ресурсы меню, которые мы скоро увидим. NavigationView обычно размещается внутри DrawerLayout.
Начало работы с NavigationView
Понимание NavigationView
Класс NavigationView расширяет FrameLayout. Он определен в xml под тегом как:
<android.support.design.widget.NavigationView/>
NavigationView по существу состоит из двух основных компонентов:
- HeaderView: это представление обычно отображается в верхней части панели навигации. По сути, он содержит изображение профиля, имя, адрес электронной почты и фоновое изображение обложки. Это представление определено в отдельном файле макета, который мы рассмотрим чуть позже. Чтобы добавить макет в наш NavigationView, используется параметр app:headerLayout
- Меню: оно отображается под заголовком и содержит все элементы навигации в виде списка. Файл макета для этого определяется в папке меню. Чтобы добавить макет в NavigationView, используется параметр app:menus
Другими важными атрибутами XML, которые используются для настройки NavigationView, являются:
- 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 Текстовое представление мира). Макеты перечислены ниже. 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.journaldev.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.journaldev.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="android.studio@android.com" />
</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>
Атрибут xml android:checkableBehavior определен для всей группы и принимает одно из трех значений, перечисленных ниже.
- single: можно отметить только один элемент из группы
- все: можно отметить все элементы (флажки)
- none : нет элементов, которые можно отметить
Атрибут android:checkable используется для установки проверяемого поведения для отдельных элементов. Он принимает логические значения. Примечание. Вложенные элементы меню возможны внутри макета приложения: меню MainActivity.java приведен ниже.
package com.journaldev.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;
}
}
Важные выводы, сделанные из приведенного выше фрагмента кода, приведены ниже:
-
The MainActivity implements NavigationView.OnNavigationItemSelectedListener and overrides the method onNavigationItemSelected. We handle the menu item clicks here and close the Drawer towards the left. Let’s display a Toast message for each of the items as show below.
@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; }
-
The ActionBarDrawerToggle is initialised as:
ActionBarDrawerToggle toggle = new ActionBarDrawerToggle( this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
The ActionBarDrawerToggle is used with a DrawerLayout to implement the recommended functionality of Navigation Drawers. It has the following usages:
- It acts as a listener, for opening and closing of drawers.
- It provides the hamburger icons in the ToolBar/ActionBar.
- It’s allows for the animation between the hamburger icon and the arrow to exist.
Note: android.support.v4.app.ActionBarDrawerToggle is deprecated. Always use android.support.v7.app.ActionBarDrawerToggle as a replacement.
-
To add a listener on the DrawerLayout the following method is used.
drawer.addDrawerListener(toggle);
This listener is used to keep notified of drawer events. Note: drawer.setDrawerListener(toggle) is now deprecated. -
toggle.syncState() : will synchronise the icon’s state and display the hamburger icon or back arrow depending on whether the drawer is closed or open. Omitting this line of code won’t change the back arrow to the hamburger icon when the drawer is closed.
-
drawer.closeDrawer(GravityCompat.START) : is used to close the drawer by setting the gravity to START(left by default)
<?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>
<?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.journaldev.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>
<item name="android:statusBarColor">@android:color/transparent</item>
Давайте настроим NavigationView так, чтобы он открывался справа налево!
Структура проекта
Пример кода Android NavigationView
Макет activity_main.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"
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>
Мы разместили панель инструментов с FrameLayout внутри 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
<?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">
<de.hdodenhof.circleimageview.CircleImageView
android:id="@+id/imageView"
android:layout_width="75dp"
android:layout_height="75dp"
android:paddingTop="@dimen/nav_header_vertical_spacing"
android:src="@drawable/profile" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="@dimen/nav_header_vertical_spacing"
android:text="Anupam Chugh"
android:textAppearance="@style/TextAppearance.AppCompat.Body1" />
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="www.JournalDev.com" />
</LinearLayout>
Другие макеты xml идентичны тем, что обсуждались выше. MainActivity.java приведен ниже
package com.journaldev.navigationviewtyling;
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.MenuItem;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity
implements NavigationView.OnNavigationItemSelectedListener {
DrawerLayout drawer;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
findViewById(R.id.drawer_button).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// open right drawer
if (drawer.isDrawerOpen(GravityCompat.END)) {
drawer.closeDrawer(GravityCompat.END);
}
else
drawer.openDrawer(GravityCompat.END);
}
});
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();
}
});
ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
toggle.setDrawerIndicatorEnabled(false);
drawer.addDrawerListener(toggle);
toggle.syncState();
NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view);
navigationView.setNavigationItemSelectedListener(this);
}
@Override
public void onBackPressed() {
if (drawer.isDrawerOpen(GravityCompat.END)) {
drawer.closeDrawer(GravityCompat.END);
} else {
super.onBackPressed();
}
}
@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
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.END);
return true;
}
}
Важные выводы, сделанные из приведенного выше кода:
toggle.setDrawerIndicatorEnabled(false);
: эта строка используется, чтобы скрыть значок гамбургера по умолчанию, который отображался слева.- Все константы GravityCompat теперь изменены на END вместо START.
Скачать пример проекта Android NavigationView