Blog >Lista Soluciones > -Drawer Menu + Action Bar + Tabs
Drawer menu manejando ActionBar con Tabs
En el post iremos viendo en Android Studio un menú de navegación vistoso paso a paso, en primer lugar crearemos el menú drawer.
Si quereis ver qué es un menú drawer podeis verlo desde
aquí.
En este caso cambiaremos los parámetros para que modificar la interfaz visual desde el drawer menu al ActionBar, y en esta ocasión también trataremos el ActionBar con pestañas.
Vamos allá con el código :
1 Creacción del Drawer Menu
CLASE itemsMenuDrawer
/**
* Created by makone
* Description : Encapsula objetos para el menú del Dawer
*/
public class itemsMenuDrawer {
private int id;
private String name;
private String subName;
private Drawable icon;
public itemsMenuDrawer(int id, String name, String subName, Drawable icon) {
this.id = id;
this.name = name;
this.subName = subName;
this.icon = icon;
}
public int getId() { return id; }
public void setId(int id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getSubName() { return subName; }
public void setSubName(String subName) { this.subName = subName; }
public Drawable getIcon() { return icon; }
public void setIcon(Drawable icon) { this.icon = icon; }
}
CLASE menuAdapter
/**
* Created by makone.
* Description : Adapter para los objetos del Drawer
*/
public class menuAdapter extends BaseAdapter {
protected Activity activity;
//ARRAYLIST CON TODOS LOS ITEMS
protected ArrayList items;
//CONSTRUCTOR
public menuAdapter(Activity activity, ArrayList items) {
this.activity = activity;
this.items = items;
}
//CUENTA LOS ELEMENTOS
@Override
public int getCount() {
return items.size();
}
//DEVUELVE UN OBJETO DE UNA DETERMINADA POSICION
@Override
public Object getItem(int arg0) {
return items.get(arg0);
}
//DEVUELVE EL ID DE UN ELEMENTO
@Override
public long getItemId(int position) {
return items.get(position).getId();
}
//METODO PRINCIPAL, AQUI SE LLENAN LOS DATOS
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = convertView;
//ASOCIAMOS LA VISTA AL LAYOUT DEL RECURSO XML DONDE ESTA LA BASE DE
if (convertView == null) {
LayoutInflater inf = (LayoutInflater) activity.
getSystemService(activity.getApplicationContext().LAYOUT_INFLATER_SERVICE);
v = inf.inflate(R.layout.itemdrawer, null);
}
itemsMenuDrawer dir = items.get(position);
//RELLENAMOS LA IMAGEN Y EL TEXTO
ImageView foto = (ImageView) v.findViewById(R.id.img_menu);
foto.setImageDrawable(dir.getIcon());
TextView txtPrinc = (TextView) v.findViewById(R.id.txt_princip);
txtPrinc.setText(dir.getName());
TextView txtSecund = (TextView) v.findViewById(R.id.txt_secund);
txtSecund.setText(dir.getSubName());
// DEVOLVEMOS VISTA
return v;
}
}
CLASE MainActivity
public class MainActivity extends ActionBarActivity {
DrawerLayout drawerLayout;
FrameLayout contenedorPrincipal;
ListView listaMenu;
private ArrayList array;
Activity activity;
Context context;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
this.activity = MainActivity.this;
this.context = activity.getApplicationContext();
gestionDatos();
}
public void gestionDatos(){
instanciarObjetos ();
configurarMenuDrawer();
}
public void configurarMenuDrawer(){
}
public void instanciarObjetos (){
contenedorPrincipal=(FrameLayout) findViewById(R.id.contenedor_principal);
drawerLayout = (DrawerLayout) findViewById(R.id.menu_drawer);
listaMenu = (ListView) findViewById(R.id.lista_menu);
listaMenu.setOnItemClickListener(new AdapterView.OnItemClickListener(){
@Override
public void onItemClick(AdapterView parent, View view,int position, long id) {
seleccionarFragment(position);
}
});
View header=getLayoutInflater().inflate(R.layout.header_menu, null); //HEADER
listaMenu.addHeaderView(header,null,false);
array = new ArrayList();
llenarListaMenu(array);
menuAdapter adaptador = new menuAdapter(this, array); //ADAPTER
listaMenu.setAdapter(adaptador);
}
public void seleccionarFragment(int posicion){
switch (posicion) {
// BOTON PLAY, PRIMERA POSICION = 0
case 1:
//fragment1
break;
case 2:
//fragment2
break;
case 3:
//fragment3
break;
case 4:
//fragment4
break;
case 5:
finish();
}
}
//LLENAR LISTA DE OBJETOS DEL MENU
public void llenarListaMenu(ArrayList arraydir){
itemsMenuDrawer item;
// Introduzco los elementos
item = new itemsMenuDrawer(1, "Nirvana", "Heart, sharped box",
context.getResources().getDrawable(R.drawable.ico1));
arraydir.add(item);
item = new itemsMenuDrawer(2, "The pixies", "Where is my mind",
context.getResources().getDrawable(R.drawable.ico2));
arraydir.add(item);
item = new itemsMenuDrawer(3,"Bob Dylan","Hurrican Carter",
context.getResources().getDrawable(R.drawable.ico3));
arraydir.add(item);
item = new itemsMenuDrawer(4, "Toxicity","System of a down",
context.getResources().getDrawable(R.drawable.ico4));
arraydir.add(item);
item = new itemsMenuDrawer(5, "Maneras de vivir", "Rosendo",
context.getResources().getDrawable(R.drawable.ico5));
arraydir.add(item);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the actionBar if it is present.
getMenuInflater().inflate(R.menu.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.
switch (item.getItemId()) {
return super.onOptionsItemSelected(item);
}
}
}//END CLASS
Este es el resultado :
Si quereis ver cómo se hacen los diseños de R.layout.itemdrawer o el propio activity_main podeis verlo en el post anteriormente mencionado
aquí.
2 Implementamos una Action Bar
Antes vamos a insertar la librería de compatibilidad con versiones anteriores, así que le damos click derecho al proyecto y en
open module settings (penúltima por abajo), vamos a dependencies y en el +
Library Dependency buscamos
com.android.support:appcompat-v7.
Podemos usarla para toda la aplicación o para alguna actividad que nos interese.
Ahora vamos a darle un menú a la Action Bar independiente del Drawer, esto a la hora de desarrollar la aplicación es muy importante estudiar este combo, ya que debemos eliminar la redundancia de información.
En este caso en particular, vamos a cambiar los parámetros para que cambie la interfaz visual desde el drawer menu al Action Bar.
Para manejar los iconos que deben ir en la Action Bar creamos una clase en el que le pasamos la opción y genera una Action Bar diferente dependiendo de donde se llame :
CLASE configActionBAR
import texto.cifrador.makone.cifradortexto.R;
/**
* Created by makone.
* Description : Gestiona los menús que deben salir en cada actividad
*/
public class configActionBAR {
ActionBar actionBar = null;
int opcion;
public configActionBAR(ActionBar menu, int opcion) {
this.actionBar = menu;
this.opcion = opcion;
ClasificarMenu(opcion);
}
public void ClasificarMenu(int opcion) {
switch (opcion) {
case 1:cifrador();
case 2:
case 3:
default:
Log.e("BotoonesMenu", "Opcion incorrecta");
}
}
public void cifrador (){
//actionBar.hide(); //Ocultar ActionBar
actionBar.setIcon(R.drawable.ic_launcher); //Establecer icono
actionBar.setTitle("Cifrador"); //Establecer titulo
actionBar.setSubtitle("Menu principal"); //Establecer Subtitulo
// actionBar.setDisplayHomeAsUpEnabled(true); //Boton atras
}
}
Y para darle la funcionalidad al botón de atrás de la Action Bar, vamos al método onOptionsItemSelected de MainActivity, donde android.R.id.home es la instancia del botón atrás del ActionBar.
CLASE MainActivity
@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.
switch (item.getItemId()) {
case android.R.id.home :
drawerLayout.openDrawer(Gravity.LEFT);
return true;
Usamos la nueva clase para poner los items en lista desplegable o en el título que nos parezca
CLASE MainActivity
public void configurarMenuDrawer(){
actionBar = getSupportActionBar();
}
//EN EL ONCLICK DE listaMenu PARA PODER DARLE OTRA APARIENCIA DEPENDIENDO DEL FRAGMENT
ACM = new configActionBAR(actionBar,position);
Ahora vamos a modificar el menú principal activity_main.xml, pero antes veamos estos atributos :
- ifRoom : Muestra el icono si lo tiene y tiene espacio.
- withText : Icono + título si tiene espacio.
- always : Siempre muestra el icono.
- never : Unicamente estará en el desplegable.
Con always hay que tener cuidado, ya que en pasadas versiones los tamaños eran más pequeños y puede dar problemas, con ifRoom está solucionado, ahora sólo nos hace falta jugar un poco con las propiedades :
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:texto.makone="http://schemas.android.com/apk/res-auto"
tools:context=".MainActivity">
<item
android:id="@+id/compartirApp"
android:title="@string/menu_google_App"
android:icon="@drawable/ic_action_share"
texto.makone:showAsAction="ifRoom|withText"/>
<item
android:id="@+id/menuAjustes"
android:title="@string/menu_ajustes"
android:icon="@drawable/ic_action_settings"
texto.makone.:showAsAction="ifRoom|withText"/>
<item
android:id="@+id/GoogleAppPago"
android:title="@string/menu_google_App"
android:icon="@drawable/google_play_icon"
texto.makone:showAsAction="ifRoom|withText"/>
<item
android:id="@+id/menuDescripcion"
android:title="@string/menu_descripcion"
android:icon="@drawable/google_play_icon"
texto.makone:showAsAction="never"/>
<item
android:id="@+id/menuEjemplo"
android:title="@string/menu_ejemplo"
android:icon="@drawable/google_play_icon"
texto.makone:showAsAction="never"/>
</menu>
texto.makone es el nombre del paquete de mi app y esta linea también es importante :
xmlns:texto.makone="http://schemas.android.com/apk/res-auto"
Vemos que Descripcion o Ejemplo no aparecen en el Action mientras que los demás están configurados para que sí. Ahora vamos con el tema de la gestión de fragments. Con Android Studio crea el fragment completamente, he creado 2, pero se pueden hacer todos los que se quiera o necesite :
Omito el diseño de los fragments, ya que podeis ver cómo en post como los mencionados anteriormente. Y esta es la clase que por defecto nos crea Android Studio
public class fragmentIntro extends Fragment {
// TODO: Rename parameter arguments, choose names that match
// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
private static final String ARG_PARAM1 = "param1";
private static final String ARG_PARAM2 = "param2";
// TODO: Rename and change types of parameters
private String mParam1;
private String mParam2;
private OnFragmentInteractionListener mListener;
/**
* Use this factory method to create a new instance of
* this fragment using the provided parameters.
*
* @param param1 Parameter 1.
* @param param2 Parameter 2.
* @return A new instance of fragment fragmentIntro.
*/
// TODO: Rename and change types and number of parameters
public static fragmentIntro newInstance(String param1, String param2) {
fragmentIntro fragment = new fragmentIntro();
Bundle args = new Bundle();
args.putString(ARG_PARAM1, param1);
args.putString(ARG_PARAM2, param2);
fragment.setArguments(args);
return fragment;
}
public fragmentIntro() {
// Required empty public constructor
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
mParam1 = getArguments().getString(ARG_PARAM1);
mParam2 = getArguments().getString(ARG_PARAM2);
}
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_cifrador, container, false);
return v;
}
// TODO: Rename method, update argument and hook method into UI event
public void onButtonPressed(Uri uri) {
if (mListener != null) {
mListener.onFragmentInteraction(uri);
}
}
//Comentamos porque sino de momento daría error
/*@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
mListener = (OnFragmentInteractionListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement OnFragmentInteractionListener");
}
}*/
@Override
public void onDetach() {
super.onDetach();
mListener = null;
}
/**
* This interface must be implemented by activities that contain this
* fragment to allow an interaction in this fragment to be communicated
* to the activity and potentially other fragments contained in that
* activity.
* * See the Android Training lesson Communicating with Other Fragments for more information.
*/
public interface OnFragmentInteractionListener {
// TODO: Update argument type and name
public void onFragmentInteraction(Uri uri);
}
}
Aprovechamos y modificamos la función seleccionarFragment para que nos vayamos moviendo por los fragment y vaya cambiando nuestro Action Bar a antojo, según este ejemplo, la opción 1 del drawer nos llevara al fragmentIntro, mientras que las demás nos llevarán al fragmentCifrador.
Para poder cambiar nuestro Action Bar desde cualquier fragment, que es un problema que me ha llevado un rato, basta con :
this.activity.invalidateOptionsMenu(); //PARA REFRESCAR ACTION BAR JUSTO ANTES DE CREAR EL MENU
this.actionBar = actionBar = ((android.support.v7.app.ActionBarActivity) activity).getSupportActionBar();
//EN CADA FRAGMENT GENERAMOS onCreateOptionsMenu Y AÑADIMOS menu.clear()
public class fragmentIntro extends Fragment {
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
menu.clear(); //Vaciar el menu
inflater.inflate(R.menu.menu_main, menu);
}
//EN CADA FRAGMENT GENERAMOS onActivityCreated Y AÑADIMOS setHasOptionsMenu(true);
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
setHasOptionsMenu(true); //PARA PODER MODIFICAR EL MENU
}
}
No os olvideis de poner el número para que entre en el switch del MainActivity, y en la clase configActionBAR(ActionBar menu, int opcion) en el método ClasificarMenu(int opcion) agregar tantos métodos como queramos modificar los Action Bar en cada caso.
public void instanciarObjetos (){
contenedorPrincipal=(FrameLayout) findViewById(R.id.contenedor_principal);
drawerLayout = (DrawerLayout) findViewById(R.id.menu_drawer);
listaMenu = (ListView) findViewById(R.id.lista_menu);
listaMenu.setOnItemClickListener(new AdapterView.OnItemClickListener(){
@Override
public void onItemClick(AdapterView parent, View view,int position, long id) {
seleccionarFragment(position);
}
});
View header=getLayoutInflater().inflate(R.layout.header_menu, null); //HEADER
listaMenu.addHeaderView(header,null,false);
array = new ArrayList();
llenarListaMenu(array);
menuAdapter adaptador = new menuAdapter(this, array); //ADAPTER
listaMenu.setAdapter(adaptador);
//PARA QUE SEA POR DEFECTO EL FRAGMENT INTRO
seleccionarFragment(1);
Log.e("MainActivity", "PASOOOOOOOO on CREATE");
}
public void seleccionarFragment(int posicion){
Log.e("MainActivity", "PAS Selecciono fragment"+posicion);
boolean salida=false;
android.support.v4.app.Fragment fragment=null;
switch (posicion) {
// BOTON PLAY, PRIMERA POSICION = 0
case 1:
fragment = new fragmentIntro();
break;
case 2:
fragment = new fragmentCifrador();
break;
case 3:
fragment = new fragmentImagen();
break;
case 4:
fragment = new fragmentCripto();
break;
case 5:
salida=true;
//MARCAMOS EL CLICKADO
listaMenu.setItemChecked(posicion, true);
drawerLayout.closeDrawer(listaMenu);
finish();
break;
default: Log.e("Main Activity ", "Opcion "+posicion);
}
//SI SE SALE DE LA APLICACIÓN, AL HACER EL CAMBIO DE FRAGMENT DARIA NULLPOINTEREXCEPTION
if (salida==false) {
FragmentManager fragmentManager = getSupportFragmentManager();
fragmentManager.beginTransaction()
.replace(R.id.contenedor_principal, fragment)
.commit();
listaMenu.setItemChecked(posicion, true); //MARCAMOS EL CLICKADO
drawerLayout.closeDrawer(listaMenu);
}
}
Si todo ha ido bien, ahora cambiará el estilo de nuestro action Bar y el contenido de los botones que tiene dentro. Ya nos podemos mover por nuestros fragments sin problema. Claro está que en cada recurso menu.xml que hagamos de cada fragmento, nos tocará listar los items, además de asignarles un icono apropiado.
4 Creacción de Swipey tabs en un fragment
Ahora vamos a conseguir un efecto muy chulo en uno de nuestros fragments, en el cifrador en concreto, vamos a insertar unas pestañas que al seleccionar cada una que no sea la misma en donde estamos, la vista se va a "correr" hacia un lado dejando paso a la nueva vista. Creamos 3 fragments más y añadimos a fragmentCifrador.xml el componente ViewPager :
<android.support.v4.view.ViewPager
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
</android.support.v4.view.ViewPager>
Page Adapter nueva clase
/**
* Created by makone
*/
public class PageAdapter extends FragmentPagerAdapter
{
public PageAdapter(FragmentManager fm) {
super(fm);
}
@Override
public android.support.v4.app.Fragment getItem(int position) {
// TODO Auto-generated method stub
switch (position) {
case 0:
return new fragmentNew1();
case 1:
return new fragmentNew2();
case 2:
return new fragmentNew3();
default:
break;
}
return null;
}
@Override
public int getCount() {
return 3;
}
}
Así que nuestro FragmentCifrador se queda así, claro que hay que crear fragmentNew1, fragmentNew2, fragmentNew3, modificarlos gráficamente y posteriormente ver el efecto.
Tenemos también que prestar atención al método
onDetach(), ya que cada vez que se inicia el fragment, se añaden tabs, es importante que en cada fragment por el que naveguemos lleve las mismas líneas de código en el mismo método
onDetach() que a continuación :
package FRAGMENTS.FRAGMENTS_CIFRADOR;
import android.app.Activity;
//import android.app.Fragment;
import android.net.Uri;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.view.ViewPager;
import android.support.v7.app.ActionBarActivity;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.View;
import android.view.ViewGroup;
import GESTION.configActionBAR;
import JAVAEE.PageAdapter;
import texto.cifrador.makone.cifradortexto.R;
public class fragmentCifrador extends Fragment implements android.support.v7.app.ActionBar.TabListener{
// TODO: Rename parameter arguments, choose names that match
// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
private static final String ARG_PARAM1 = "param1";
private static final String ARG_PARAM2 = "param2";
// TODO: Rename and change types of parameters
private String mParam1;
private String mParam2;
private OnFragmentInteractionListener mListener;
public static android.support.v7.app.ActionBar actionBar;
public configActionBAR CAB;
Activity host;
ViewPager viewpager;
PageAdapter ft;
// TODO: Rename and change types and number of parameters
public static fragmentCifrador newInstance(String param1, String param2) {
fragmentCifrador fragment = new fragmentCifrador();
Bundle args = new Bundle();
args.putString(ARG_PARAM1, param1);
args.putString(ARG_PARAM2, param2);
fragment.setArguments(args);
return fragment;
}
public fragmentCifrador() {
// Required empty public constructor
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
setHasOptionsMenu(true); //PARA PODER MODIFICAR EL MENU
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
mParam1 = getArguments().getString(ARG_PARAM1);
mParam2 = getArguments().getString(ARG_PARAM2);
}
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_cifrador, container, false);
aniadirTabs(v);
return v;
}
private void aniadirTabs(View v) {
//RECUPERAMOS CONTEXTO APLICACION
// host = (Activity) activity.getApplicationContext();
host = (Activity) v.getContext();
actionBar = ((ActionBarActivity) host).getSupportActionBar();
CAB = new configActionBAR(host, 2);
SacarPestanias (v,host);
}
public void SacarPestanias (View v,Activity activity){
viewpager = (ViewPager) v.findViewById(R.id.pager);
//ft = new PageAdapter(getSupportFragmentManager());
//Aqui le añadimos los fragments donde nos queremos mover
ft = new PageAdapter(((android.support.v7.app.ActionBarActivity) activity)
.getSupportFragmentManager());
viewpager.setAdapter(ft);
actionBar.setNavigationMode(android.app.ActionBar.NAVIGATION_MODE_TABS);
actionBar.addTab(actionBar.newTab().setText("CASAR").setTabListener(this));
actionBar.addTab(actionBar.newTab().setText("Estadisticas").setTabListener(this));
actionBar.addTab(actionBar.newTab().setText("Editor").setTabListener(this));
viewpager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageSelected(int arg0) {
actionBar.setSelectedNavigationItem(arg0);
}
@Override
public void onPageScrolled(int arg0, float arg1, int arg2) {
// TODO Auto-generated method stub
}
@Override
public void onPageScrollStateChanged(int arg0) {
// TODO Auto-generated method stub
}
});
}
@Override
public void onTabSelected(android.support.v7.app.ActionBar.Tab tab, FragmentTransaction fragmentTransaction) {
viewpager.setCurrentItem(tab.getPosition());
}
@Override
public void onTabUnselected(android.support.v7.app.ActionBar.Tab tab, FragmentTransaction fragmentTransaction) { }
@Override
public void onTabReselected(android.support.v7.app.ActionBar.Tab tab, FragmentTransaction fragmentTransaction) { }
// TODO: Rename method, update argument and hook method into UI event
public void onButtonPressed(Uri uri) {
if (mListener != null) {
mListener.onFragmentInteraction(uri);
}
}
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
/* try {
mListener = (OnFragmentInteractionListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement OnFragmentInteractionListener");*/
}
@Override
public void onDetach() {
super.onDetach();
mListener = null;
actionBar = ((ActionBarActivity) host).getSupportActionBar();
actionBar.removeAllTabs();
actionBar.setNavigationMode(android.app.ActionBar.NAVIGATION_MODE_STANDARD); //PARA BORRAR PESTAÑERO
}
public interface OnFragmentInteractionListener {
// TODO: Update argument type and name
public void onFragmentInteraction(Uri uri);
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
menu.clear();
inflater.inflate(R.menu.menu_cifradores, menu);
}
}
Podeis descargar el código fuente de thebestandroide con sólo compartir en facebook,twitter,linkedin o suscribirte a nuestro canal RSS más abajo.