En artículos anteriores del Curso de Programación Android hemos visto como dar forma a la interfaz de usuario de nuestra aplicación mediante el uso de diversos tipos de layouts, como por ejemplo los lineales, absolutos, relativos, u otros más elaborados como los de tipo lista o tabla. Éstos van a ser siempre los elementos organizativos básicos de nuestra interfaz, pero sin embargo, dado el poco espacio con el que contamos en las pantallas de muchos dispositivos, o simplemente por cuestiones de organización, a veces es necesario/interesante dividir nuestros controles en varias pantallas. Y una de las formas clásicas de conseguir esto consiste en la distribución de los elementos por pestañas o tabs. Android por supuesto permite utilizar este tipo de interfaces, aunque lo hace de una forma un tanto peculiar, ya que la implementación no va a depender de un sólo elemento sino de varios, que además deben estar distribuidos y estructurados de una forma determinada nada arbitraria. Adicionalmente no nos bastará simplemente con definir la interfaz en XML como hemos hecho en otras ocasiones, sino que también necesitaremos completar el conjunto con algunas líneas de código. Desarrollemos esto poco a poco.
En Android, el elemento principal de un conjunto de pestañas será el control TabHost. Éste va a ser el contenedor principal de nuestro conjunto de pestañas y deberá tener obligatoriamente como id el valor «@android:id/tabhost«. Dentro de éste vamos a incluir un LinearLayout que nos servirá para distribuir verticalmente las secciones principales del layout: la sección de pestañas en la parte superior y la sección de contenido en la parte inferior. La sección de pestañas se representará mediante un elemento TabWidget, que deberá tener como id el valor «@android:id/tabs«, y como contenedor para el contenido de las pestañas añadiremos un FrameLayout con el id obligatorio «@android:id/tabcontent«. Por último, dentro del FrameLayout incluiremos el contenido de cada pestaña, normalmente cada uno dentro de su propio layout principal (en mi caso he utilizado LinearLayout) y con un id único que nos permita posteriormente hacer referencia a ellos fácilmente (en mi caso he utilizado por ejemplo los ids «tab1«, «tab2«, …). A continuación represento de forma gráfica toda la estructura descrita.
Si traducimos esta estructura a nuestro fichero de layout XML tendríamos lo siguiente:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_height="match_parent" android:layout_width="match_parent"> <TabHost android:id="@android:id/tabhost" android:layout_width="match_parent" android:layout_height="match_parent"> <LinearLayout android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" > <TabWidget android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@android:id/tabs" /> <FrameLayout android:layout_width="match_parent" android:layout_height="match_parent" android:id="@android:id/tabcontent" > <LinearLayout android:id="@+id/tab1" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" > <TextView android:id="@+id/textView1" android:text="Contenido Tab 1" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </LinearLayout> <LinearLayout android:id="@+id/tab2" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" > <TextView android:id="@+id/textView2" android:text="Contenido Tab 2" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </LinearLayout> </FrameLayout> </LinearLayout> </TabHost> </LinearLayout>
Como podéis ver, como contenido de las pestañas tan sólo he añadido por simplicidad una etiqueta de texto con el texto «Contenido Tab NºTab«. Esto nos permitirá ver que el conjunto de pestañas funciona correctamente cuando ejecutemos la aplicación.
Con esto ya tendríamos montada toda la estructura de controles necesaria para nuestra interfaz de pestañas. Sin embargo, como ya dijimos al principio del artículo, con esto no es suficiente. Necesitamos asociar de alguna forma cada pestaña con su contenido, de forma que el el control se comporte correctamente cuando cambiamos de pestaña. Y esto tendremos que hacerlo mediante código en nuestra actividad principal.
Empezaremos obteniendo una referencia al control principal TabHost y preparándolo para su configuración llamando a su método setup(). Tras esto, crearemos un objeto de tipo TabSpec para cada una de las pestañas que queramos añadir mediante el método newTabSpec(), al que pasaremos como parámetro una etiqueta identificativa de la pestaña (en mi caso de ejemplo «mitab1«, «mitab2«, …). Además, también le asignaremos el layout de contenido correspondiente a la pestaña llamando al método setContent(), e indicaremos el texto y el icono que queremos mostrar en la pestaña mediante el método setIndicator(texto, icono). Veamos el código completo.
Resources res = getResources(); TabHost tabs=(TabHost)findViewById(android.R.id.tabhost); tabs.setup(); TabHost.TabSpec spec=tabs.newTabSpec("mitab1"); spec.setContent(R.id.tab1); spec.setIndicator("", res.getDrawable(android.R.drawable.ic_btn_speak_now)); tabs.addTab(spec); spec=tabs.newTabSpec("mitab2"); spec.setContent(R.id.tab2); spec.setIndicator("TAB2", res.getDrawable(android.R.drawable.ic_dialog_map)); tabs.addTab(spec); tabs.setCurrentTab(0);
Si vemos el código, vemos por ejemplo como para la primera pestaña creamos un objeto TabSpec con la etiqueta «mitab1«, le asignamos como contenido uno de los LinearLayout que incluimos en la sección de contenido (en este caso R.id.tab1) y finalmente le asignamos el texto «TAB1» y el icono android.R.drawable.ic_btn_speak_now (Éste es un icono incluido con la propia plataforma Android. Si no existiera en vuestra versión podéis sustituirlo por cualquier otro icono). Finalmente añadimos la nueva pestaña al control mediante el método addTab().
Si ejecutamos ahora la aplicación tendremos algo como lo que muestra la siguiente imagen, donde podremos cambiar de pestaña y comprobar cómo se muestra correctamente el contenido de la misma.
En Android 2.x
Y en Android 4.x
Una pequeña sorpresa. Como podéis comprobar, aunque estamos indicando en todos los casos un texto y un icono para cada pestaña, el comportamiento difiere entre las distintas versiones de Android. En Android 4, el comportamiento por defecto del control TabHost es mostrar sólo el texto, o solo el icono, pero no ambos. Si eliminamos el texto de la primera pestaña y volvemos a ejecutar veremos como el icono sí aparece.
TabHost.TabSpec spec=tabs.newTabSpec("mitab1"); spec.setContent(R.id.tab1); spec.setIndicator("", res.getDrawable(android.R.drawable.ic_btn_speak_now)); tabs.addTab(spec);
Con esta pequeña modificación la aplicación se vería así en Android 4.x
En cuanto a los eventos disponibles del control TabHost, aunque no suele ser necesario capturarlos, podemos ver a modo de ejemplo el más interesante de ellos, OnTabChanged, que se lanza cada vez que se cambia de pestaña y que nos informa de la nueva pestaña visualizada. Este evento podemos implementarlo y asignarlo mediante el método setOnTabChangedListener() de la siguiente forma:
tabs.setOnTabChangedListener(new OnTabChangeListener() { @Override public void onTabChanged(String tabId) { Log.i("AndroidTabsDemo", "Pulsada pestaña: " + tabId); } });
En el método onTabChanged() recibimos como parámetro la etiqueta identificativa de la pestaña (no su ID), que debimos asignar cuando creamos su objeto TabSpec correspondiente. Para este ejemplo, lo único que haremos al detectar un cambio de pestaña será escribir en el log de la aplicación un mensaje informativo con la etiqueta de la nueva pestaña visualizada. Así por ejemplo, al cambiar a la segunda pestaña recibiremos el mensaje de log: «Pulsada pestaña: mitab2«.
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.
Curso de Programación Android en PDF
¿Te ha sido de utilidad el Curso de Programación Android? ¿Quieres colaborar de forma económica con el proyecto? Puedes contribuir con cualquier cantidad, unos céntimos, unos euros, cualquier aportación será bienvenida. Además, si tu aportación es superior a una pequeña cantidad simbólica recibirás como agradecimiento un documento con la última versión del curso disponible en formato PDF. Sea como sea, muchas gracias por colaborar!
Más información:
44 comentarios
[…] Interfaz de usuarioen Android: Tab Layout [Nuevo!] […]
Que bueno,has pillado carrerilla salva.me pongo a trastear con los 2 ultimos articulos.Espero que puedas hacer algo de servicios web.Gracias y enhorabuena.
Hola
A mi al copiar el código de xml me da error en todos lo match_parent.
Es por culpa de la versión del sdk?
Gracias
Hola Salva, no es problema de la versión del SDK sino más bien del target que estás utilizando. El valor «match_parent» se incluyó con la versión 2.2 de Android (Froyo), por lo que si estás desarrollando para una versión anterior no lo reconocerá. Utiliza en su lugar el valor «fill_parent«. Saludos.
Hola , estoy siguiendo el curso, muchas gracias por publicarlo. En este ejemplo (lo he hecho con la 2.1 y con fill_parent) si no pongo un nuevo objeto spec y reutilizo el que tengo, como haces en el código, en cuanto cambio de pestaña un par de veces se me mezclan los mensajes que pongo en cada pestaña.
Hola, queria hacer un menu fijo, que apareciera desde el principio sin tener que pulsar el boton menu en el telefono. Como no he sabido hacerlo con un menu, he intentado hacerlo con un tab layout. Mi problema es que consigo situar las pestañas en la parte inferior, pero entonces las pestañas dejan de ser seleccionables y no hacen nada.
Si fueras tan amable de indicarme el error…
Muchas gracias por tu tiempo.
Buenas tardes, soy estoy usando android 4.0.3. y no me muestra las imágenes en el emulador de eclipse ni para tabs, ni para menus. ¿Sabrías cuál es la explicación.?
PD:Las imágenes las tengo en los tres directorios y son del tamaño correcto.
Hay alguna forma de definir el texto en el archivo strings.xml en lugar de un texto definido en esta parte del código:
spec.setIndicator(«TAB1»,
he intentado con setIndicator(R.string.tab1, pero me dice lo siguiente:
The method setIndicator(CharSequence, Drawable) in the type TabHost.TabSpec is not applicable for the arguments (int, Drawable)
Hola,
¿Donde se encuentra el log de la aplicacion donde se escriben las trazas con Log.i?
Un saludo.
hola ayudenme porfa.. tengo un tab que me corre hasta ahora q estoy construyendo los frames… jejeje
pero necesito que en el tercer tab este el mapa de google….
mi principal problea biene con el id del mapa qye no puede ser declarado
Hola yeyo, podrías escribir esta consulta en el foro e incluir tu código para que podamos intentar ayudarte. Así será más fácil. Saludos.
amigo no puedo darte la donacion, la pagina dice que la donacion debe ser mayor que cero,
cuando pongo un monto superior..
ya me funciona gracias!!
estoy intentando trabajar con TabActivity, pero cuando lo importo, me sale tachado «TabActivity» lo mismo pasa cuando lo uso en el extend…tengo todo bien..pero porque sale tachadoo!!! AYUDAAAA PORFAVOOOR
Amigo, no logro visualizar los iconos de los tabs en el emulador ni en un teléfono. Estoy desarrollando bajo la versión 4.0.3. Hay algo adicional que tenga que hacer? Tengo el código tal cual está en el ejercicio y he probado con diferentes drawables.
Que tal?
Estoy provando tu codigo con Android 2.3.3, corre perfectamente pero solo me muestra el contenido de la primera tab (que es la seleccionada por defecto), justo despues de la barra de titulo de la aplicacion; y no me muestra ninguna de las dos tabs, asi que no puedo cambiar de tab.
Es algun error de la version de android o con algun «Macht_parent».
Saludos.
Muchas Gracias! No habia logrado entender q se tenia q hacer cosas en la actividad tambien, usualmente con solo el layout ya se ven los ejemplos basicos.
Que pasada de tuto vi varios y este fue el unico que me sirvio muchas gracias
Hola quisiera saber si al pulsar la pestaña, en lugar de los layouts definidos en el main.xml me podrían aparecer otros layouts… , por ejemplo, los que tengo en el res/layout
Gracias, increible tutorial
Hola quisiera saber donde se visualiza el log, aun no logro ver el mensaje que indicamos en el evento que explicaste a lo ultimo, gracias!
Hola. Cuando uso el codigo del XML me da error de ejecucion y tengo que forzar el cierre. Que puede ser. Lo he copiado tal cual.
[…] […]
hola amigo me gustaria saber si ya tienes un tutorial de como editar la parte del titulo de una aplicacion como ponerle un icono personalisado. lo que pasa que quiero ponerle un icono al titulo como lo tiene what’s upp pero quiero saber si la version es la que indica si puede ir un icono en el titulo o no. espero y puedas responder ami pregunta gracias
Me da un error en tabs.setOnTabChangedListener(new OnTabChangeListener()
muchas gracias por el aporte, no he podido hacerlo funcionar, estoy trabajando en android 4,
Excelente material!
Con la nueva libreria de compatibilidad de Android (android-support-v7-appcompat) que incluye la ActionBarCompat se pueden implementar las pestañas de manera más rápida usando ActionBar.TabListener ¿no?
Ya he visto que has añadido nuevo material relacionado con la ActionBarCompat, así que te lo dejo como sugerencia para próximas actualizaciones.
Enhorabuena por tu trabajo. Yo no dejo de volver siempre a consultar a tu curso :)
Que tal, mira tengo un proyecto como éste tipo, la única diferencia es que tengo 3 Tabs. El problema es que cuando en el primer tab (Fragment) cargo un ListView y paso al tercero me destruye el primer tab. Si paso al segundo no pasa nada. ¿Alguna sugerencia?
Una pregunta, ¿Como puedo personalizar el alto del tab? Es que quedan muy grandes quería ajustarlos al texto. hay alguna forma de cambiar el valor .
Gracias
Genial.
Tenía otro método para hacer pestañas, pero este es muy bueno para no tener que usar actividades independientes.
Saludos
como puedo hacer para cuando le de a uno de los tabs me abra otro activity
Tengo una gran duda alguien me puede ayudar … Estoy usando tabs y no se como diablos cambiar el color de la linea de seleccion y mucho menos el color de la letra solo el color de fondo de lografo cambiar … Ojo no estoy usando nada prediseñado
De antemano muchas gracias y quedo a la espera
Hola, acabo de donarte por el PDF pero sobretodo por tu esfuerzo y dedicación.
Tengo una duda con los Tabhost que tenía hace tiempo y ahora al ver tu código me ha vuelto.
Cada vez que pongo un Tabhost me sale el theme negro bastante feo, tengo puesto la última versión del SDK para la aplicación que estoy desarrollando y me ha pasado lo mismo al poner tu código en un nuevo proyecto… por qué ? he intentado cambiar de theme pero siempre me dan errores que no consigo apañar.
Solución ??
Saludos,
Estoy teniendo problemas con las creación de las pestanas. Sigo las instrucciones del tutorial y mi aplicacion no funciona.
Aqui les dejo el codigo.
fichero: fragment_main.xml
fichero MainActivity.java
….
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (savedInstanceState == null) {
getSupportFragmentManager().beginTransaction()
.add(R.id.container, new PlaceholderFragment()).commit();
}
TabHost tabs=(TabHost)findViewById(android.R.id.tabhost);
tabs.setup();
TabHost.TabSpec spec=tabs.newTabSpec(«mitab1»);
spec.setContent(R.id.tab1);
spec.setIndicator(«TAB1», null);
tabs.addTab(spec);
spec=tabs.newTabSpec(«mitab2»);
spec.setContent(R.id.tab2);
spec.setIndicator(«TAB2», null);
tabs.addTab(spec);
tabs.setCurrentTab(0);
}
….
Realmente no se donde esta el problema.
Podrian ayudarme por favor?
Gracias.
Al iniciar la aplicacion me tira este error:
10-10 02:21:09.484: E/AndroidRuntime(1189): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.logros/com.example.logros.MainActivity}: java.lang.NullPointerException
Y tengo exactamente lo mismo que el github
Hola muchas gracias por los tutoriales. Estoy aprendiendo a programar en Android y tengo una duda que no consigo solucionar en ninguna parte. Mi intencion es, partiendo de una pantalla con 4 pestañas y teniendo un boton o lista de elementos en una de ellas poder hacer click en el boton o en uno de los elementos de la lista mencionados y que se abra otro layout. Se como implementar pestañas pero no soy capaz de hacer que se me abra un segundo layout al hacer click sobre un boton en una de las pestañas. Le estaria muy agradecido si pudiera ayudarme. Un saludo.
Hola sgoliver,
¿Podrías por favor indicarnos qué cambios habría que realizar para mostrar el TabWidget en la parte inferior de la pantalla y no arriba como has hecho en tu ejemplo?
Gracias.
Hola, primero gracias por estas publicaciones, me han servido mucho.
Segundo: ya hice mi tabhost y funciona todo bien.. mi pregunta es:
tengo la acivivty con la tabhost (4 pesañas) y tengo otra activity con botones (4 botones), y quiero que estos botones me redirijan uno a cada pestaña de la tahost, cómo lo hago? El rpimer boton lo dirigí a la activity de la tabhost y me abre por defecto la primera pestaña, pero quiero que cada botón me abra una pestaña distinta, por favor, ayuda.
Hola… muy bueno su aporte… para deslizar la pantalla y pasar al otro tab es posible?
Buenas,
genial la serie de tutoriales que estás haciendo. La verdad que me parece increible todo lo que has llevado a acabo.
Tengo una duda sobre este concepto, ¿esto es lo mismo que material design?, es decir, últimamente oigo mucho ese estilo, pero creo que es relativamente nuevo, entonces me preguntaba si hay alguna diferencia entre estas pestañas y las de material design o son lo mismo. Obviamente me refiero a nivel de visualización, funcionalidad, acabado, etc.
Gracias y un saludo!
Super agradecido!! El mejor blog!
Buenos días, lo primero enhorabuena por tus tutoriales, están muy trabajados y son muy claros de entender, ahora mi duda. Me estoy obsesionando con algo que no puede ser complicado pero me estoy liando y no lo saco. El tema es el siguiente:
Tengo una actividad que recibe una serie de variables (id, latitud, longitud), esta actividad utiliza dos tabs con FragmentPagerAdapter, el primer tab con su actividad que debería recibir el id para actualizar los datos de su fragment y la segunda actividad debería recibir la latitud y la longitud para actualizar un mapa. Lo que quiero saber es como pasar estas variables a cada actividad para actualizar sus respectivos fragments.
Gracias de antemano por el tiempo y la atención prestada.
Hola, como haríamos si retornamos al view que contiene las pestañas del Tab y posicicionarnos del Tab que queremos, por ejemplo si al cargar de inicio el view lo posicionamos en tabs.setCurrentTab(0); pero si nos vamos a otro view regresamos como haríamos para hacer el tabs.setCurrentTab(1); desde Otro view? en
Intent i = new Intent(Origen.this, DestinoTab.class);
startActivity(i);
finish()
Saludos.
Tengo el codigo igual pero en mi telefono la aplicación se detiene :c alguien sabe por que?