Introducción a AWS

Crear una aplicación Android

Crear una aplicación Android sencilla con AWS Amplify

Módulo 1: Crear e implementar una aplicación Android

En este módulo, creará una aplicación Android y la implementará en la nube con el servicio de alojamiento web de AWS Amplify.

Introducción

AWS Amplify proporciona un flujo de trabajo basado en Git destinado a crear, administrar, integrar e implementar backends sin servidor para aplicaciones web y móviles. La Command Line Interface (CLI, Interfaz de línea de comandos) de Amplify proporciona una interfaz de usuario simple basada en texto para aprovisionar y administrar servicios de backend, como la autenticación de usuario o una API REST o GraphQL para sus aplicaciones. Las bibliotecas de Amplify permiten integrar fácilmente estos servicios de backend con solo algunas líneas de código en las aplicaciones.

En este módulo, comenzaremos creando una nueva aplicación Android para tomar notas de viaje. Una nota se compone de un título, una descripción y una imagen. Mejoraremos esta aplicación en los siguientes módulos.

Lo que aprenderá

  • Crear una aplicación Android
  • Actualizar la vista principal para mostrar una lista de elementos
  • Crear y probar su aplicación

Conceptos clave

Kotlin: Kotlin fue elegido como el lenguaje de programación para este tutorial porque permite reducir gran parte del código repetitivo y se diseñó con la seguridad de tipos en consideración.

Jetpack: este tutorial utiliza Jetpack de Android, una colección de bibliotecas Android que incorporan las prácticas recomendadas y brindan compatibilidad con versiones anteriores en las aplicaciones Android.

 Tiempo de realización

10 minutos

 Servicios utilizados

