Gestione degli intenti Android tra le attività utilizzando Kotlin

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.

AndroidlyIntents

Source:
https://www.digitalocean.com/community/tutorials/android-intent-handling-between-activities-using-kotlin