¡IMPORTANTE!: este tutorial está obsoleto, consultar en su lugar Diseño Android: Toolbar y Pestañas con Material Design
El artículo anterior presentó el widget ViewPager para construir interfaces gráficas «paginadas». Una vez que tenemos nuestro ViewPager funcionando, el siguiente paso será utilizar un mecanismo que nos permita saber en qué página estamos y/o si hay páginas adyacentes a las que se pueda navegar. Gracias a Jake Wharton (un prestigioso desarrollador Android al que recomiendo seguir en Twitter y G+) disponemos de una librería «open source» con licencia Apache 2.0 llamada ViewPagerIndicator. En este artículo veremos cómo usarla con el proyecto del artículo anterior y algunas de sus posibilidades, siendo en mi opinión la más interesante la posibilidad de utilizar pestañas y permitir la navegación en el ViewPager tanto mediante desplazamiento gestual como por pulsación de la pestaña.
Podemos echar un vistazo a todo lo que ofrece esta librería instalando su demo desde Google Play.
Entorno de pruebas:
Requisitos: Los conocimientos presentados en el artículo anterior (uso básico de un ViewPager).
Proyecto de ejemplo
Tal y como ya se ha comentado en la introducción, se utilizará el proyecto de ejemplo del artículo anterior con nombre distinto para poder tener los dos proyectos en el mismo Eclipse así que vamos a ver cómo incluir en el mismo la libreria ViewPagerIndicator. Describiré el proceso para Eclipse ADT, pero esta libreria también se puede utilizar en Android Studio, Maven o Graddle sin problema alguno (se encuentra en los repositorios de Maven).
En primer lugar hay que descargar y descomprimir la última versión de ViewPagerIndicator. Encontraremos dos directorios, uno con la aplicación demo (/sample) y otro con la libreria (/library) propiamente dicha que formará parte de nuestro proyecto. Lo que hay que hacer es importar esta libreria en Eclipse: Import->Android->Existing Android Code Into Workspace. Seleccionamos el directorio library y creamos el proyecto a partir de su código
Ahora añadimos este proyecto como dependencia de la demo de ViewPager que vamos a utilizar. Esto se hace desde las propiedades del proyecto en la sección Android.
Tras la importación he obtenido el siguiente error:
El mensaje es claro e indica que tenemos en nuestro proyecto la misma libreria duplicada (android-support-v4.jar) ya que ViewPagerIndicator la incorpora en su libs pero su contenido no coincide con la que tenemos en el libs de nuestra demo al tratarse de una versión anterior por lo que Eclipse ADT no sabe cuál de las dos debe usar. La solución por la que he optado es reemplazar el .jar de ViewPagerIndicator por el de la demo que es la última versión disponible. Ahora que las dos librerias con el msimo nombre son iguales no hay ningún problema. Es más, he optado por eliminarla del proyecto demo al no ser ya necesaria (podemos usarla al estar incluída en una librería que tenemos como dependencia).
Usando los indicadores de página
Nota: Para ver y «jugar» con todas las opciones disponibles podemos importar en Eclipse la demo del directorio samples. En este artículo vamos a ver lo básico.
Para añadir un indicador «típico» (el mismo ejemplo usado en la página oficial) que nos muestre el nombre de cada página y las adyacentes se seguirán los siguientes pasos:
- Incluir el widget en el layout justo donde definamos el ViewPager.
<?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" > <com.viewpagerindicator.TitlePageIndicator android:id="@+id/indicator" android:layout_width="fill_parent" android:layout_height="wrap_content" /> <android.support.v4.view.ViewPager android:id="@+id/pager" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@android:color/darker_gray" android:paddingBottom="5dp" android:paddingLeft="8dp" android:paddingRight="8dp" android:paddingTop="5dp" /> </LinearLayout>
- Asociar al ViewPagerIndicator el ViewPager y el listener para el scroll.
- Implementar en el PageAdapter el método que devuelve el nombre de cada página.
class MainPageAdapter extends PagerAdapter { @Override public CharSequence getPageTitle(int position) { String title = null; switch (position) { case 0: title = getString(R.string.one); break; case 1: title = getString(R.string.two); break; case 2: title = getString(R.string.three); break; default: title = getString(R.string.four); break; } return title; } ...
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); viewPager = (ViewPager) findViewById(R.id.pager); viewPager.setAdapter(new MainPageAdapter()); TitlePageIndicator titleIndicator = (TitlePageIndicator)findViewById(R.id.indicator); titleIndicator.setViewPager(viewPager); titleIndicator.setOnPageChangeListener(new OnPageChangeListener() { @Override public void onPageSelected(int position) { Toast.makeText(MainActivity.this, "page " + (position + 1), Toast.LENGTH_SHORT).show(); } @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { } @Override public void onPageScrollStateChanged(int state) { } }); }
Ya está operativo el ViewPagerIndicator y se puede probar su funcionamiento. Nótese que TitlePageIndicator permite cambiar de página pulsando sobre el título de la misma.
Estilos
Para persoalizar los estilos del indicador lo mejor es recurrir a la aplicación sample que viene junto a la librería y ver en el fichero /res/values/styles.xml las propiedades del cada tipo de indicador que podemos modificar. Copiamos la que necesitemos al styles.xml de nuestra aplicación, modificamos los atributos que sean oportunos y aplicamos el estilo al ViewPagerIndicator. Para nuestro indicador usaremos el siguiente estilo:
<?xml version="1.0" encoding="utf-8"?> <resources> <style name="CustomTitlePageIndicator"> <!-- color de fondo de toda la barra en la que se muestra el nombre --> <item name="android:background">#eaeaea</item> <!-- color de la barra indicadora, debajo de los nombres --> <item name="footerColor">#b2cb39</item> <!-- ancho linea inferior del footer --> <item name="footerLineHeight">2dp</item> <!-- ancho de la barra inferior al nombre --> <item name="footerIndicatorHeight">4dp</item> <!-- estilo del indicador inferior "none","triangle","underline"--> <item name="footerIndicatorStyle">underline</item> <!-- color del texto --> <item name="android:textColor">@android:color/black</item> <!-- color del texto de la página seleccionada --> <item name="selectedColor">@android:color/black</item> <!-- aplica bold al texto seleccionado --> <item name="selectedBold">true</item> </style> </resources>
Y se aplica al widget
<com.viewpagerindicator.TitlePageIndicator android:id="@+id/indicator" android:layout_width="fill_parent" android:layout_height="wrap_content" style="@style/CustomTitlePageIndicator"/>
Otros indicadores
ViewPagerIndicator proporciona hasta seis tipos de indicadores. Además del que hemos usado tenemos los siguientes:
- CirclePageIndicator: muestra una fila de circunferencias que representan cada página, la página seleccionada se muestra como un círculo.
- IconPageIndicator: representa cada página con un pequeño icono. Requiere que el PageAdapter implemente la interfaz IconPagerAdapter que contiene un método que devuelve el icono correspondeinte a cada página (el identificador de su drawable). Aunque resulte tentador estos íconos no se pueden pulsar para cambiar de página (aunque siendo un proyecto open source siempre se podría revisar el código para averiguar cómo implementar esta funcionalidad).
- LinePageIndicator: similar a CirclePageIndicator pero con rectángulos en lugar de círculos.
- UnderlinePageIndicator: muestra una especie de barra de scroll horizontal que sólo indica la página actual y por defecto «desaparece» cuando la página es mostrada.
- TabPageIndicator: similar a TitlePageIndicator pero simulando pestañas. Vamos a utilizarlo en el proyecto de ejemplo en la siguiente sección.
Pestañas
Con TabPageIndicator podemos hacer que los indicadores simulen ser pestañas. Éstas, al igual que los títulos de TitlePageIndicator, son pulsables. Utilizarlas es sencillo y la única particularidad que tienen es que las pestañas consisten en 9-patch que tendremos que crear (o bien reutilizar las que viene en el proyecto de ejemplo). Para utilizar pestañas en nuestro ejemplo tendremos que:
- Usar su indicador en la Activity:
TabPageIndicator titleIndicator = (TabPageIndicator)findViewById(R.id.indicator);
- También en el layout
<com.viewpagerindicator.TabPageIndicator android:id="@+id/indicator" android:layout_width="fill_parent" android:layout_height="wrap_content" />
- Y ahora, lo más importante que es el estilo a aplicar. Para que funcione correctamente, he tenido que sobreescribir el estilo por defecto para TabViewPager (vpiTabPageIndicatorStyle) creando un tema para mi aplicación (AppTheme); aplicar directamente el estilo al widget no me ha funcionado a diferencia del TitlePageIndicator.
<style name="AppTheme" parent="android:Theme.Light" > <item name="vpiTabPageIndicatorStyle">@style/CustomTabPageIndicator</item> </style> <style name="CustomTabPageIndicator" parent="Widget.TabPageIndicator"> <item name="android:textAppearance">@android:style/TextAppearance.Medium</item> <item name="android:background">@drawable/custom_tab_indicator</item> <item name="android:textColor">@android:color/black</item> <item name="android:textSize">15sp</item> <item name="android:divider">@drawable/custom_tab_indicator_divider</item> <item name="android:dividerPadding">10dp</item> <item name="android:showDividers">middle</item> <item name="android:paddingLeft">8dp</item> <item name="android:paddingRight">8dp</item> <item name="android:fadingEdge">horizontal</item> <item name="android:fadingEdgeLength">8dp</item> </style>
- Obsérvese que necesitamos un drawable con selectores para la pestaña (/res/drawable/custom_tab_indicator.xml). El separador de pestañas es un 9-patch.
<?xml version="1.0" encoding="utf-8"?> <!-- Copyright (C) 2008 The Android Open Source Project Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. --> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <!-- Non focused states --> <item android:state_focused="false" android:state_selected="false" android:state_pressed="false" android:drawable="@drawable/custom_tab_indicator_unselected" /> <item android:state_focused="false" android:state_selected="true" android:state_pressed="false" android:drawable="@drawable/custom_tab_indicator_selected" /> <!-- Focused states --> <item android:state_focused="true" android:state_selected="false" android:state_pressed="false" android:drawable="@drawable/custom_tab_indicator_unselected_focused" /> <item android:state_focused="true" android:state_selected="true" android:state_pressed="false" android:drawable="@drawable/custom_tab_indicator_selected_focused" /> <!-- Pressed --> <!-- Non focused states --> <item android:state_focused="false" android:state_selected="false" android:state_pressed="true" android:drawable="@drawable/custom_tab_indicator_unselected_pressed" /> <item android:state_focused="false" android:state_selected="true" android:state_pressed="true" android:drawable="@drawable/custom_tab_indicator_selected_pressed" /> <!-- Focused states --> <item android:state_focused="true" android:state_selected="false" android:state_pressed="true" android:drawable="@drawable/custom_tab_indicator_unselected_pressed" /> <item android:state_focused="true" android:state_selected="true" android:state_pressed="true" android:drawable="@drawable/custom_tab_indicator_selected_pressed" /> </selector>
- Por último, se importa también del sample los 9-patch necesarios (sólo lo he hecho para la resolución xhdpi)
Ya tenemos nuestro ViewPager con pestañas, ahora es cuestión de adaptar las mismas al estilo de nuestra aplicación. El resultado final con las pestañas de la demo oficial de ViewPagerIndicator en Jelly Bean:
El ancho de la pestaña es igual para todas las que se muestran en pantalla por lo que hay que tener cuidado y utilizar los títulos más cortos posibles. No obstante, se puede forzar la visualización del title completo aplicando el siguiente pull request.
Código completo
El proyecto completo para Eclipse ADT se encuentra en Github. Para más información sobre cómo utilizar GitHub, consultar este artículo.
Pero, entonces¿ no hay forma de cambiar de página desde ningún tipo de ViewPageIndicator?
Muy bueno el tutorial anterior de View Page 😉 , fué el que me sirvió más de todos los que leí.
.
Pero en este estoy trabado :/, culpa de Android Studio (deje Eclipse porque este otro tenia cosas a favor). Pero no encuentro la forma de importar la libreria. Creo que llevo dos horas Googleando y probando cosas
Alguna solucion?
No juegues, gracias valedor, me sirvió mucho tu post, saludos desde México D.F.
Excelente aporte… Impresionante… Solo una pregunta como puedo hacer que al seleccionar una tab en el tabpagerindicator la animación de cambio de tab sea mas lenta
En principio la velocidad de scroll del ViewPager no es configurable pero puedes aplicar esta técnica: http://stackoverflow.com/questions/10812009/change-viewpager-animation-duration-when-sliding-programmatically#14179739
Supongo que dentro de startScroll se puede jugar con los parámetros de entrada para ajustar la velocidad según el número de pestañas a desplazar.
Excelente! No te imaginas lo que me ayudaste, sabes como hacer para cambiar el color rojo que se encuentra debajo de los titulos en la tab?
El tab se «estila» mediante imágenes 9-patch que encontrarás en el código de ejemplo en res/drawable-xhdpi/custom_tab_indicator_…Puedes simplemente utilizar esos ficheros y cambiar los colores.
Si quieres un efecto Google Play, te recomiendo utilizar esta otra librería: https://github.com/astuetz/PagerSlidingTabStrip
Hola,
Estoy tratando de incluir una rutina del tipo AsyncTask para cargar la lista, pero esta se ejecuta dos veces, ya que no se porque se ejecuta dos veces el método initListView()
Hola primero que nada, Felicidades por tutorial realmente es bueno. pero tengo un problema no he podido solucionar el problema de las librerias, al hacer la dependecia me salen mil errores y e borrado el .jar de libs como lo sugeriste pero creo que no era asi. Ayuda porfavor!!!!!
Saludos