In questo tutorial, parleremo degli Intent di Android e li implementeremo usando Kotlin nella nostra applicazione.
Cosa imparerai?
- Cos’è un Intent?
- Tipi di Intent?
- Utilizzo degli Intent tra attività
- Invio di dati utilizzando gli Intent di Android
- Utilizzo di Parcelable e Serializable per passare gli oggetti
- Creazione di intenti abbreviati
Intent di Android
Come suggerisce il nome, un Intent è qualcosa che viene utilizzato per eseguire un’azione rispetto al flusso dell’applicazione Android. Gli Intent possono essere utilizzati per:
- Avviare una nuova attività e passare alcuni dati.
- Avviare frammenti/comunicare tra frammenti.
- Avviare/terminare un servizio.
- Avviare attività da un ricevitore di trasmissioni.
In questo tutorial, ci concentreremo principalmente sugli Intent per gestire le attività. Una definizione di Intent consiste principalmente in un’istanza dell’attività corrente. Impostiamo il nome del componente che può essere: Il nome della classe completamente qualificato dell’attività da chiamare. Questo tipo di Intent è un Intent esplicito. Un’azione come URL, numero di telefono, posizione. Visualizzerà tutte le applicazioni disponibili di quei tipi. Questo rientra nella categoria degli Intent impliciti. In Kotlin, ecco il modo per creare un’attività.
val intent = Intent(this, OtherActivity::class.java)
startActivity(intent)
startActivity
aggiungerebbe OtherActivity
allo stack delle attività e la avvierebbe. Come fa la nostra Applicazione a capire quale attività è la prima ad essere invocata? Nel file AndroidManifest.xml impostiamo l’intent filter con l’azione android.intent.action.MAIN
e la categoria android.intent.category.LAUNCHER
sulla prima attività da avviare quando si apre la nostra applicazione. finish()
è utilizzato per distruggere un’attività e rimuoverla dallo stack.
Intent Flags
I flags sono come opzioni che possono essere impostate sugli intent per personalizzare il processo di avvio. Se si avvia sempre la stessa attività, verrebbe creato un nuovo esemplare e aggiunto allo stack delle attività. Per evitare ciò, è possibile utilizzare i flags: FLAG_ACTIVITY_SINGLE_TOP
– Se impostato, l’attività non verrà avviata se è già in esecuzione in cima allo stack delle attività.
intent.flags = Intent.FLAG_ACTIVITY_SINGLE_TOP
Allo stesso modo, l’utilizzo di un flag FLAT_ACTIVITY_CLEAR_TOP
non avvierà un’altra istanza dell’attività se ne esiste già una. Questo flag cancellerà tutte le attività sopra l’attività chiamata e la posizionerà in cima allo stack.
Passaggio di dati attraverso gli Intent
Per passare dati alle nuove attività utilizziamo coppie chiave-valore all’interno della funzione putExtra
, putStringArrayListExtra
etc. putExtra passa generalmente i tipi di base come Int, Float, Char, Double, Boolean, String insieme a IntArray… etc.
val intent = Intent(this, OtherActivity::class.java)
intent.putExtra("keyString", "Androidly String data")
Questi campi Extra sono involontariamente avvolti nell’oggetto Bundle
che in definitiva contiene tutti i dati da passare. Per recuperare i dati nell’altra attività, dobbiamo utilizzare la proprietà extras
sui bundles
. Recupero dei dati nella nuova attività
val bundle: Bundle? = intent.extras
val string: String? = intent.getString("keyString")
val myArray: ArrayList<String>? = intent.getStringArrayList("myArray")
intent
, extras
sono equivalenti a getIntent()
, getExtras()
in Java. Abbiamo utilizzato un tipo nullable Bundle?
per prevenire NullPointerExceptions
quando non esistono dati. Allo stesso modo, per i dati che vengono recuperati utilizzando le chiavi, abbiamo utilizzato i tipi nullable per prevenire NPE che possono verificarsi quando la chiave è errata.
Utilizzo di dati Parcelable e Serializable
A volte abbiamo bisogno di passare un oggetto completo da un’attività all’altra. Non è possibile farlo a meno che non si implementi l’interfaccia Parcelable o Serializable. Differenza tra Parcelable e Serializable
- L’interfaccia Parcelable fa parte del SDK Android. Serializable è un’interfaccia standard di Java.
- Nel Parcelable è necessario impostare tutti i dati che devi passare in un oggetto Parcel e sovrascrivere i metodi writeToParcel() ecc. Nella serializable, l’implementazione dell’interfaccia è sufficiente per passare i dati.
- Parcelable è più veloce di Serializable.
Invio dei dati Parcelable
Kotlin offre alcune utili annotazioni per risparmiarci la sovrascrittura del metodo writeToParcel() per impostare i dati sul Parcelable. Invece, possiamo utilizzare l’annotazione @Parcelize come mostrato di seguito:
@Parcelize
data class Student(
val name: String = "Anupam",
val age: Int = 24
) : Parcelable
Nota: Attualmente nel tuo build.gradle devi aggiungere il seguente codice affinché l’annotazione @Parcelize funzioni:
android {
androidExtensions {
experimental = true
}
//..
....
}
Nella tua Activity fai:
val student = Student()
val intent = Intent(this, OtherActivity::class.java)
intent.putExtra("studentData", student)
startActivity(intent)
Invio dei dati Serializable
data class Blog(val name: String = "Androidly", val year: Int = 2018) : Serializable
val blog = Blog("a", 1)
val intent = Intent(this, OtherActivity::class.java)
intent.putExtra("blogData", blog as Serializable)
startActivity(intent)
Utilizziamo la conoscenza sopra riportata nel nostro progetto Android Studio.
Struttura del progetto
Codice Layout
Il codice per il layout activity_main.xml
è fornito di seguito:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
tools:context=".MainActivity">
<Button
android:id="@+id/btnSimpleIntent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="SIMPLE INTENT" />
<Button
android:id="@+id/btnSimpleIntentAndData"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="SIMPLE INTENT WITH DATA" />
<Button
android:id="@+id/btnParcelableIntent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Parcelable Intent" />
<Button
android:id="@+id/btnSerializableIntent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Serializable Intent" />
<Button
android:id="@+id/btnBrowserIntent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Browser Intent" />
<Button
android:id="@+id/btnMapsIntent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Maps Intent" />
<Button
android:id="@+id/btnGenericIntent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Generic Intent" />
</LinearLayout>
Il codice per il layout activity_other.xml
è fornito di seguito:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
tools:context=".MainActivity">
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Intent Data goes here" />
</LinearLayout>
Codice Attività
Il codice per la classe MainActivity.kt è fornito di seguito:
package net.androidly.androidlyintents
import android.app.Activity
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.os.Parcelable
import android.view.View
import android.widget.Toast
import android.widget.Toast.LENGTH_LONG
import kotlinx.android.parcel.Parcelize
import kotlinx.android.synthetic.main.activity_main.*
import java.io.Serializable
@Parcelize
data class Student(
val name: String = "Anupam",
val age: Int = 24
) : Parcelable
data class Blog(val name: String = "Androidly", val year: Int = 2018) : Serializable
class MainActivity : AppCompatActivity(), View.OnClickListener {
fun Context.gotoClass(targetType: Class<*>) =
ComponentName(this, targetType)
fun Context.startActivity(f: Intent.() -> Unit): Unit =
Intent().apply(f).run(this::startActivity)
inline fun <reified T : Activity> Context.start(
noinline createIntent: Intent.() -> Unit = {}
) = startActivity {
component = gotoClass(T::class.java)
createIntent(this)
}
var arrayList = ArrayList<String>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
btnSimpleIntent.setOnClickListener(this)
btnSimpleIntentAndData.setOnClickListener(this)
btnParcelableIntent.setOnClickListener(this)
btnSerializableIntent.setOnClickListener(this)
btnBrowserIntent.setOnClickListener(this)
btnMapsIntent.setOnClickListener(this)
btnGenericIntent.setOnClickListener(this)
arrayList.add("Androidly")
arrayList.add("Android")
arrayList.add("Intents")
}
override fun onClick(v: View?) {
when (v?.id) {
R.id.btnSimpleIntent -> {
val intent = Intent(this, OtherActivity::class.java)
startActivity(intent)
}
R.id.btnSimpleIntentAndData -> {
val intent = Intent(this, OtherActivity::class.java)
with(intent)
{
putExtra("keyString", "Androidly String data")
putStringArrayListExtra("arrayList", arrayList)
putExtra("keyBoolean", true)
putExtra("keyFloat", 1.2f)
}
startActivity(intent)
}
R.id.btnParcelableIntent -> {
val student = Student()
val intent = Intent(this, OtherActivity::class.java)
intent.putExtra("studentData", student)
startActivity(intent)
}
R.id.btnSerializableIntent -> {
val blog = Blog("a", 1)
val intent = Intent(this, OtherActivity::class.java)
intent.putExtra("blogData", blog as Serializable)
startActivity(intent)
}
R.id.btnBrowserIntent -> {
val url = "https://www.androidly.net"
val uri = Uri.parse(url)
val intent = Intent(Intent.ACTION_VIEW, uri)
if (intent.resolveActivity(packageManager) != null) {
startActivity(intent)
} else {
Toast.makeText(applicationContext, "No application found", LENGTH_LONG).show()
}
}
R.id.btnMapsIntent -> {
val loc = "12.9538477,77.3507442"
val addressUri = Uri.parse("geo:0,0?q=" + loc)
val intent = Intent(Intent.ACTION_VIEW, addressUri)
if (intent.resolveActivity(packageManager) != null) {
startActivity(intent)
} else {
Toast.makeText(applicationContext, "No application found", LENGTH_LONG).show()
}
}
else -> start<OtherActivity> {
putExtra("keyString", "Androidly Generic Intent")
}
}
}
}
Nel codice sopra, abbiamo utilizzato Pulsanti per ogni tipo di Intent. Abbiamo utilizzato l’espressione with
di Kotlin per evitare di impostare i dati sull’oggetto intent
ogni volta. Inoltre, abbiamo creato tre diversi intent oltre a quelli già discussi in precedenza. Un intent del browser viene utilizzato per avviare l’URL presente nell’intento nell’app del browser. Utilizza Intent(Intent.ACTION_VIEW, uri)
. Un intent di posizione viene utilizzato per avviare la posizione lat,lng nell’applicazione mappe. Entrambi sono intent impliciti. Infine, abbiamo utilizzato un intent generico in cui utilizziamo le funzioni di estensione di Kotlin e le espressioni lambda per creare una funzione abbreviata per avviare un intent. Per questo usiamo le seguenti funzioni:
fun Context.gotoClass(targetType: Class<*>) =
ComponentName(this, targetType)
fun Context.startActivity(createIntent: Intent.() -> Unit): Unit =
Intent().apply(createIntent).run(this::startActivity)
inline fun <reified T : Activity> Context.start(
noinline createIntent: Intent.() -> Unit = {}
) = startActivity {
component = gotoClass(T::class.java)
createIntent(this)
}
La funzione startActivity è una funzione di estensione che cerca una funzione di ordine superiore come suo parametro. Grazie a questo, ora possiamo avviare gli intent in poche righe come: start<OtherActivity>
Il codice per la classe OtherActivity.kt è riportato di seguito.
package net.androidly.androidlyintents
import android.content.Context
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.widget.Toast
import kotlinx.android.synthetic.main.activity_other.*
class OtherActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_other)
val bundle: Bundle? = intent.extras
bundle?.let {
bundle.apply {
//Intent con dati
val string: String? = getString("keyString")
textView.text = string
val myArray: ArrayList? = getStringArrayList("myArray")
showToast(message = "MyArrayList size:${myArray?.size}")
val arrayList: ArrayList? = getStringArrayList("arrayList")
showToast(message = "ArrayList size:${arrayList?.size}")
val float: Float? = bundle.get("keyFloat") as Float?
var boolean = bundle.get("boolean") as? Boolean
showToast(message = "Float data is:$float")
showToast(message = "Boolean data is:$boolean")
boolean = bundle.get("keyBoolean") as? Boolean
showToast(message = "Boolean correct key data is:$boolean")
}
bundle.apply {
//Dati serializzabili
val blog = getSerializable("blogData") as Blog?
if (blog != null) {
textView.text = "Blog name is ${blog?.name}. Year started: ${blog?.year}"
}
}
bundle.apply {
//Dati Parcelable
val student: Student? = getParcelable("studentData")
if (student != null) {
textView.text = "Name is ${student?.name}. Age: ${student?.age}"
}
}
}
}
private fun showToast(context: Context = applicationContext, message: String, duration: Int = Toast.LENGTH_SHORT) {
if (!message.contains("null"))
Toast.makeText(context, message, duration).show()
}
}
Abbiamo usato let
e apply
per gestire i tipi nullabili e evitare di fare bundle.field in ogni riga. L’output dell’applicazione sopra in azione è riportato di seguito: Questo pone fine a questo tutorial sugli intent di Android in Kotlin. Puoi scaricare il progetto dal link qui sotto.