Inicio Android Action Bar en Android (II)

Action Bar en Android (II)

por sgoliver

Como comenté en el artículo anterior del curso Android proporciona un mecanismo que nos permite «integrar» (por llamarlo de alguna manera) una barra de pestañas con la action bar.

En este momento muchos de vosotros seguro que habréis pensando: «Yo ya he aprendido a usar el control TabWidget en un artículo anterior de tu curso. ¿por qué iba a querer aprender otra forma de utilizar pestañas?«. Pues bien, además de convertirnos instantaneamente en programadores mucho más modernos y elegantes por utilizar fragments para nuestros tabs :), el hecho de enlazar una lista de pestañas con la action bar tiene una serie de ventajas adicionales, relacionadas sobre todo con la adaptación de tu interfaz a distintos tamaños y configuraciones de pantalla. Así, por ejemplo, si Android detecta que hay suficiente espacio disponible en la action bar, integrará las pestañas dentro de la propia action bar de forma que no ocupen espacio extra en la pantalla. Si por el contrario no hubiera espacio suficiente colocaría las pestañas bajo la action bar como de costumbre. Más tarde veremos un ejemplo gráfico de esto, pero ahora empecemos.

Lo primero que debemos hacer será crear un nuevo fragment para albergar el contenido de cada una de las pestañas que tendrá nuestra aplicación. Como ya vimos en el artículo dedicado a los fragments, necesitaremos crear como mínimo una pareja de ficheros para cada fragment, el primero para el layout XML y el segundo para su código java asociado. Para el caso de ejemplo de este artículo, que partirá del ya construido en el artículo anterior, incluiré tan sólo dos pestañas, y los ficheros asociados a ellas los llamaré de la siguiente forma:

Pestaña 1:

  • Tab1Fragment.java
  • fragment1.xml

Pestaña 2:

  • Tab2Fragment.java
  • fragment2.xml

La interfaz de los fragmets será mínima para no complicar el ejemplo, y contendrán únicamente una etiqueta de texto «Tab 1» o «Tab 2» para poder diferenciarlas. Por ejemplo para la pestaña 1 tendríamos un fichero fragment1.xml con el siguiente contenido:

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

    <TextView
        android:id="@+id/textView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/tab_1" />

</LinearLayout>

Por su parte, su clase java asociada Tab1Fragment.java no tendrá ninguna funcionalidad, por lo que el código se limita a inflar el layout y poco más:

package net.sgoliver.android.actionbartabs;

import android.app.Fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

public class Tab1Fragment extends Fragment {
	@Override
	public View onCreateView(LayoutInflater inflater,
			ViewGroup container, Bundle savedInstanceState) {
		return inflater.inflate(R.layout.fragment1, container, false);
	}
}

Con esto ya tendríamos preparado el contenido de nuestras pestañas, y nos quedaría añadirlas a nuestra aplicación, enlazarlas con la action bar, y asignarles un listener desde el que poder responder a los eventos que se produzcan.

Vamos a empezar por el último paso, crear el listener. Como he comentado, este listener debe contener el código asociado a los eventos clásicos de las pestañas  (normalmente la selección, reselección, o deselección de pestañas) y entre otras cosas tendrá que encargarse de mostrar en cada momento el fragment correspondiente a la pestaña seleccionada. Hay muchas formas de implementar este listener, y sé que la que yo voy a mostrar aquí no es la mejor de todas, pero sí es de las más sencillas para empezar. Vamos a crear nuestro listener creando una nueva clase que extienda de ActionBar.TabListener, en mi caso la llamaré MiTabListener (en un alarde de genialidad). Dentro de esta clase tendremos que sobrescribir los métodos de los eventos onTabSelected(), onTabUnselected() y onTabReselected(). Creo que el nombre de cada uno explica por sí solo lo que hacen. Veamos primero el código:

package net.sgoliver.android.actionbartabs;

