Contenidos
- Actividad MainActivity.java
- Layout activity_main.xml
- Layout navigation_drawer_header.xml
- Menú navigation_drawer_menu.xml
- Archivos de estilos styles.xml para todas las versiones
- Colores mediante el archivo colors.xml
- Archivos de atributos attrs.xml para todas las versiones
- Drawables utilizados
- Segunda Actividad
Actividad MainActivity.java
Primero vamos a ver el archivo MainActivity.java al completo para después ir desguazando cada parte del mismo.
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 |
import android.content.Intent; import android.os.Bundle; import android.support.design.widget.NavigationView; import android.support.v4.view.GravityCompat; import android.support.v4.widget.DrawerLayout; import android.support.v7.app.ActionBar; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.Toolbar; import android.view.Menu; import android.view.MenuItem; import android.widget.TextView; import android.widget.Toast; public class MainActivity extends AppCompatActivity { DrawerLayout drawerLayout; Toolbar toolbar; ActionBar actionBar; TextView textView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); toolbar = (Toolbar) findViewById(R.id.toolbar); setSupportActionBar(toolbar); actionBar = getSupportActionBar(); actionBar.setHomeAsUpIndicator(R.drawable.ic_menu_white_24dp); actionBar.setDisplayHomeAsUpEnabled(true); drawerLayout = (DrawerLayout) findViewById(R.id.navigation_drawer_layout); NavigationView navigationView = (NavigationView) findViewById(R.id.navigation_view); if (navigationView != null) { setupNavigationDrawerContent(navigationView); } setupNavigationDrawerContent(navigationView); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.menu_main, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case android.R.id.home: drawerLayout.openDrawer(GravityCompat.START); return true; } return super.onOptionsItemSelected(item); } private void setupNavigationDrawerContent(NavigationView navigationView) { navigationView.setNavigationItemSelectedListener( new NavigationView.OnNavigationItemSelectedListener() { @Override public boolean onNavigationItemSelected(MenuItem menuItem) { textView = (TextView) findViewById(R.id.textView); switch (menuItem.getItemId()) { case R.id.item_navigation_drawer_inbox: menuItem.setChecked(true); textView.setText(menuItem.getTitle()); drawerLayout.closeDrawer(GravityCompat.START); return true; case R.id.item_navigation_drawer_starred: menuItem.setChecked(true); textView.setText(menuItem.getTitle()); drawerLayout.closeDrawer(GravityCompat.START); return true; case R.id.item_navigation_drawer_sent_mail: menuItem.setChecked(true); textView.setText(menuItem.getTitle()); drawerLayout.closeDrawer(GravityCompat.START); return true; case R.id.item_navigation_drawer_drafts: menuItem.setChecked(true); textView.setText(menuItem.getTitle()); drawerLayout.closeDrawer(GravityCompat.START); return true; case R.id.item_navigation_drawer_settings: menuItem.setChecked(true); textView.setText(menuItem.getTitle()); Toast.makeText(MainActivity.this, "Launching " + menuItem.getTitle().toString(), Toast.LENGTH_SHORT).show(); drawerLayout.closeDrawer(GravityCompat.START); Intent intent = new Intent(MainActivity.this, SettingsActivity.class); startActivity(intent); return true; case R.id.item_navigation_drawer_help_and_feedback: menuItem.setChecked(true); Toast.makeText(MainActivity.this, menuItem.getTitle().toString(), Toast.LENGTH_SHORT).show(); drawerLayout.closeDrawer(GravityCompat.START); return true; } return true; } }); } } |
Lo primero que vemos arriba son todos los elementos que se han importado, hay que tener cuidado ya que algunas veces cuando añades una, por ejemplo, Toolbar, en vez de importarte import android.support.v7.widget.Toolbar; te importa otro elemento diferente. Esto ocurre con varias de ellos.
Lo siguiente que hacemos es declarar los elementos necesarios dentro de nuestra case principal, pero fuera del método onCreate:
0 1 2 3 4 5 |
DrawerLayout drawerLayout; Toolbar toolbar; ActionBar actionBar; TextView textView; |
En el método onCreate damos soporte a la Toolbar que más adelante añadiremos al layout activity_main .xml, aparte de mostrar el icono característico de las tres rayas paralelas en la ActionBar o Toolbar (como queráis llamar a la barra superior de la app).
0 1 2 3 4 5 6 7 |
toolbar = (Toolbar) findViewById(R.id.toolbar); setSupportActionBar(toolbar); actionBar = getSupportActionBar(); actionBar.setHomeAsUpIndicator(R.drawable.ic_menu_white_24dp); actionBar.setDisplayHomeAsUpEnabled(true); |
El código restante dentro del método onCreate son los engranajes para hacer funcionar al navigation drawer mediante el nuevo View llamado NavigationView. Básicamente hacemos referencia al mismo, y para que quede más limpio el código, creamos un método llamado setupNavigationDrawerContent, en el cual le pasamos la instancia que hemos creado navigationView. El método lo colocaremos fuera del método onCreate siendo el siguiente:
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
private void setupNavigationDrawerContent(NavigationView navigationView) { navigationView.setNavigationItemSelectedListener( new NavigationView.OnNavigationItemSelectedListener() { @Override public boolean onNavigationItemSelected(MenuItem menuItem) { textView = (TextView) findViewById(R.id.textView); switch (menuItem.getItemId()) { case R.id.item_navigation_drawer_inbox: menuItem.setChecked(true); textView.setText(menuItem.getTitle()); drawerLayout.closeDrawer(GravityCompat.START); return true; case R.id.item_navigation_drawer_starred: menuItem.setChecked(true); textView.setText(menuItem.getTitle()); drawerLayout.closeDrawer(GravityCompat.START); return true; case R.id.item_navigation_drawer_sent_mail: menuItem.setChecked(true); textView.setText(menuItem.getTitle()); drawerLayout.closeDrawer(GravityCompat.START); return true; case R.id.item_navigation_drawer_drafts: menuItem.setChecked(true); textView.setText(menuItem.getTitle()); drawerLayout.closeDrawer(GravityCompat.START); return true; case R.id.item_navigation_drawer_settings: menuItem.setChecked(true); textView.setText(menuItem.getTitle()); Toast.makeText(MainActivity.this, "Launching " + menuItem.getTitle().toString(), Toast.LENGTH_SHORT).show(); drawerLayout.closeDrawer(GravityCompat.START); Intent intent = new Intent(MainActivity.this, SettingsActivity.class); startActivity(intent); return true; case R.id.item_navigation_drawer_help_and_feedback: menuItem.setChecked(true); Toast.makeText(MainActivity.this, menuItem.getTitle().toString(), Toast.LENGTH_SHORT).show(); drawerLayout.closeDrawer(GravityCompat.START); return true; } return true; } }); } |
Cómo se puede observar en Figura 1, básicamente estamos haciendo referencia a todos los elementos que habrá en el Nacivigation Drawer. Así, con el switch, cuando el usuario seleccione uno de los elementos, ocurrirá lo siguiente para cada uno de ellos:
- Se marcará como seleccionado el ítem en cuestión gracias a menuItem.setChecked(true).
- El TextView que se encuentra en mitad de la pantalla cambiará de nombre, obteniendo el mismo del título del ítem presionado, textView.setText(menuItem.getTitle());.
- Se cerrará el navigation drawer mediante drawerLayout.closeDrawer(GravityCompat.START);.
- En el caso del elemento Settings, también se va a iniciar otra actividad, como se puede ver en las líneas destacadas:
0 1 2 3 4 5 6 7 8 9 |
case R.id.item_navigation_drawer_settings: menuItem.setChecked(true); textView.setText(menuItem.getTitle()); Toast.makeText(MainActivity.this, "Launching " + menuItem.getTitle().toString(), Toast.LENGTH_SHORT).show(); drawerLayout.closeDrawer(GravityCompat.START); Intent intent = new Intent(MainActivity.this, SettingsActivity.class); startActivity(intent); return true; |
Y para finalizar, el código siguiente es para abrir el navigation drawer si se presiona sobre el botón de las tres rayas en la Toolbar, mediante el siguiente método:
0 1 2 3 4 5 6 7 8 9 10 |
@Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case android.R.id.home: drawerLayout.openDrawer(GravityCompat.START); return true; } return super.onOptionsItemSelected(item); } |
Layout activity_main.xml
Cómo hemos hecho antes, vamos a ver el código al completo y lo vamos desguazando:
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
<?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" android:id="@+id/navigation_drawer_layout" android:layout_width="match_parent" android:layout_height="match_parent" android:fitsSystemWindows="@bool/fitsSystemWindows"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <FrameLayout android:layout_width="match_parent" android:layout_height="@dimen/status_bar_height" android:background="?colorPrimary"/> </LinearLayout> <FrameLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="@dimen/status_bar_height"> <android.support.v7.widget.Toolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?actionBarSize" android:background="?attr/colorPrimary" android:elevation="4dp" app:popupTheme="@style/ThemeOverlay.AppCompat.Light" app:theme="@style/ToolbarTheme" /> <TextView android:id="@+id/textView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:text="Inbox" android:textAppearance="@style/TextAppearance.AppCompat.Display1" android:textColor="@color/md_text" /> </FrameLayout> <android.support.design.widget.NavigationView android:id="@+id/navigation_view" android:layout_width="wrap_content" android:layout_height="match_parent" android:layout_gravity="start" android:fitsSystemWindows="@bool/fitsSystemWindows" app:headerLayout="@layout/navigation_drawer_header" app:menu="@menu/navigation_drawer_menu" app:theme="@style/NavigationViewTheme" /> </android.support.v4.widget.DrawerLayout> |
El contenedor principal es, evidentemente, un DrawerLayout, con un atributo que hay que mencionar por ser muy importante, que es el android:fitsSystemWindows="true", para evitar que los elementos de la pantalla superpongan las barras del sistema. El DrawerLayout se encarga de poder tener un Navigation Drawer además de contener el resto de elementos de la actividad principal.
Dentro del DrawerLayout se puede ver que hay un FrameLayout que contiene una Toolbar, un TextView, y el elemento estrella, un NavigationView. En este es en el que nos tenemos que centrar en esta entrada, para los otros dos visita las páginas correspondientes o echa un vistazo al vídeo en el que explicaré más cosas que aquí.
0 1 2 3 4 5 6 7 8 9 10 |
<android.support.design.widget.NavigationView android:id="@+id/navigation_view" android:layout_width="wrap_content" android:layout_height="match_parent" android:layout_gravity="start" android:fitsSystemWindows="@bool/fitsSystemWindows" app:headerLayout="@layout/navigation_drawer_header" app:menu="@menu/navigation_drawer_menu" app:theme="@style/NavigationViewTheme" /> |
Vamos a ver cada uno de los nuevos atributos del NavigationView:
- El primer atributo que quiero destacar es el android:fitsSystemWindows="@bool/fitsSystemWindows", ya que como veis no le estamos dando un valor true o false, le estamos indicando que le daremos el valor @bool/fitsSystemWindows, este valor lo determinaremos nosotros en los archivos attrs.xml que haremos para cada versión, ya que casi todas las versiones necesita que sea True, excepto para KitKat es necesario que sea False. También verás en la Toolbar o en el FrameLayout que también utilizamos esto, además de una altura que variará en el FrameLayout y en el margen superior de otro FrameLayout. Como he dicho antes, visita el vídeo si no te queda claro el por qué lo he hecho.
- El atributo relevante es app:headerLayout="@layout/navigation_drawer_header" se encarga de permitirte que puedas añadir un header (la parte superior clásica con la imagen, el avatar, etc) al navigation drawer, este header lo tienes que crear en otro layout que verás más adelante y que se llamará navigation_drawer_header.xml.
- El atributo app:menu="@menu/navigation_drawer_menu" es para indicarle los elementos que tendrá el navigation drawer. Es aquí donde se encuentra la magia ya que te evitas tener que hacer un RecyclerView o un ListView además de tener que personalizar cada item para que cumpla las especificaciones de material design.
- El último atributo app:theme="@style/NavigationViewTheme" lo he añadido yo personalmente ya que, por ahora, los textos están utilizando por defecto la fuente Roboto Regular, y si has visitado las especificaciones de material design para el navigation drawer, verás que debería ser Roboto Medium. Con este método lo arreglamos después en nuestro archivo styles.xml. Es probable que esto más adelante no haga falta ya que lo arregle Google por defecto.
Archivos de estilos styles.xml para todas las versiones
Aquí básicamente tenemos los elementos necesarios para dar los colores y el formato necesario para todas las versiones. El arreglo que mencioné anteriormente de que es necesario que el Navigation Drawer muestre los elementos con la letra Roboto Medium en vez de Roboto Regular es el siguiente:
0 1 2 3 4 |
<style name="NavigationViewTheme" parent="AppTheme"> <item name="textAppearanceListItem">@style/TextAppearance.AppCompat.Body2</item> </style> |
Como se puede ver, en la v19 le indicamos que la status bar sea translucida y en la v21 que sea transparente además del otro atributo para que en el navigation drawer se muestre el header debajo de la status bar. Como veis, aunque le decimos que la barra tiene que ser transparente en la v21, la librería ya se encarga de añadir el translucido cuando el navigation drawer se abre.
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
<?xml version="1.0" encoding="utf-8"?> <resources> <style name="AppTheme" parent="Base.AppTheme"> </style> <style name="Base.AppTheme" parent="Theme.AppCompat.Light.NoActionBar"> <item name="colorPrimary">@color/md_indigo_500</item> <item name="colorPrimaryDark">@color/md_indigo_700</item> <item name="colorAccent">@color/md_pink_500</item> <item name="colorControlHighlight">@color/md_indigo_500_25</item> <item name="android:windowBackground">@color/md_white_1000</item> </style> <style name="ToolbarTheme" parent="AppTheme"> <item name="android:textColorPrimary">@android:color/white</item> <item name="android:textColorSecondary">@android:color/white</item> </style> <style name="NavigationViewTheme" parent="AppTheme"> <item name="textAppearanceListItem">@style/TextAppearance.AppCompat.Body2</item> </style> </resources> |
0 1 2 3 4 5 6 7 8 9 |
<?xml version="1.0" encoding="utf-8"?> <resources> <style name="AppTheme" parent="Base.AppTheme"> <item name="android:windowTranslucentStatus">true</item> </style> </resources> |
0 1 2 3 4 5 6 7 8 9 10 |
<?xml version="1.0" encoding="utf-8"?> <resources> <style name="AppTheme" parent="Base.AppTheme"> <item name="android:windowDrawsSystemBarBackgrounds">true</item> <item name="android:statusBarColor">@android:color/transparent</item> </style> </resources> |
Colores mediante el archivo colors.xml
Los colores utilizados los puedes añadir tanto de mi GitHub como visitando esta página sobre Color en Material Design. Simplemente crea un archivo colors.xml en app/res/values/colors.xml.
Los strings son los que se crean por defecto al ir creando cada uno de los elementos.
0 1 2 3 4 5 6 7 |
<resources> <string name="app_name">Navigation Drawer</string> <string name="hello_world">Hello world!</string> <string name="action_settings">Settings</string> <string name="title_activity_settings">Settings</string> </resources> |
Archivos de atributos attrs.xml para todas las versiones
Estos archivos son para arreglar lo que hemos ido mencionando a lo largo de la entrada:
- Para que Navigation Drawer se vea bien en Kitkat
- Para que el avatar del header se vea bien en versiones de Android con barra translucida y negra.
0 1 2 3 4 5 6 7 |
<?xml version="1.0" encoding="utf-8"?> <resources> <bool name="fitsSystemWindows">true</bool> <dimen name="navigation_drawer_header_margin">16dp</dimen> <dimen name="status_bar_height">0dp</dimen> </resources> |
0 1 2 3 4 5 6 7 |
<?xml version="1.0" encoding="utf-8"?> <resources> <bool name="fitsSystemWindows">false</bool> <dimen name="navigation_drawer_header_margin">41dp</dimen> <dimen name="status_bar_height">25dp</dimen> </resources> |
0 1 2 3 4 5 6 7 |
<?xml version="1.0" encoding="utf-8"?> <resources> <bool name="fitsSystemWindows">true</bool> <dimen name="navigation_drawer_header_margin">41dp</dimen> <dimen name="status_bar_height">0dp</dimen> </resources> |
Drawables utilizados
Los drawables utilizados los puedes encontrar en la siguiente carpeta de GitHub.
Además, recomiendo visitar las siguientes páginas para obtener más recursos:
Segunda Actividad
Cómo se ha dicho, el elemento del navigation drawer Settings, inicia una segunda actividad, por lo que es necesario crearla, aunque no tiene nada de especial, simplemente os dejo aquí los cambios que he añadido al SettingsActivity.java y al activity_settings.xml, ya que el archivo menu_settings no ha sido modificado.
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
import android.os.Build; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.support.v7.widget.Toolbar; import android.util.TypedValue; import android.view.Menu; import android.view.MenuItem; public class SettingsActivity extends AppCompatActivity { Toolbar toolbar; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_settings); toolbar = (Toolbar) findViewById(R.id.toolbar); setSupportActionBar(toolbar); getSupportActionBar().setDisplayHomeAsUpEnabled(true); TypedValue typedValueColorPrimaryDark = new TypedValue(); SettingsActivity.this.getTheme().resolveAttribute(R.attr.colorPrimaryDark, typedValueColorPrimaryDark, true); final int colorPrimaryDark = typedValueColorPrimaryDark.data; if (Build.VERSION.SDK_INT >= 21) { getWindow().setStatusBarColor(colorPrimaryDark); } } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.menu_inbox, 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); } } |
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
<FrameLayout 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="match_parent"> <android.support.v7.widget.Toolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?actionBarSize" android:background="?attr/colorPrimary" android:elevation="4dp" app:popupTheme="@style/ThemeOverlay.AppCompat.Light" app:theme="@style/ToolbarTheme" /> <TextView android:textAppearance="@style/TextAppearance.AppCompat.Display1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:text="Settings" android:textColor="@color/md_text" /> </FrameLayout> |
Y ya está todo listo, tu navigation drawer debería funcionar correctamente si lo ejecutas.
Puedes ver más información sobre Navigation Drawer en la página oficial de Google en inglés pulsando aquí.