Lo necesario para empezar a desarrollar en Android
- Java Development Kit (JDK) 7 (Mínimo) (Esto porque Android Studio usa la JVM y Dalvik(ART) para compilar tu aplicación).
- Android Studio (Es el IDE oficial para desarrollar en Android).
- Software Development Kit (SDK) (De las versiones de Android).
- Las SDKs son independientes de las actualizaciones de Android Studio, debes tenerlas al día.
- En el SDK Manager (settings > SDK Manager) encontrarémos 3 categorías.
- Tools : Paquetes que necesita Android Studio para compilar y ejecutar.
- SDK API (level) : También se pueden descargar Samples del SDK y System Images para usar el emulador nativo de Android Studio
- Extras : Incluye todas las librerias oficiales de Google; además de herramientas que mejorarán tu experiencia al ejecutar las aplicaciones.
- El Emulador nativo de Android Studio es bueno pero consume muchos recursos. Anahí recomienda Genymotion. Existen además emuladores alternativos.
- Si necesitas una guía o estándar a seguir, ya hay una propuesta por la comunidad de desarrollo en GitHub.
- Si necesitas librerías y recursos.
Arquitectura de una aplicación en Android
Arquitectura
- Librerías de código
- Archivos de recursos y vistas (las vistas suelen estar en formatos .xml)
- Código fuente (normalmente en Java o Kotlin)
- Android Manifest (toda la interacción que tendrá el exterior con la app se define acá)
Ejecutable
- Se empaqueta en formato .apk
- Contiene archivos .dex que son el código fuente empaquetado y optimizado y librerías.
- Bytecode : classes.dex
- Native code : libs/<arch>/*.so
- Resources, es necesario buscar optimizar los recursos para mejorar el peso y rendimiento de la app.
- res/
- resources.arsc
- Misc (misceláneos)
- assets/
- META-INF/
- AndroidManifest.xml
Android Studio
Estructura del proyecto
- Una vez se ha creado un nuevo proyecto, encontrarás los siguientes directorios:
- App
- manifests
- AndroidManifest.xml (tiene toda la información que relaciona el sistema operativo y el hardware)
- java (todos los controladores, lo que procesa los recursos y la interfaz, la muestra y hace funcionar)
- res (los recursos del proyecto)
- drawable (todas las imágenes)
- layout (las estructuras de las interfaces en .xml)
- menu (los menús de la aplicación)
- mipmap (todos los íconos de la app)
- values (todos los valores almacenados de la app)
- colors.xml (los valores de los colores que distinguen la app)
- dimens.xml (todo lo relacionado a dimensiones: margin, padding, etc)
- strings.xml (todas las cadenas de texto de la app)
- styles.xml (los temas que usa nuestra app - tiene que ver con material design)
- manifests
- Gradle Scripts
- build.gradle (Module: app)
- acá en la sección dependencies se incluyen las dependencias que se usarán en la app.
- En la sección android se definen cosas como la version de SDK que compila, la versión mínima y target del SDK y mñas.
- build.gradle (Module: app)
- App
Java POJOS (Plain Old Java Object)
- Los POJOS son clases simples, lo más simple posibles.
- POJOs es utilizado para describir estructuras de objetos.
- Un problema complicado hacerlo simple inspirándonos en objetos, abstraer sus atributos y convertir estos objetos en POJOs.
Ejemplo de POJO:
- en: app > java > package : puedes hacer click derecho y crear una nueva clase.
- En esta clase defines inicialmente los atributos del objeto.
- Luego, normalmente, estebleces los métodos Getter & setter, puedes hacer click derecho y presionar generate, seleccionas el método, sombreas los atributos sobre los que quieres aplicar los métodos y ¡listo!
- Aunque los POJOs no debería Heredar ni implementar. En caso de que se necesite hacer transferencia de objetos, bien se puede usar la interfaz Serializable. Esto es una excepción a la regla. - Diferencia entre Serializable y Parceable -
Arquitectura MVC - Modelo Vista Controlador
- Todas las vistas están en Código .xml
- El controlador maneja toda la interacción de las vistas, todo lo que ahí sucede está definido en el controlador (java)
- El modelo hace referencia a la representación que le damos a nuestro proyecto, a nuestro negocio, y lo que lo compone, en código (estructuras de datos, bases de datos).
- Las vistas están ubicadas en res > layout
- Los controladores son los archivos java en app > java > package
- Los modelos suelen ser los POJOs que creamos, estos, por cuestiones de orden, suele ser positivo archivarlos en un nuevo package llamado models en: **app > java > package **> models.
- Nuestro controlador llama su vista desde el método:
setContentView(R.layout.mi_vista)
- Los modelos se llaman creando un nuevo objeto de la clase del modelo:
Ejemplo
Alumno alumno = new Alumno();
alumno.setNombre("Carlos Salazar");
alumno.setFechaNacimiento(new Date());
alumno.setNumero_cuenta(1234567);
LinearLayout vs RelativeLayout
View
- Es un objeto que sirve para dibujar algo en la pantalla, con el cual el usuario pueda interactuar. Básicamente todo lo que compone nuestra interfaz es un view.
ViewGroup
- Es un objeto que se compone de otros views ordenados, dando como resultado un Layout.
LinearLayout
- Es un ViewGroup que alinea a todos los hijos en una sola dirección, Vertical u Horizontal.
RelativeLayout
- Es un ViewGroup que alinea a todos los hijos en posiciones relativas. Se pueden alinear en relación a:
- otro View
- la etiqueta Padre
ListView y GridView
ScrollView
- Es un ViewGroup que permite hacer scroll en su contenido. Debe en ciertos casos para evitar contenido cortado.
FrameLayout
- Es un ViewGroup que organiza los elementos uno encima de otro. Por defecto agrupa los elementos en la parte superior izquierda, recuerda acomodarlos como necesites usando layout_gravity y padding.
ConstraintLayout
- Es un ViewGroup que permite hacer la vista de forma responsive, adapable a todo tipo de diseño por sus propiedades relativas.
- Completamen+te sencillo de usar desde el editor gráfico.
CoordinatorLayout
- Es el Layout padre, engloba toda la pantalla utilizable por la app.
- Ayuda a especificar comportamientos e interacciones en los views hijos (como animaciones).
- Permite vistas flotantes, es decir, vistas con elevación (propiedad elevation) y sombras.
Layouts tips
- Lo contenido dentro de la etiqueta son las propiedades del view.
Widgets Básicos XML
- TextView = Label.
- EditText = Un campo para ingresar texto. Se define el tipo de contenido a ingresar con la etiqueta:
android:inputType="TIPO DE TEXTO"
- Button = Puede contener texto e imagen con texto.
- ImageButton = Boton que contiene sólo una imagen.
- Para asignar una acción al botón:
- Dentro de las propiedades del layout se puede definir un “onClick” que haga referencia a un método ya definido en el controlador Java respectivo.
- Se puede definir un “setOnClickListener”.
android:onClick="[metodo]"
- Este método debe estar creado en el controlador java recibiendo un parámetro de tipo view, que en este caso será el mismo view.
public void [metodo](View view) {}
Aplicación
- Suele ser una buena práctica, asignar un id a cada view:
android:id="@+id/[id_asignado]"
- Para los EditText, la propiedad hint es el texto que se muestra como ejemplo al usuario hasta que se posicione sobre este elemento.
Widgets Básicos JAVA
Todo view agregado en el xml ya tiene vida, es decir, ya es un objeto creado que tiene una referencia en memoria, en este caso la referencia es el id que le asignamos. Con este id podemos acceder a ese objeto desde el controlador JAVA.
Para acceder a la referencia, debemos entender que es tomado en cuenta como un “recurso”, estas referencias son almacenadas dentro de un archivo denominado R, por ello, para acceder a ellas debemos pasar por R.
Ejemplo en Java
EditText edtNombre = findViewById(R.id.[identificador])
Si te das cuenta, luego de R accedimos a id. Así mismo se puede obtener cualquier recurso de R.drawable.XXX, R.dimen.xxx, R.string.XXX*, etc.
- Toast = Es un mensaje que nos notifica o nos dá retroalimentación sobre alguna acción realizada. No tiene un xml, se define directamente desde el código controlador JAVA.
Ejemplo para evento onClick. Recibe la información de un TextView y la muestra a través de un Toast.
public void enviarDatos(View view) {
EditText edtNombre = (EditText) findViewById(R.id.nombre);
String nombre = edtNombre.getText().toString();
Toast.makeText(getBaseContext(),"Felicidades, tu nombre es " + nombre, Toast.LENGTH_LONG).show();
}
- Toast.makeText invoca al objeto Toast y a su método para escribirle texto.
- Los parámetros que pide son:
- (1.Contexto, 2.Mensaje, 3.Duración)
- luego de definidos los parámetros, se le dice al Toast que se muestre con .show().
Material Design
- https://www.materialpalette.com : Te permite generar una paleta a partir de 2 colores que seleccionas. Además te ofrece un preview de interfaz con los colores de la paleta.
- Material Design tiene como fundamento que todo elemento debe tener dinamismo, generarle al usuario la sensación de que realmente interactua con la app.
- Las librerías de material design son:
- @android:style/Theme.Material (para el tema obscuro)
- @android:style/Theme.Material.Light (tema claro)
- @android:style/Theme.Material.Light.DarkActionBar (tema claro con action bar obscura)
- Theme.AppCompat (es la libreria que nos da la compatibilidad de Material Design en versiones anteriores.
- Theme.Material (obscuro)
- Theme.AppCompat.Material (claro)
Tema
- El tema, el estilo de la app está configurado en el archivo styles.xml (res > values > styles.xml)
- Entre los diversos elementos que se pueden definir en el styles.xml, están:
- colorPrimary
- colorPrimaryDark (barra de notificaciones de android)
- textColorPrimary
- windowBackground
- navigationBarColor
Configurando nuestro tema Material
res > values > styles.xml
- en : https://material.io/tools/color/ se pueden encontrar distintas paletas de colores ofrecidas por Google.
- https://www.materialpalette.com es una herramienta excelente para generar paletas.
- Todos los colores están definidos en res > values > colors.xml
- El colorPrimary normalmente debe ser un color 500 de intensidad.
- El colorPrimaryDark suele ser 200 más intenso que el colorPrimary, es decir, 700 de intensidad.
- Los colores de acentuación o colorAccent son aquellos con una letra A antes de la intensidad. Ej: A200
Widgets de Material Design
Material Design ofrece una librería de soporte con estos widgets y que además son compatibles con versiones anteriores android.support.v7.widget.Cardview
- ToolBar
- TextInputLayout
- TextInputEditText (por defecto toma el colorAccent)
- Raised Button
- Floating Action Button
- CardView
- RecyclerView (sustituye ListView)
Más información sobre las librerías de soporte de Google - https://developer.android.com/topic/libraries/support-library/features.html#v7
Activity
- Son las diversas “pantallas” dentro de Android. Es sumamente importante tener el mayor control posible sobre el comportamiento de las actividades para así causar una mejor experiencia al usuario.
- En Android Nativo todas las pantallas (activities) deben estar compuestas de dos archivos:
- Un archivo Java con un objeto que herede de la clase Activity. Este archivo es el que controla la Interfaz.
- Un Layout (.xml) que contiene la interfaz gráfica. Este archivo es el que el Java se encargará de mostrar en pantalla.
- Todo activity debe estar declarado en el AndroidManifest.xml
<activity android:name=".NombreActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
- intent-filter : Le da instrucciones al sistema sobre que abrir cuando detecta cierta acción (se usa únicamente para definir y lanzar la pantalla inicial)
- action…MAIN : Le dice al sistema que al abrir la aplicación, la primera pantalla que muestre será esta.
- category…LAUNCHER : Ejecuta el lanzador, ya sabiendo que esta es la pantalla inicial.
Creando un Activity
- Como buena práctica, el controlador (JAVA) del Activity inicial siempre estará en el primer nivel del package y los demás controladores (JAVA) de los Activity’s los ubicaremos en una nueva carpeta app > java > [paquete] > view.
- Ubicados sobre la nueva carpeta hacemos click derecho y seleccionamos New > Activity > Empty Activity.
- Colocamos el nombre del Activity (NombreActivity) y automáticamente se creará su layout .xml relacionado (activity_nombre).
Ciclo de vida (resumen por @JuanGonzalez97)
-
onCreate(Bundle): Se llama en la creación de la actividad. Se utiliza para realizar todo tipo de inicializaciones, como la creación de la interfaz de usuario o la inicialización de estructuras de datos. Puede recibir información de estado de la actividad (en una instancia de la clase Bundle), por si se reanuda desde una actividad que ha sido destruida y vuelta a crear.
-
onStart(): Nos indica que la actividad está a punto de ser mostrada al usuario.
-
onResume(): Se llama cuando la actividad va a comenzar a interactuar con el usuario.
-
onPause(): Indica que la actividad está a punto de ser lanzada a segundo plano, normalmente porque otra actividad es lanzada.
-
onStop(): La actividad ya no va a ser visible para el usuario.
-
onRestart(): Indica que la actividad va a volver a ser representada después de haber pasado por onStop().
-
onDestroy(): Se llama antes de que la actividad sea totalmente destruida. Por ejemplo, cuando el usuario pulsa el botón de volver o cuando se llama al método finish().
Maquetando nuestros activities
- Una buena manera de guiarnos para iniciar un Layout es crear uno básico de referencia para poder ver la estructura que contiene. Por ejemplo para ver la estructura inicial del CoordinatorLayout.
- Si copias el Layout de otro Activity, recuerda modificar el tools:context… para que haga referencia al controlador (JAVA) correcto.
namespace
- “xmlns:” es un namespace. Básicamente nos permiten tener acceso a las propiedades del elemento al que hagan referencia. EJ.
- xmlns:android=“http://schemas.android.com/apk/res/android” Hace referencia a las propiedades android
- xmlns:app=“http://schemas.android.com/apk/res-auto” Hace referencia a las propiedades app
- xmlns:tools=“http://schemas.android.com/tools” Hace referencia a las propiedades tools
- Lo ideal es ubicar los namespace en el Layout padre y no repetirlos en el resto de los ViewGroups.\
Imagenes densificadas
- Todas las imagenes deben ser densificadas. La resolución de la imagen debe corresponder a las distintas densidades de pantallas disponibles (ldpi, mdpi, hdpi, xhdpi, xxhdpi, xxxhdpi, tvdpi). Esto se hace con el fin de evitar la pérdida de resolución de la imagen al ser vista desde distintos dispositivos con distintas densidades de pantalla.
- El plugin Android Drawable Importer nos ayuda con la densificación de las imagenes.
- En Drawable hacemos click derecho y seleccionamos ** New > Batch Drawable Import**. En esta nueva pantalla seleccionamos el archivo a importar y luego nos permite definir cual es la densidad original de la imagen e incluir las densidades que queremos generar mediante el plugin. Al presionar aceptar, se crearán todas las versiones de densidad seleccionadas.
Textos
- Como buena práctica, TODOS los textos deben declararse en ** app > res > values > strings.xml**.
- Se les debe asignar un name, que será la variable por la que podrá ser llamada y el texto que se quiere mostrar. EJ.
<string name="variable_string">Soy un texto</string>
- Para llamarlo desde cualquier otro elemento, se usa:
android:xxxxx="@string/variable_string"
- Para cambiar el tamaño del texto, es necesario crear la variable en el archivo dimens para que pueda ser añadida esta propiedad al texto con:
android:textSize="@dimen/VARIABLE"
- La unidad de medida recomendada para el tamaño de texto es sp, en lugar de dp.
- Si le quieres añadir un estilo personalizado a un TextInputLayout - TextInputEditText, debes hacerlo en el TextInputLayout a través de:
android:theme="@style/NOMBRE DE ESTILO
Internacionalización de texto
- Android nos permite generar versiones de multiples idioma tomando en cuenta las variables en el archivo strings.xml
- Para crear otro idioma:
- Se debe crear un nuevo “Values Resource File” en el directorio Values con el nombre strings pero este debe tener un Available qualifier de tipo Locale. Una vez lo asignas, verás la lista de idiomas con su respectiva etiqueta para agragarla y así generar una nueva carpeta values-XX, donde estarán el archivo strings con el idioma correspondiente.
- El nuevo archivo strings estará vacío y debes agregar en el, todas las variables de texto que necesites traducir desde el archivo original, y cambiar el texto por el idioma correspondiente.
Creando un estilo personalizado
- En el archivo styles están definidos los estilos, cada estilo con una etiqueta style:
<style name="CLASE u OBJETO" parent="SUELE SER EL TEMA PADRE">
</style>
- Dentro de esta etiqueta se definen los items
<itemname="VARIABLE">NUEVA CARACTERISTICA</item>
- Por ejemplo, para crear el nuevo estilo de RaisedButtonDark:
<style name="RaisedButtonDark" parent="Theme.AppCompat.Light">
<item name="colorButtonNormal">@color/colorPrimaryDark</item>
<item name="colorControlHighlight">@color/colorPrimary</item>
<item name="textColor">@android:color/white</item>
</style>
- Donde:
- RaisedButtonDark = El objeto al que queremos aplicar el estilo.
- Theme.AppCompat.Light = Es el Tema padre del cual estaremos tomando el estilo original.
- colorButtonNormal = Es el color de fondo que tiene el botón.
- colorControlHighlight = Es el color del botón cuando es presionado.
- textColor = Es el color del texto del botón.
- @android:color/white = Hace referencia a los colores ya predefinidos en android.
- Para aplicar el estilo, tendrás que hacerlo agregando android:theme="@style/NAME DEL STYLE". Un EJ. siguiendo el ejemplo anterior:
<Button
android:id="@+id/login"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/text_button_login"
android:theme="@style/RaisedButtonDark"
/>
EditText con Material Design
- EJ de estilo:
<style name="EditTextWhite" parent="TextAppearance.AppCompat">
<item name="android:textColorHint">@color/textColorWhite</item>
<item name="android:textSize">@dimen/edittext_textsize_login</item>
<item name="android:textColorPrimary">@color/textColorWhite</item>
<item name="colorAccent">@color/textColorWhite</item>
<item name="colorControlNormal">@color/textColorWhite</item>
<item name="colorControlActivated">@color/textColorWhite</item>
</style>
<android.support.design.widget.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:passwordToggleEnabled="true"
android:theme="@style/EditTextWhite"
>
<android.support.design.widget.TextInputEditText
android:id="@+id/password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/hint_password"
android:inputType="textPassword"
/>
</android.support.design.widget.TextInputLayout>
Toolbar
- Cuando trabajamos con un Toolbar, es necesario que el ViewGroup que le sigue, tenga la siguiente propiedad para que se acomode bajo el Toolbar y no superpuesto:
app:layout_behavior="@string/appbar_scrolling_view_behavior"
- La Toolbar suele ser un elemento que no está en todas las Activity’s de nuestra app, por lo tanto, suele ser más práctico hacer un archivo .xml propio para la Toolbar y luego llamarlo donde sea necesario, con:
<include layout="@layout/actionbar_toolbar"/>
- Recuerda incluir en la Toolbar los namespaces que necesita; y que no es necesario que el actionbar_toolbar.xml inicie con algún ViewGroup. Debe iniciar directamente con:
<android.support.design.widget.AppBarLayout
xxxxxxxxxxxxxxxxxx;
>
- Una vez creada la lista de la Toolbar, necesitamos hacer que se muestre a través del controlador (JAVA) de la Activity sobre la que trabajamos.
- Lo ideal es crear el siguiente método para poder reutilizarlo:
public void showToolbar(String tittle, boolean upButton){
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
getSupportActionBar().setTitle(tittle);
getSupportActionBar().setDefaultDisplayHomeAsUpEnabled(upButton);
}
-
Donde:
- String tittle = Es el título que se mostrará en la Toolbar.
- boolean upButton = Es lo que nos confirma si tenemos, o no, un botón de regreso (Ya que no todas nuestras Activity’s necesitarán un botón de regreso)
- Toolbar toolbar = (Toolbar) findViewByid = Crea un objeto de tipo toolbar y lo relaciona la vista (.xml) del elemento con el id que definimos.
- setSupportActionBar = Hace que el nuevo objeto toolbar tenga soporte en versiones anteriores de Android.
-
Una vez está definido el método, es necesario invocarlo en el método onCreate de la Activity, sin embargo, debemos definir los parámetros que recibirá el método.
- tittle : Para el título creamos una nueva variable string y la forma de llamar este recurso en la invocación del método es:
showToolbar(getResources().getString(R.string.[VARIABLE STRING]),false);
- Donde false responde al parámetro boolean upButton.
Intents
Siempre que queramos unir distintos componentes, debemos usar la clase Intent y crear un objeto Intent.
- Explicitos = Para conectar un componente con otro, dentro de la misma app.
- Implicitos = Conectan componentes desde una app a otra.
Cuando se usa un Intent, la nueva Activity que se inicia siempre pasará por el método onCreate.
Aplicando un Intent
- Para iniciar, es necesario que tengamos algún view que desencadene un evento, como un onClick(FUNCION).En el archivo controlador (JAVA) correspondiente se define ese método (recuerda que debe recibir como parámetro (View view).
- Dentro del método, se crea un objeto intent que recibirá como 1er parámetro el contexto actual, la Activity donde está ubicado; y como 2do parámetro recibirá la Activity objetivo, colocando el sufijo .class.
- Para finalizar invocamos stratActivity(intent) y esto será todo. EJ.
public void goCreateAccount(View view){
Intent intent = new Intent(this, CreateAccountActivity.class);
startActivity(intent);
}
Botón Back vs Botón Up
- Botón Back
- Destruye la Activity y regresa en un orden cronológico inverso al que venimos manejando en la app. No podemos controlarlo por defecto.
- Botón Up
- Regresa a una Activity superior en nivel de Jerarquía.
- Para crear un Botón Up, es necesario establecer las jerarquías en el Android Manifest. En este caso, a la Activity hijo, le indicarémos cual es su actividad padre:
<activity android:name=".view.CreateAccountActivity"
android:parentActivityName=".LoginActivity"
>
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".LoginActivity"
/>
</activity>
- Donde:
android:parentActivityName=".LoginActivity"
- Establece directamente la relación con la Activity padre, pero sólo funciona desde Lollypop en adelante.
- Y
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".LoginActivity"
/>
- Le damos soporte al establecimiento de la relación con una actividad padre, definida en **value**.
- Luego, debemos situarnos en el controlador (JAVA) del Activity en cuestión, y en el método que creamos para generar la Toolbar, ya tiene sentido que colocaramos la recepción del parámetro upButton (que ahora debe ser true donde invocamos el método) y la función getSupportActionBar().setDisplayHomeAsUpEnabled(upButton) que es la que define la existencia del botón.
CardView
- Es un ViewGroup que nos permite mostrar información dentro de “tarjetas”. Se pueden modificar los bordes y añadir sombras si lo deseamos.
- La etiqueta CardView proviene del soporte.
- Dentro de CardView debemos organizar el resto del los Views en Layouts.
- Para poder usar CardView es NECESARIO añadir la librería de soporte correspondiente en Gradle (app).
- CardView necesita de ciertas propiedades obligatorias:
- android:id="@+id/id"
- android:layout_width=“match_parent”
- android:layout_height=""
- Una buena práctica es mantener el CardView en un Layout separado porque suele contener muchos elementos, y para usarlo repetidas veces es mejor simplemente incluir un Layout que copiar y copiar tanto código.
- CardView tiene sus propias propiedades y por eso necesita si propio namespace:
xmlns:cardview="http://schemas.android.com/apk/res-auto"
- Las propiedades podemos verlas escribiendo cardview:. Ej.
cardview:cardCornerRadius=""
Personalizar estado de CheckBox
- Es necesario crear un nuevo Layout Resource en el directorio ** app > res > drawable >**. Este recurso puede hacer referencia directa al checkbox selector sin problemas. Ej: checkbox_selector.xml
- En este nuevo recurso ubicado en drawable aplicarémos la siguiente lógica:
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/heart"
android:state_checked="false"></item>
<item android:drawable="@drawable/heart_full"
android:state_checked="true"></item>
<item android:drawable="@drawable/heart"></item>
</selector>
- Muestra la imagen del corazón vacío (heart), si el estado del selector es false.
- Muestra la imagen del corazón lleno (heart_full), si el estado del selector es true.
- Por defecto, el selector tendrá la imagen del corazón vacío (heart)
- Luego, dentro del CheckBox podemos hacer referencia al selector definido, de la siguiente manera:
android:button="@drawable/checkbox_selector"
BottomNavigationView
- Para poder usar este widget, es necesario que incluyas las librerías de soporte de diseño y compatibilidad:
dependencies {
implementation 'com.android.support:appcompat-v7:X.X.X'
implementation 'com.android.support:design:X.X.X'
// donde X.X.X es la versión que prefieras usar
}
- En primer lugar debemos generar un nuevo Layout para nuestro BottomNavigationView, este View debe tener asociado un menu:
<android.support.design.widget.BottomNavigationView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/bottombar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_gravity="bottom|end"
android:background="@color/colorPrimary"
app:itemBackground="@drawable/bottombar_itembackground"
app:itemIconTint="@color/textColorWhite"
app:itemTextColor="@color/textColorWhite"
app:menu="@menu/bottombar_menu"
/>
- El menú contendrá los botones del BottomNavigationView, cada item deberá tener un id, title e icon.
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/search"
android:title="@string/tab_search"
android:icon="@drawable/ic_search"
/>
<item
android:id="@+id/home"
android:title="@string/tab_home"
android:icon="@drawable/ic_home"
/>
<item
android:id="@+id/profile"
android:icon="@drawable/ic_user"
android:title="@string/tab_profile" />
</menu>
- Si regresas al primer punto, verás app:itemBackground="@drawable/bottombar_itembackground", es decir, que al fondo de cada botón le estamos asignado no sólo un color. Este recurso bottombar_itembackground contiene lo siguiente en este caso:
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_checked="true" android:drawable="@color/colorPrimaryDark"/>
<item android:state_pressed="true" android:drawable="@color/colorPrimaryDark"/>
<item android:drawable="@color/colorPrimary" />
</selector>
-
Por defecto el botón tendrá el colorPrimary.
-
Cuando el botón sea presionado y cuando esté seleccionado, tendrá el colorPrimaryDark.
-
Listo, ya el ButtonNavigationView tiene el estilo que deseamos.
Desde este sitio obtuve la información para hacerlo
RecyclerView
- Está pensado como el sustituto de ListView, sencillamente por su nivel de eficiencia. En lugar de cargar todos los elementos listados, carga sólamente los que puedan ser mostrados en pantalla y va mostrando el resto a medida que se hace slide.
- RecyclerView no sólo muestra sus elementos en forma de lista, sino también en forma de cuadrícula y escalonada.
- Estructura:
- RecyclerView = Compuesto del controlador (java) y el Layout .xml.
- LayoutManager = Que está relacionado directamente al RecyclerView, este es el que define el tipo de Layout que se mostrará:
- Lista (LinearLayoutManager)
- Cuadrícula (GridLayoutManager)
- Escalonada (StaggeredGridManager)
- Adapter se compone de dos clases principales:
- Clase Adapter = Que establecerá la relación entre la lista de elementos y el RecyclerView, será como la configuración.
- Clase ViewHolder = Que gestionará todos los Views de cada elemento particular de la lista que se pasará al RecyclerView.
- Dataset (POJO) = Será el modelo que seguirá nuestra CardView o cualquier elemento que sea nuestra unidad de lista.
- LayoutManager = Que está relacionado directamente al RecyclerView, este es el que define el tipo de Layout que se mostrará:
- RecyclerView = Compuesto del controlador (java) y el Layout .xml.
Implementación del RecyclerView
- Importa la librería de RecyclerView en Graddle (app):
implementation 'com.android.support:recyclerview-v7:28.x.x-x
// donde .x.x-x es la versión
- En el fragment o Activity donde quieras mostrar el RecyclerView, simplemente agregas la etiqueta que ya tienes disponible:
<android.support.v7.widget.RecyclerView
android:id="@+id/pictureRecycler"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
- En nuestro package principal creamos uno nuevo llamado adapter. Dentro de este nuevo paquete vamos a crear una nueva clase PictureAdapterRecyclerView.
- Ubicados dentro de la clase que creamos, definimos el ViewHolder que hereda de RecyclerView.ViewHolder (Te saldrá subrayado y te pedirá crear un constructor).
- Dentro del ViewHolder definimos todos los views variables (username, likes, time, etc) que componen al CardView, como private.
- A cada view definido en el ViewHolder se le relaciona el view que está en el CardView, mediante el id. Esto se hace dentro del constructor.
- Hasta ahora se verá así:
public class PictureAdapterRecyclerView {
public class PictureViewHolder extends RecyclerView.ViewHolder{
private ImageView pictureCard;
private TextView userNameCard;
private TextView timeCard;
//private CheckBox likeCheckCard;
private TextView likeNumberCard;
public PictureViewHolder(@NonNull View itemView) {
super(itemView);
pictureCard = (ImageView) itemView.findViewById(R.id.pictureCard);
userNameCard = (TextView) itemView.findViewById(R.id.userNameCard);
timeCard = (TextView) itemView.findViewById(R.id.timeCard);
likeNumberCard = (TextView) itemView.findViewById(R.id.likeNumberCard);
}
}
}
- Sin embargo, la clase PictureAdapterRecyclerView debe heredar de RecyclerView.Adapter, por lo que quedaría:
public class PictureAdapterRecyclerView extends RecyclerView.Adapter {
...
- Ahora, a la clase Adapter debemos hacerle llegar una colección de objetos, esto lo hacemos a través de nuestro POJO. Así que en nuestro package creamos un nuevo paquete llamado model y en este directorio creamos una nueva clase, en este caso llamada Picture.
- Dentro de este POJO, definimos los elementos que serán enviados al Adapter, sería algo como esto:
public class Picture {
private String picture;
private String userName;
private String time;
private String like_number = "0";
}
- Ahora es necesario generar un constructor, getters y setters, el generador lo puedes mostrar presionando alt + insert y seleccionando el método que quieres incluir o haciendo click derecho > Generate. El resultado será algo como esto:
public class Picture {
private String picture;
private String userName;
private String time;
private String like_number = "0";
public Picture(String picture, String userName, String time, String like_number) {
this.picture = picture;
this.userName = userName;
this.time = time;
this.like_number = like_number;
}
public String getPicture() {
return picture;
}
public void setPicture(String picture) {
this.picture = picture;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getTime() {
return time;
}
public void setTime(String time) {
this.time = time;
}
public String getLike_number() {
return like_number;
}
public void setLike_number(String like_number) {
this.like_number = like_number;
}
}
- Una vez definido nuestro POJO, es necesario regresar al Adapter y agregarle la colección de datos que recibirá. En este caso, recibirá una colección de PictureViewHolder, por eso, nuestra clase PictureAdapterRecyclerView quedará de esta manera (cuando agregas la colección de datos <PictureAdapterRecyclerView.PictureViewHolder> te exigirá la creación de los nuevos métodos que también ves a continuación):
public class PictureAdapterRecyclerView extends RecyclerView.Adapter<PictureAdapterRecyclerView.PictureViewHolder> {
@NonNull
@Override
public PictureViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
return null;
}
@Override
public void onBindViewHolder(@NonNull PictureViewHolder pictureViewHolder, int i) {
}
@Override
public int getItemCount() {
return 0;
}
public class PictureViewHolder extends RecyclerView.ViewHolder{
...
...
...
}
}
- Posterior a esto, aún trabajando en la clase Adapter, crearémos 3 variables:
- private ArrayList<Picture> pictures; = es el arreglo de imagenes que recibiremos, aun no sabemos el origen, pero sabemos que recibirémos un arregle de imagenes.
- private int resources; = el recurso será nuestro Layout, nuestro CardView.
- private Activity activity; = es para definir la actividad desde la que se está llamando esta clase. Será útil cuando querramos ubicar la imagen que traerémos de internet.
- Creamos un constructor para estas tres nuevas variables (recuerda que puedes hacerlo mediante el generador Alt + insert).
private ArrayList<Picture> pictures;
private int resource;
private Activity activity;
public PictureAdapterRecyclerView(ArrayList<Picture> pictures, int resource, Activity activity) {
this.pictures = pictures;
this.resource = resource;
this.activity = activity;
}
- En el método onCreateViewHolder() de PictureViewHolder le pasarémos al constructor el view, es decir, nuestro CardView. Para esto primero debemos inflar (convertir de .xml a un view, algo mostrable en pantalla) el CardView y luego enviarlo. De esta manera queda nuestro método:
@NonNull
@Override
public PictureViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
View view = LayoutInflater.from(viewGroup.getContext()).inflate(resource, viewGroup, false);
return new PictureViewHolder(view);
}
- El método onBingViewHolder() es prácticamente el transmisor de datos. En este caso, asigna cada elemento del arreglo que definimos, al CardView correspondiente. En este método definimos lo siguiente:
- Picture picture = pictures.get(position); = es el que se encarga de recorrer el arreglo y así ubicarse en un CardView por cada elemento del arreglo (viene de nuestro POJO).
- Luego, accedemos a pictureViewHolder, un objeto que recibimos como parámetro en esta clase. Luego accedemos al view al cual le definirémos información, y la información que le asignarémos, la tendrémos en picture (lo que creamos en el paso anterior).
- Quedará así:
@Override
public void onBindViewHolder(@NonNull PictureViewHolder pictureViewHolder, int i) {
Picture picture = pictures.get(i);
pictureViewHolder.userNameCard.setText(picture.getUserName());
pictureViewHolder.timeCard.setText(picture.getTime());
pictureViewHolder.likeNumberCard.setText(picture.getLike_number());
}
- Después, al método getItemCount() le asignamos el tamaño del arreglo, es la cantidad de elementos a crear.
return pictures.size()
Mostrar RecyclerView (y otros) en el fragment Home
- Ubicados en el HomeFragment.java agregamos en primer lugar la Toolbar. para esto creamos el método showToolbar (ya lo definimos antes en el CreateAccountActivity, pero esta vez tendrá ciertas diferencias).
- Aparte de los parámetros String tittle y boolean upButton ahora tendrá también el parámetro View view porque recordemos el que fragment se va a comportar como un view. Quedaría así:
public void showToolbar(String tittle, boolean upButton, View view){
Toolbar toolbar = view.findViewById(R.id.toolbar);
((AppCompatActivity) getActivity()).setSupportActionBar(toolbar);
(((AppCompatActivity) getActivity()).getSupportActionBar()).setTitle(tittle);
((AppCompatActivity) getActivity()).getSupportActionBar().setDisplayHomeAsUpEnabled(upButton);
}
- Luego, en el método onCreateView() el inflater que retorna como respuesta se lo asignarémos a un nuevo objeto View view = inflater.inflate(R.layout.fragment_home, container, false); y el método retornará view.
- Posterior al objeto creado invocamos al método showToolbar(“Home”, false, view) (false porque no tendrá upButton).
- Definimos un objeto **RecyclerView picturesRecycler = (RecyclerView) view.findViewById(R.id.pictureRecycler);*.
- Inicializamos un objeto LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getContext()); definiendo que adquiere el contexto actual.
- Asignamos la orientación con setOrientation(LinearLayoutManager.VERTICAL).
- Luego al objeto picturesRecycler le asignamos el LayputManager configurado con setLayoutManager().
- Creamos un objeto PictureAdapterRecyclerView pictureAdapterRecyclerView = new PictureAdapterRecyclerView(buildPictures(), R.layout.cardview_picture, getActivity()); donde builPictures() es un array que definirémos ahora de forma estática.
- Creamos el arreglo public ArrayList<Picture> buildPictures(){ … donde introducirémos objetos de tipo Picture con los parámetros que especificamos (picture, username, time, likes).
- Finalmente, el código queda de esta manera:
public class HomeFragment extends Fragment {
public HomeFragment() {
// Required empty public constructor
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_home, container, false);
showToolbar("Home", false, view);
RecyclerView picturesRecycler = (RecyclerView) view.findViewById(R.id.pictureRecycler);
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getContext());
linearLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
picturesRecycler.setLayoutManager(linearLayoutManager);
PictureAdapterRecyclerView pictureAdapterRecyclerView =
new PictureAdapterRecyclerView(buildPictures(), R.layout.cardview_picture, getActivity());
picturesRecycler.setAdapter(pictureAdapterRecyclerView);
return view;
}
public ArrayList<Picture> buildPictures(){
ArrayList<Picture> pictures = new ArrayList<>();
pictures.add(new Picture("https://images.pexels.com/photos/466685/pexels-photo-466685.jpeg?auto=compress&cs=tinysrgb&h=350", "Nicoll Sarai", "2 horas", "48"));
pictures.add(new Picture("https://images.pexels.com/photos/466685/pexels-photo-466685.jpeg?auto=compress&cs=tinysrgb&h=350", "Carlos Fernández", "1 día", "4"));
pictures.add(new Picture("https://images.pexels.com/photos/466685/pexels-photo-466685.jpeg?auto=compress&cs=tinysrgb&h=350", "Nicoll Sarai", "2 semanas", "8"));
pictures.add(new Picture("https://images.pexels.com/photos/466685/pexels-photo-466685.jpeg?auto=compress&cs=tinysrgb&h=350", "Fraini Salazar", "20 horas", "21"));
pictures.add(new Picture("https://images.pexels.com/photos/466685/pexels-photo-466685.jpeg?auto=compress&cs=tinysrgb&h=350", "Erick Rengel", "1 día", "12"));
pictures.add(new Picture("https://images.pexels com/photos/466685/pexels-photo-466685.jpeg?auto=compress&cs=tinysrgb&h=350", "Nicoll Sarai", "10 minutos", "123"));
return pictures;
}
public void showToolbar(String tittle, boolean upButton, View view){
Toolbar toolbar = view.findViewById(R.id.toolbar);
((AppCompatActivity) getActivity()).setSupportActionBar(toolbar);
(((AppCompatActivity) getActivity()).getSupportActionBar()).setTitle(tittle);
((AppCompatActivity) getActivity()).getSupportActionBar().setDisplayHomeAsUpEnabled(upButton);
}
}
- Listo, hasta ahora ya podemos ver nuestra RecyclerView aún no estamos importando la imagen desde internet y quedan detalles por arreglar.
Mostrar imágenes desde internet
Para esto utilizarémos la librería Picasso.
- En primer lugar implementarémos la librería de Picasso (que está en su página web) a nuestro archivo build.gradle (app) y sincronizamos.
- En el archivo PictureAdapterRecyclerView.java, en el método onBindViewHolder() agregarémos:
Picasso.get().load(picture.getPicture()).into(pictureViewHolder.pictureCard);
- donde picture.getPicture() es la imagen, y pictureViewHolder.pictureCard es el objeto en el cual se agregará.
- Por último, en el archivo AndroidManifest.xml, antes de la etiqueta <application … debemos dar permiso a nuestra app para que acceda a internet:
<uses-permission android:name="android.permission.INTERNET"/>
CollapsingToolbarLayout y AppBarLayout
AppBarLayout
- Es esencialmente un LinearLayout vertical que nos permite acomodar los elementos que trabajarán con un Toolbar.
- Nos permite manejar características de Material Design en AppBar.
CollapsingToolbarLayout
- Es un Wrapper para Toolbar el cual implemente un efecto de Collapse en toolbar y los elementos hijos.
- Para poder usar este efecto, debe ser hijo directo de AppBarLayout.
**Creando Activity “Detalles de imagen”
- Esta vez dejo el código comentado porque es más de lo que ya hemos visto.
- en activity_picture_detail:
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".view.PictureDetailActivity"
>
<android.support.design.widget.AppBarLayout
android:id="@+id/appBar"
android:layout_width="match_parent"
android:layout_height="@dimen/height_appbarlayout"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
<android.support.design.widget.CollapsingToolbarLayout
android:id="@+id/collapsingToolbar"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_scrollFlags="scroll|exitUntilCollapsed"
app:contentScrim="@color/colorPrimary"
>
<!--
app:layout_scrollFrags : Definimos que los hijos deben poder hacer scroll
app:contentScrim : Es el color que tendra la toolbar una vez colapsa
-->
<ImageView
android:id="@+id/pictureDetailHeader"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:background="@drawable/image"
app:layout_collapseMode="parallax"
/>
<!--
android:scaleType="centerCrop" : La imagen no se estira, se centra y se corta.
app:layout_collapseMode="parallax" : Agrega efecto Parallax mientra colapsa
-->
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="wrap_content"
android:layout_height="?attr/actionBarSize"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
app:layout_collapseMode="pin"
/>
<!--
app:layout_collapseMode="pin" : Hace que este view se mantenga fijo
-->
</android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
<android.support.v4.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
>
<!--
app:layout_behavior="@string/appbar_scrolling_view_behavior" :
Este ViewGroup se muestra luego de la Toolbar, no sobre ella
-->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingTop="@dimen/padding_vertical_text_card"
>
<TextView
android:id="@+id/usernamePictureDetail"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="@dimen/textsize_username_detail"
android:text="@string/username_card"
android:paddingStart="@dimen/padding_horizontal_text_card"
android:paddingEnd="@dimen/padding_horizontal_text_card"
/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingBottom="@dimen/padding_vertical_text_card"
android:paddingStart="@dimen/padding_horizontal_text_card"
android:paddingEnd="@dimen/padding_horizontal_text_card"
>
<TextView
android:id="@+id/likeNumberPictureDetail"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="@dimen/textsize_likenumber_detail"
android:text="10"
/>
<TextView
android:id="@+id/secondWordPictureDetail"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="@dimen/textsize_likenumber_detail"
android:text="@string/secondword_detail"
android:layout_marginStart="@dimen/marginleft_secondword"
/>
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="@dimen/height_divider"
android:background="@android:color/darker_gray"
/>
<TextView
android:id="@+id/tittlePictureDetail"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="@dimen/textsize_likenumber_detail"
android:text="@string/tittle_picturedetail"
android:layout_gravity="center_horizontal"
android:textStyle="bold"
android:paddingTop="@dimen/paddingtop_tittle_picturedetail"
/>
<TextView
android:id="@+id/textContentPictureDetail"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="@dimen/textsize_likenumber_detail"
android:text="@string/lorem"
android:layout_gravity="center_horizontal"
android:paddingTop="@dimen/paddingtop_tittle_picturedetail"
android:paddingStart="@dimen/padding_horizontal_textcontent_picturedetail"
android:paddingEnd="@dimen/padding_horizontal_textcontent_picturedetail"
/>
</LinearLayout>
</android.support.v4.widget.NestedScrollView>
<android.support.design.widget.FloatingActionButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="@dimen/fab_margin"
android:src="@drawable/heart"
app:layout_anchor="@+id/appBar"
app:layout_anchorGravity="bottom|end"
/>
<!--
app:layout_anchor="@+id/appBar" : Hace que este view cuelgue sobre appBar
app:layout_anchorGravity="bottom|end" : Le da una gravedad al view en relacion al anchor
-->
</android.support.design.widget.CoordinatorLayout>
- Declaramos la Toolbar en PictureDetailActivity.java:
public class PictureDetailActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_picture_detail);
showToolbar("", true);
}
// No hace falta el parámetro view porque estamos trabajando en un activity, no en fragment. Lo mismo pasa con el ((AppCompatActivity) getActivity()).
public void showToolbar(String tittle, boolean upButton){
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
getSupportActionBar().setTitle(tittle);
getSupportActionBar().setDisplayHomeAsUpEnabled(upButton);
// Esto lo dejamos por acá porque seguramente lo necesitarémos luego, simplemente un objeto.
CollapsingToolbarLayout collapsingToolbarLayout = (CollapsingToolbarLayout) findViewById(R.id.collapsingToolbar);
}
}
- Por último, establecemos la relación con la Activity padre en el AndroidManifest.
<activity
android:name=".view.PictureDetailActivity"
android:parentActivityName=".view.ContainerActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".view.ContainerActivity" />
</activity>
onClick Listener en RecyclerView
- En el PictureAdapterRecyclerView, dentro del método onBindViewHolder (el que establece la relación de cada elemento CardView), declaramos un Listener, el método onClick y dentro de este método el intent:
pictureViewHolder.pictureCard.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent(activity, PictureDetailActivity.class);
activity.startActivity(intent);
}
});
Vista de perfil (e imagen circular)
- En el fragment_profile.xml definimos una estructura similar a la del activity_picture_detail.xml, tendrémos lo siguiente, para iniciar:
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:context=".view.fragment.ProfileFragment"
>
<android.support.design.widget.AppBarLayout
android:id="@+id/appBarProfile"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/AppTheme.AppBarOverlay"
android:background="@color/colorPrimary"
>
<android.support.design.widget.CollapsingToolbarLayout
android:id="@+id/collapsingToolbarProfile"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/usernameProfile"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/username_card"
android:textColor="@color/textColorWhite"
android:textSize="@dimen/textsize_username_profile"
/>
<android.support.v7.widget.Toolbar
android:id="@+id/toolbarProfile"
android:layout_width="wrap_content"
android:layout_height="?attr/actionBarSize"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
/>
</android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
<de.hdodenhof.circleimageview.CircleImageView
android:id="@+id/circleImageProfile"
android:layout_width="@dimen/dimen_circleimage_profile"
android:layout_height="@dimen/dimen_circleimage_profile"
android:src="@drawable/image"
android:layout_gravity="center|top"
android:layout_marginTop="@dimen/margintop_circleimage_profile"
android:elevation="@dimen/elevation_circleimage_profile"
/>
</android.support.design.widget.CoordinatorLayout>
- Donde el TextView será el nombre de usuario y <de.hdodenhof.circleimageview.CircleImageView … será nuestra imagen de perfil circular. Este View proviene de una librería llamada CircleImageView que tendrémos que agregar a nuestras dependencias (versión actual en el enlace).
Comportamientos dependientes
Aquí está la documentación del plugin que nos permite establecer comportamientos en Views, dependiendo del comportamiento de otro view. Es super sencillo de usar.
https://github.com/zoonooz/simple-view-behavior
Transiciones en Android
- Sólo están disponibles desde Android 5.0 en adelante. Por lo tanto, debemos hacer una validación de la versión actual.
- Hay 3 tipos de transiciones. Cada una tiene clases desde las que puedes crear un objeto o una clase que herede, para hacer tus propios efectos:
- Fade
- Slide
- Explode
- Para hacer un efecto de transición dependiente, debemos identificar el elemento de salida y el de entrada.
- Al elemento de salida le agregamos un:
android:transitionName="@string/transitionname_X"
- Este nombre de transición será una especie de identificador y es una cadena de texto.
- Luego el mismo atributo se lo debemos agregar al elemento de entrada.
- Ya que tenemos el identificador de la transición, debemos hacer que funcione, y para eso es necesario que entendamos donde está el evento que desencadena la transición (normalmente un onClick() ) y en este evento definimos la validación y la transición:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP){
Explode explode = new Explode();
explode.setDuration(1000);
activity.getWindow().setExitTransition(explode);
activity.startActivity(intent, ActivityOptionsCompat.makeSceneTransitionAnimation(activity, view, activity.getString(R.string.transitionname_picture)).toBundle());
} else {
activity.startActivity(intent);
}
- En este caso quisimos hacer una personalización al efecto y por eso creamos un objeto de la clase Explode, modificamos uno de sus valores y luego aplicamos la transición de salida explode. Sin embargo, si quisieramos no hacer cambios en el efecto por defecto, implementaríamos
getWindow().setExitTransition(new Explode);
- Otra cosa importante es que, si el evento que desencadena la transición está definido en el controlador (java) de la Activity de salida, no es necesario hacer referencia a la Activity, sin embargo, si el evento está definido fuera de la Activity como en este caso donde está definido en un Adapter, si es necesario hacer referencia a la Activity de salida antes del método getWindow().
- Posterior a esto lanzarémos la nueva actividad, pero aparte del intent pasarémos un parámetro de opciones.
activity.startActivity(intent, ActivityOptionsCompat.makeSceneTransitionAnimation(activity, view, activity.getString(R.string.transitionname_picture)).toBundle());
-
Donde definimos que se ejecutará una animación de transición y recibirá como parámetros:
- La Activity actual.
- El view actual.
- el identificador de la transición.
-
Luego del makeSceneTransitionAnimation(…) agregamos .toBundle().
-
Nos resta definir la transición de entrada. Nos ubicamos en el elemento de entrada, en onCreate() en caso de que sea el método que queremos controlar y hacemos lo mismo, validamos y ejecutamos la transición.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP){
getWindow().setEnterTransition(new Fade());
}
StatusBar transparente
La StatusBar puede ser transparente sólo a partir de Lollypop (API 21). Para esto, en el archivo styles v-21 copiamos el estilo:
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
y agregamos un nuevo item:
<item name="android:statusBarColor">@android:color/transparent</item>
Curso Definitivo de Android 2016