import android.app.ActionBar;
import android.app.Fragment;
import android.app.FragmentTransaction;
import android.app.ActionBar.Tab;
import android.util.Log;

public class MiTabListener implements ActionBar.TabListener {

	private Fragment fragment;

	public MiTabListener(Fragment fg)
	{
		this.fragment = fg;
	}

	@Override
	public void onTabReselected(Tab tab, FragmentTransaction ft) {
		Log.i("ActionBar", tab.getText() + " reseleccionada.");
	}

	@Override
	public void onTabSelected(Tab tab, FragmentTransaction ft) {
		Log.i("ActionBar", tab.getText() + " seleccionada.");
		ft.replace(R.id.contenedor, fragment);
	}

	@Override
	public void onTabUnselected(Tab tab, FragmentTransaction ft) {
		Log.i("ActionBar", tab.getText() + " deseleccionada.");
		ft.remove(fragment);
	}
}

Como podéis ver, en cada uno de estos métodos lo único que haremos en nuestro caso será mostrar u ocultar nuestros fragments de forma que quede visible el correspondiente a la pestaña seleccionada. Así, en el evento onTabSelected() reemplazaremos el fragment actualmente visible con el de la pestaña seleccionada (que será un atributo de nuestra clase, después veremos dónde y cómo se asigna), y en el método onTabUnselected() ocultamos el fragment asociado a la pestaña ya que está habrá sido deseleccionada. Ambas acciones se realizan llamando al método correspondiente de FragmentTransaction, que nos llega siempre como parámetro y nos permite gestionar los fragments de la actividad. En el primer caso se usará el método replace() y en el segundo el método remove(). Además de todo esto, he añadido mensajes de log en cada caso para poder comprobar que se lanzan los eventos de forma correcta.

Implementado el listener tan sólo nos queda crear las pestañas, asociarle sus fragments correspondientes, y enlazarlas a nuestra action bar. Todo esto lo haremos en el método onCreate() de la actividad principal. Son muchos pasos, pero sencillos todos ellos.

Comenzaremos obteniendo una referencia a la action bar mediante el método getActionBar(). Tras esto estableceremos su método de navegación a NAVIGATION_MODE_TABS para que sepa que debe mostrar las pestañas. A continuació creamos las pestañas el método newTab() de la action bar y estableceremos su texto con setText(). Lo siguiente será instanciar los dos fragments y asociarlos a cada pestaña a través de la clase listener que hemos creado, llamando para ello a setTabListener(). Y por último añadiremos las pestañas a la action bar mediante addTab(). Estoy seguro que con el código se entenderá mucho mejor:

@Override
protected void onCreate(Bundle savedInstanceState) {
	super.onCreate(savedInstanceState);
	setContentView(R.layout.activity_main);

	//Obtenemos una referencia a la actionbar
	ActionBar abar = getActionBar();

	//Establecemos el modo de navegación por pestañas
	abar.setNavigationMode(
  		ActionBar.NAVIGATION_MODE_TABS);

	//Ocultamos si queremos el título de la actividad
	//abar.setDisplayShowTitleEnabled(false);

	//Creamos las pestañas
	ActionBar.Tab tab1 =
		abar.newTab().setText("Tab 1");

	ActionBar.Tab tab2 =
		abar.newTab().setText("Tab 2");

	//Creamos los fragments de cada pestaña
        Fragment tab1frag = new Tab1Fragment();
        Fragment tab2frag = new Tab2Fragment();

        //Asociamos los listener a las pestañas
        tab1.setTabListener(new MiTabListener(tab1frag));
        tab2.setTabListener(new MiTabListener(tab2frag));

        //Añadimos las pestañas a la action bar
        abar.addTab(tab1);
        abar.addTab(tab2);
}

Con esto habríamos acabado. Si ejecutamos la aplicación en el emulador veremos algo como lo siguiente:

actionbar-vertical-tabs

