Nozioni di base su AWS
Creazione di un'applicazione Android
Creazione di un'applicazione Android semplice utilizzando AWS Amplify

crea applicazione android
Modulo 1: Creazione e distribuzione di un'applicazione Android
In questo modulo, creerai un'applicazione Android e la distribuirai nel cloud tramite il servizio di hosting Web di AWS Amplify.
Introduzione
AWS Amplify fornisce un flusso di lavoro basato su Git per creare, gestire, integrare e distribuire back-end serverless per applicazioni Web e per dispositivi mobili. L'interfaccia a riga di comando (CLI) Amplify mette a disposizione una semplice interfaccia utente basata su testo per offrire e gestire servizi di back-end, ad esempio l'autenticazione utente o l'API REST o GraphQL per le tue applicazioni. Le librerie Amplify consentono una facile integrazione di questi servizi di back-end con l'aggiunta di poche righe di codice alle tue applicazioni.
In questo modulo, inizieremo creando una nuova applicazione Android per prendere note di viaggio. Una nota è composta da un titolo, una descrizione e un'immagine. Miglioreremo questa applicazione nei seguenti moduli.
Avrai modo di approfondire i seguenti aspetti
- Creazione di un'applicazione Android
- Aggiornamento della visualizzazione principale per mostrare un elenco di elementi
- Creazione e test dell'applicazione
Concetti chiave
Kotlin - Kotlin è stato scelto come linguaggio di programmazione per questo tutorial perché consente di ridurre di molto il codice boilerplate ed è stato progettato considerando la sicurezza del tipo.
Jetpack - Questo tutorial utilizza Jetpack di Android, un insieme di librerie Android che incorpora le best practice e fornisce compatibilità con le versioni precedenti nelle applicazioni Android.
Tempo richiesto per il completamento
10 minuti
Servizi utilizzati
Implementazione
-
Creazione di un progetto Android
Avvia Android Studio e seleziona Start a new Android Studio project (Inizia un nuovo progetto Android Studio) dalla splash screen:
In Phone and Tablet (Telefono e tablet), seleziona Basic Activity (Attività di base) e fai clic su Next (Avanti):
Digita un nome per il progetto, ad esempio Android Getting Started (Nozioni di base su Android). Assicurati che il linguaggio sia Kotlin e che l'SDK minimo sia API 26: Android 8.0 (oreo), quindi fai clic su Finish (Fine):
Una volta creata la bozza del progetto, dobbiamo eseguire quattro passaggi per fare in modo che la nostra applicazione di base funzioni:
- Eliminazione di classi e file indesiderati e aggiunta di plug-in
- Creazione di classi per contenere le nostre strutture di dati
- Creazione di una visualizzazione per contenere una singola nota dell'elenco
- Modifica della MainActivity per visualizzare un elenco di note
-
Eliminazione di file non necessari e aggiunta di plug-in
In res/layout e java/com.example.androidgettingstarted, elimina i quattro file fragment (seleziona i 4 file, fai clic con il tasto destro del mouse e scegli Delete (Elimina) dal menu contestuale):
In Gradle Scripts (Script Gradle), apri build.gradle (Module:app) e aggiungi il plug-in Kotlin Extension.
plugins { id 'com.android.application' id 'kotlin-android' id 'kotlin-android-extensions' // <== add this line }
-
Creazione di una classe di dati UserData
La classe UserData contiene lo stato dell'utente, un flag isSignedIn e un elenco di valori Note.
Per creare una nuova classe, fai clic con il tasto destro del mouse su java/com.example/androidgettingstarted e seleziona New (Nuovo) -> Kotlin file/class (File/classe Kotlin). Digita UserData come nome.
Incolla il codice in basso nel nuovo file appena creato (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 } }
Che cosa abbiamo aggiunto?
- La classe UserData deve contenere i dati dell'utente, ovvero un flag isSignedIn per tenere traccia dello stato di autenticazione corrente e un elenco di oggetti Note.
- Queste due proprietà sono implementate in base al framework di pubblicazione/sottoscrizione di LiveData. Permette all'interfaccia utente grafica (GUI) di sottoscrivere le modifiche e di reagire di conseguenza. Per ulteriori informazioni su LiveData, puoi leggere questa documentazione o seguire questo breve tutorial video. Per seguire la best practice, mantieni privata la proprietà MutableLiveData e mostra esclusivamente la proprietà LiveData di sola lettura. Quando i dati da pubblicare corrispondono a un elenco, è necessario un codice boilerplate aggiuntivo per garantire che gli osservatori siano avvisati quando i singoli componenti dell'elenco vengono modificati.
- Abbiamo anche aggiunto una classe di dati note, solo per contenere i dati delle singole note. Due proprietà diverse vengono utilizzate per ImageName e Image. Image verrà esaminata in un modulo successivo.
- È stato implementato il singolo modello di progettazione per l'oggetto UserData, in quanto può essere utilizzato come riferimento da qualsiasi punto dell'applicazione solo con UserData.
-
Aggiunta della GUI per le singole celle nell'elenco
Le singole celle di un elenco a scorrimento sono chiamate RecyclerView, in quanto la visualizzazione può essere riciclata quando l'utente scorre lo schermo verso l'alto e verso il basso e la visualizzazione non è più visibile.
Proprio come una normale visualizzazione, creiamo file XML di layout e una classe Kotlin. Una singola cella avrà questo aspetto:
Per creare il file di layout, fai clic con il tasto destro del mouse su res/layout e seleziona New (Nuovo) -> Layout Resource File (File risorse di layout). Digita content_note come nome e lascia tutti gli altri valori, in quanto modificheremo direttamente il file XML.
Apri la visualizzazione Codice per il file appena creato.
Nella visualizzazione Codice del nuovo file appena creato (content_note.xml), sostituisci il codice generato incollando il codice in basso:
<?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>
Infine, crea la classe Kotlin: fai clic con il tasto destro del mouse su java/com.example/androidgettingstarted e seleziona New (Nuovo) -> Kotlin file/class (File/classe Kotlin). Digita NoteRecyclerViewAdapter come nome.
Incolla il codice in basso nel nuovo file appena creato (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) } }
Che cosa abbiamo aggiunto?
Il codice sopra è composto da- un file xml di layout che descrive il layout del componente in una cella che rappresenta una nota. Visualizza l'immagine, il titolo e la descrizione della nota.
- una classe Kotlin di supporto. Riceve un oggetto dati Note al momento della creazione e assegna singoli valori alle visualizzazioni corrispondenti (immagine, titolo e descrizione)
-
Aggiornamento dell'attività principale
Ora che abbiamo le classi di dati (UserData e Note) e la visualizzazione delle singole note (NoteRecyclerViewAdapter), creiamo l'elenco di note nell'attività principale.
Dall'elenco di file a sinistra di Android Studio, apri res/layout/content_main.xml e sostituisci il codice con questo contenuto:
<?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>
Dall'elenco di file a sinistra di Android Studio, apri java/com.example/androidgettingstarted/MainActivity.kt e sostituisci il codice con questo contenuto:
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" } }
Che cosa abbiamo aggiunto?
- il layout principale è una RecyclerView che gestisce l'elenco delle singole celle che abbiamo creato in precedenza
- la classe di attività principale osserva le modifiche nell'elenco di note e genera un NoteRecyclerViewAdapter per creare singole celle.
-
Verifica delle dipendenze delle build
In Gradle Scripts (Script Gradle), apri build.gradle (Module:app) e verifica che le dipendenze generate siano corrette. È necessario controllare le 'versioni delle librerie'.
```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' }
-
Creazione e test
Ora crea e avvia l'applicazione nel simulatore. Fai clic sull'icona Run (Esegui) ▶️ nella barra degli strumenti o digita ^ R.
Dopo qualche istante, l'applicazione parte nel simulatore Android con una schermata inizialmente vuota.
In questa fase, non ci sono dati di cui eseguire il rendering al runtime. Elimineremo l'icona della posta in una fase successiva.