Implementación

  • Inicie Android Studio y seleccione Start a new Android Studio project (Iniciar un nuevo proyecto de Android Studio) en la pantalla de presentación.

    En Phone and Tablet (Teléfono y tableta), seleccione Basic Activity (Actividad básica) y haga clic en Next (Siguiente).

    Escriba un nombre para el proyecto, por ejemplo, Android Getting Started. Asegúrese de que el lenguaje sea Kotlin y el SDK mínimo sea API 26: Android 8.0 (oreo). A continuación, haga clic en Finish (Finalizar).

    Ahora que existe el esqueleto del proyecto, debemos seguir cuatro pasos para ejecutar nuestra aplicación básica:

    1. Eliminar clases y archivos no deseados y agregar complementos
    2. Crear clases para contener las estructuras de datos
    3. Crear una clase View para contener la clase Note individual en la lista
    4. Modificar MainActivity para mostrar una lista de objetos Note
  • En res/layout y java/com.example.androidgettingstarted, elimine los cuatro archivos fragment (seleccione los cuatro archivos, haga clic con el botón derecho y elija Delete [Eliminar] en el menú contextual).

    En Gradle Scripts (Scripts de Gradle), abra build.gradle (Module:app) y agregue el complemento de extensión Kotlin.

    plugins {
        id 'com.android.application'
        id 'kotlin-android'
        id 'kotlin-android-extensions' //  <== add this line
    }
  • La clase UserData contiene el estado del usuario: un marcador isSignedIn y una lista de valores de notas.

    Para crear una nueva clase, haga clic con el botón derecho en java/com.example/androidgettingstarted y seleccione New -> Kotlin file/class (Nuevo -> Archivo/clase Kotlin). Escriba UserData como nombre.

    Pegue el siguiente código en el nuevo archivo recién creado (UserData.kt).

    package com.example.androidgettingstarted
    
    import android.graphics.Bitmap
    import android.util.Log
    import androidx.lifecycle.LiveData
    import androidx.lifecycle.MutableLiveData
    
    // a singleton to hold user data (this is a ViewModel pattern, without inheriting from ViewModel)
    object UserData {
    
        private const val TAG = "UserData"
    
        //
        // observable properties
        //
    
        // signed in status
        private val _isSignedIn = MutableLiveData<Boolean>(false)
        var isSignedIn: LiveData<Boolean> = _isSignedIn
    
        fun setSignedIn(newValue : Boolean) {
            // use postvalue() to make the assignation on the main (UI) thread
            _isSignedIn.postValue(newValue)
        }
    
        // the notes
        private val _notes = MutableLiveData<MutableList<Note>>(mutableListOf())
    
        // please check https://stackoverflow.com/questions/47941537/notify-observer-when-item-is-added-to-list-of-livedata
        private fun <T> MutableLiveData<T>.notifyObserver() {
            this.postValue(this.value)
        }
        fun notifyObserver() {
            this._notes.notifyObserver()
        }
    
        fun notes() : LiveData<MutableList<Note>>  = _notes
        fun addNote(n : Note) {
            val notes = _notes.value
            if (notes != null) {
                notes.add(n)
                _notes.notifyObserver()
            } else {
                Log.e(TAG, "addNote : note collection is null !!")
            }
        }
        fun deleteNote(at: Int) : Note?  {
            val note = _notes.value?.removeAt(at)
            _notes.notifyObserver()
            return note
        }
    
        fun resetNotes() {
            this._notes.value?.clear()  //used when signing out
            _notes.notifyObserver()
        }
    
    
        // a note data class
        data class Note(val id: String, val name: String, val description: String, var imageName: String? = null) {
            override fun toString(): String = name
    
            // bitmap image
            var image : Bitmap? = null
    
        }
    }

    ¿Qué acabamos de agregar?

    • La clase UserData es responsable de almacenar los datos del usuario, es decir, un marcador isSignedIn para rastrear el estado de autenticación actual y una lista de objetos de Note.
    • Estas dos propiedades se implementan de acuerdo con el marco de publicación/suscripción de LiveData. Permite que la Graphical User Interface (GUI, Interfaz gráfica del usuario) se suscriba a los cambios y reaccione en consecuencia. Para obtener más información sobre LiveData, puede leer este documento o mirar este breve video tutorial. Para seguir las prácticas recomendadas, mantenga la propiedad MutableLiveData como privada y solo exponga la propiedad LiveData de solo lectura. Se requiere código repetitivo adicional cuando los datos a publicar son una lista para asegurarse de que se notifique a los observadores cuando se modifiquen los componentes individuales de la lista.
    • También agregamos la clase de datos Note solo para contener los datos de las notas individuales. Se utilizan dos propiedades distintas para ImageName e Image. Abordaremos Image en un módulo posterior.
    • Se implementó el patrón de diseño singleton para el objeto UserData, ya que permite la referencia a él desde cualquier lugar de la aplicación solo con UserData.
  • Las celdas individuales en una lista de desplazamiento se denominan RecyclerView, ya que la vista se puede reciclar cuando el usuario se desplaza hacia arriba y hacia abajo y cuando ya no es visible en la pantalla.

    Al igual que para una vista normal, creamos archivos XML de diseño y una clase Kotlin. Una celda individual se ve así:

    Para crear el archivo de diseño, haga clic con el botón derecho en res/layout y seleccione New -> Layout Resource File (Nuevo -> Archivo de recurso de diseño). Escriba content_note como nombre y deje todos los demás valores, ya que editaremos directamente el archivo XML.

    Abra la vista Code (Código) para el archivo recién creado.

    En la vista Code (Código) del nuevo archivo recién creado (content_note.xml), reemplace el código generado con el siguiente código:

    <?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="wrap_content"
        android:orientation="horizontal"
        android:paddingVertical="16dp">
    
        <ImageView
            android:id="@+id/image"
            android:layout_width="100dp"
            android:layout_height="match_parent"
            android:padding="8dp"
            android:scaleType="centerCrop"
            android:src="@drawable/ic_launcher_background" />
    
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:layout_marginLeft="5dp"
            android:orientation="vertical">
    
            <TextView
                android:id="@+id/name"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="Title"
                android:textSize="20sp"
                android:textStyle="bold" />
    
            <TextView
                android:id="@+id/description"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="Title"
                android:textSize="15sp" />
    
        </LinearLayout>
    </LinearLayout>

    Por último, cree la clase Kotlin: haga clic con el botón derecho en java/com.example/androidgettingstarted y seleccione New -> Kotlin file/class (Nuevo -> Archivo/clase Kotlin). Escriba NoteRecyclerViewAdapter como el nombre.

    Pegue el siguiente código en el nuevo archivo que acaba de crear (NoteRecyclerViewAdapter.kt).

    package com.example.androidgettingstarted
    
    import android.view.LayoutInflater
    import android.view.View
    import android.view.ViewGroup
    import android.widget.ImageView
    import android.widget.TextView
    import androidx.recyclerview.widget.RecyclerView
    
    // this is a single cell (row) in the list of Notes
    class NoteRecyclerViewAdapter(
        private val values: MutableList<UserData.Note>?) :
        RecyclerView.Adapter<NoteRecyclerViewAdapter.ViewHolder>() {
    
        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
            val view = LayoutInflater.from(parent.context)
                .inflate(R.layout.content_note, parent, false)
            return ViewHolder(view)
        }
    
        override fun onBindViewHolder(holder: ViewHolder, position: Int) {
    
            val item = values?.get(position)
            holder.nameView.text = item?.name
            holder.descriptionView.text = item?.description
    
            if (item?.image != null) {
                holder.imageView.setImageBitmap(item.image)
            }
        }
    
        override fun getItemCount() = values?.size ?: 0
    
        inner class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
            val imageView: ImageView = view.findViewById(R.id.image)
            val nameView: TextView = view.findViewById(R.id.name)
            val descriptionView: TextView = view.findViewById(R.id.description)
        }
    }

    ¿Qué acabamos de agregar?
    El código anterior está compuesto por los siguientes elementos:

    • Un archivo XML de diseño que describe el diseño del componente en una celda que representa una Note. Muestra la imagen, el título y la descripción de la nota.
    • Una clase Kotlin de respaldo. Recibe un objeto de datos de Note en el momento de la creación y asigna valores individuales a sus vistas correspondientes (imagen, título y descripción).
  • Ahora que tenemos las clases de datos (UserData y Note) y la vista de la nota individual (NoteRecyclerViewAdapter), vamos a crear la lista de notas en la actividad principal.

    Desde la lista de archivos a la izquierda de Android Studio, abra res/layout/content_main.xml y reemplace el código con este contenido:

    <?xml version="1.0" encoding="utf-8"?>
    <FrameLayout 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:id="@+id/frameLayout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        >
    
        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/item_list"
            android:name="com.example.myapplication.ItemListFragment"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_marginTop="60dp"
    
            android:paddingHorizontal="8dp"
            android:paddingVertical="8dp"
            app:layoutManager="LinearLayoutManager"
            tools:context=".MainActivity"
            tools:listitem="@layout/content_note" />
    
    </FrameLayout>

    Desde la lista de archivos a la izquierda de Android Studio, abra java/com.example/androidgettingstarted/MainActivity.kt y reemplace el código con este contenido:

    package com.example.androidgettingstarted
    
    import android.os.Bundle
    import android.util.Log
    import androidx.appcompat.app.AppCompatActivity
    import androidx.lifecycle.Observer
    import androidx.recyclerview.widget.ItemTouchHelper
    import androidx.recyclerview.widget.RecyclerView
    import kotlinx.android.synthetic.main.activity_main.*
    import kotlinx.android.synthetic.main.content_main.*
    
    class MainActivity : AppCompatActivity() {
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)
            setSupportActionBar(toolbar)
    
            // prepare our List view and RecyclerView (cells)
            setupRecyclerView(item_list)
        }
    
        // recycler view is the list of cells
        private fun setupRecyclerView(recyclerView: RecyclerView) {
    
            // update individual cell when the Note data are modified
            UserData.notes().observe(this, Observer<MutableList<UserData.Note>> { notes ->
                Log.d(TAG, "Note observer received ${notes.size} notes")
    
                // let's create a RecyclerViewAdapter that manages the individual cells
                recyclerView.adapter = NoteRecyclerViewAdapter(notes)
            })
        }
    
        companion object {
            private const val TAG = "MainActivity"
        }
    }

    ¿Qué acabamos de agregar?

    • El diseño principal es un RecyclerView que administra la lista de celdas individuales que creamos previamente.
    • La clase de actividad principal observa los cambios en la lista de Note y crea un NoteRecyclerViewAdapter para crear celdas individuales.
  • En Gradle Scripts (Scripts de Gradle), abra build.gradle (Module:app) y verifique que las dependencias generadas sean correctas. Las `versiones de bibliotecas` deben revisarse.

    ```gradle
    dependencies {
        implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
        implementation 'androidx.core:core-ktx:1.3.2'
        implementation 'androidx.appcompat:appcompat:1.2.0'
        implementation 'com.google.android.material:material:1.2.1'
        implementation 'androidx.constraintlayout:constraintlayout:2.0.2'
        implementation 'androidx.navigation:navigation-fragment-ktx:2.3.1'
        implementation 'androidx.navigation:navigation-ui-ktx:2.3.1'
        testImplementation 'junit:junit:4.+'
        androidTestImplementation 'androidx.test.ext:junit:1.1.2'
        androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
    }
  • Ahora cree e inicie la aplicación en el simulador. Haga clic en el ícono Run (Ejecutar) ▶️️ en la barra de herramientas o escriba ^ R.

    Luego de un momento, la aplicación se inicia en el simulador de Android, con una pantalla inicial vacía.

    En esta etapa, no hay datos para representar en el tiempo de ejecución. Nos desharemos del icono de correo en un paso posterior.

Conclusión

Ha creado correctamente una aplicación Android. ¡Está listo para comenzar a crear con Amplify!

¿Este módulo le resultó útil?

Inicializar Amplify