Como vemos, Android ha colocado nuestras pestañas bajo la action bar porque no ha encontrado sitio suficiente arriba. Pero si probamos con la orientación horizontal (Ctrl+F12) pasa lo siguiente (click para ampliar):

actionbar-horizontal-tabs

Dado que ya había espacio suficiente en la propia action bar, Android ha colocado las pestañas directamente sobre ella de forma que hemos ganado espacio para el resto de la interfaz. Esto no lo habríamos podido conseguir utilizando el control TabWidget.

Por último, si cambiamos varias veces de pestaña deberíamos comprobar que se muestra en cada caso el fragment correcto y que el en log aparecen los mensajes de depuración que hemos incluido.

Espero que con estos dos últimos artículos hayáis aprendido al menos a aprovechar la funcionalidad básica de la action bar. Habría por supuesto más cosas que contar, por ejemplo algunas cuestiones de navegación entre actividades, pero eso lo dejaremos para más adelante.

Puedes consultar y/o descargar el código completo de los ejemplos desarrollados en este artículo accediendo a la pagina del curso en GitHub.

También te puede interesar

18 comentarios

Jesús 19/03/2013 - 20:48

Gracias por el Tutorial, ¿Esto se puede implementar con ViewPager y con varios Fregments independientes, como mostrar las Tab en una columna y la información en otra?

Responder
FanBoy 17/04/2013 - 23:05

Me ha surgido un problema, cuando creo y escondo el icono de home, los tab se suben y la barra se baja O_o

Alguien sabe por que ?

Responder
Juan Carlos 30/04/2013 - 12:35

Gracias sgoliver por todo, yo al menos estoy aprendiendo mucho, mi pregunta es: como implento código en las clases?, es decir, quiere que en la tab uno me aparezca un listview con una serie de datos en la dos varios textview con otros datos que saco de otro clase, etc… ando algo perdido.
Un saludo

Responder
arezong 03/05/2013 - 19:49

Me sumo a la pregunta de Jesus.

Como se puede implementar el ViewPager para que al deslizar el dedo horizontalmente cambie de pestaña. Al igual que lo hace la app de Twitter.

Muchas gracias por el tutorial.

Saludos.

Responder
Jose Antonio 30/05/2013 - 19:12

Tengo una duda puesto que la actividad principal crea los Fragments de este modo:
Fragment tab1frag = new Tab1Fragment();
Fragment tab2frag = new Tab2Fragment();
Pero me da un error que he solucionado de este modo:
Fragment tab1frag = new Fragment();
Fragment tab2frag = new Fragment();

¿A nadie le había pasado lo mismo?

Responder
Jose Antonio 31/05/2013 - 9:13

Fallo corregido, las clases Fragments no extendía de Fragment

Responder
Mercedes 14/06/2013 - 17:01

Qué tal amigos, muy buenos estos cursos, yo he aprendido muchas cosas con ellos. Me surge una duda sobre el funcionamiento de los tabs que maneja el actionbar, uno dentro de un tab por medio de fragments puede tener una navegación, pero me he topado con que los tab del actionbar cada vez que se activan vuelven a crear el fragment, osea que si yo ahí tuviese la administración de un perfil de usuario y dentro del fragment tuviese un menú principal como (Cambiar contraseña, cambiar correo electrónico, cambiar datos personales) y luego pantallas para cada uno de estos o una navegación con un árbol más extenso, si cambiase a otra pantalla que sea por ejemplo publicaciones. Si el usuario regresa a la pantalla de perfil siempre le aparecerá el fragmente principal (el memú) aunque el lo haya dejado en otro punto del árbol (por ejemplo la pantalla de cambio de correo).

Mi pregunta basicamente es si hay alguna manera de que al regresar a un tab no vuelva a crear el fragment sino que mantenga todo como estaba antes.

Gracias.

Responder
Ruudy 25/10/2013 - 12:54

Muchas gracias, pero en este apartado estaria bien que especificaras que como minimo hay que usar la API level 11, al inicio del curso recomendabas la API 8, version 2.2, y he ido usandola sucesivamente hasta este paso, en el cual da errores.

Ademas con respecto a los fragments comentar que en el ejemplo previo recomendabas el uso de «import android.support.v4.app.Fragment;», y en este ejemplo todos van con «import android.app.Fragment;», ¿cual es la diferencia?

Saludos!

Responder
admin 25/10/2013 - 15:48

Hola Daniel, para versiones anteriores de Android debes utilizar ActionBarCompat. Tienes otro artículo en el curso que lo explica todo: http://www.sgoliver.net/blog/?p=4088

Saludos.

Responder
David 15/11/2013 - 23:48

Interesante el curso quizas deberias pensar en publicar tu libro en forma física ya que leer de la compu o desde el tablet es incomodo hasta ahora.
Tengo una pregunta
¿En onTabUnselected es necesario remover el fragmento?? pregutno esto porque en el evento onTabSelected usamos Replace, que me parece algo asi como reemplazar o como dicen chancar el fragment actual por el nuevo lo que implicaria que se borraria por sobreescritura

agradecido de antemano por cualquier respuesta me despido

Responder
vmllana 16/11/2013 - 12:55

Buenas,
Lo primero comentar que el manual es de lo mejor que he visto en castellano. He adquirido el pdf y me ha ayudado bastante, aunque tengo muchas dudas todavía. Una de ellas es como recojer el contenido de un editview o spinner de un fragment estando en otro, estoy en una pestaña y quiero recuperar info de otra.

Gracias y saludos

Responder
Guillermo 01/12/2013 - 17:24

Estoy aprendiendo a programar en Android con este curso y hasta el artículo anterior bien, pero en este tengo un problema. No sé de donde sale el «R.id.contenedor» en el método onTabSelected() del TabListener. Sin eso la aplicación no funciona, pero no sé de donde sale ese contenedor, ni siquiera a lo que se refiere. Estaría muy agradecido si alguien me pudiese ayudar.

Responder
Karlos 18/02/2014 - 10:06

Hola Guillermo, se que escribiste hace tiempo pero estoy haciendo tambien el curso y me surgió la misma duda que a ti, pero conseguí resolverla contenedor es el nombre que le ha dado al layout de la clase Main. espero que aun te sirva. un saludo.

Responder
jaime 14/07/2014 - 22:16

hola

Excelente tutoriales de android, me han servido de mucho.

Quisiera saber, si se puede, que solo salgan los tabs y poder quitar la barra de titulos que esta arriba de los tabs ?

gracias

Responder
jaime 14/07/2014 - 23:22

corregido:

abar.setDisplayShowTitleEnabled(false);

Responder
Carlos Eduardo Calderón 11/08/2014 - 22:38

Hola, tengo un problema, en mi galaxy tab 10″ con android 3.2 me devuelve null cuando llamo a ActionBar abar = getActionBar(); , sin embargo en android 4.3 en mi S3 me devuelve un valor… no se porque puede ser, probé incluyendo
getWindow().requestFeature(Window.FEATURE_ACTION_BAR); pero tampoco..
Gracias de antemano…

Responder
JASSON TRUJILLO 15/08/2014 - 15:26

Buen día como agrego onClickLIstener a los objetos de los tabs, gracias

Responder
Irwin Ortiz 10/12/2014 - 18:01

Hola, gracias por el tutorial, se puede colocar MapFragments en una de las pestañas? saludos

Responder

Dejar un comentario

Este sitio web utiliza cookies para que usted tenga la mejor experiencia de usuario. Si continúa navegando está dando su consentimiento para la aceptación de las mencionadas cookies y la aceptación de nuestra política de cookies, pinche el enlace para mayor información. Aceptar Más Información

Política de Privacidad y Cookies