Neste tutorial, estaremos discutindo Intenções do Android e implementando-as usando Kotlin em nossa aplicação.
O que Você Aprenderá?
- O que são Intenções?
- Tipos de Intenções?
- Usando Intenções entre Atividades
- Enviando Dados Usando Intenções do Android
- Usando Parcelable e Serializable para passar objetos
- Criando intenções abreviadas
Intenções do Android
Como o nome diz, Intent é algo usado para realizar alguma ação com relação ao fluxo da aplicação Android. As Intenções podem ser usadas para:
- Iniciar uma nova atividade e passar alguns dados.
- Iniciar Fragmentos/Comunicar entre fragmentos.
- Iniciar/Encerrar serviço.
- Iniciar atividades a partir de um receptor de transmissão
Neste tutorial, estaremos focando principalmente em intenções para lidar com atividades. Uma definição de intenção consiste principalmente em uma instância da atividade atual. Configuramos o nome do componente que pode ser: O nome da classe totalmente qualificado da atividade a ser chamada. Esse tipo de intenção é uma intenção explícita. Uma ação como URL, número de telefone, localização. Ele exibirá todas as aplicações disponíveis desses tipos. Isso se enquadra na categoria de intenção implícita. Em Kotlin, o seguinte é o modo de criar uma atividade.
val intent = Intent(this, OtherActivity::class.java)
startActivity(intent)
startActivity
adicionaria OtherActivity
à pilha de atividades e a iniciaria. Como o nosso aplicativo percebe qual atividade deve ser chamada primeiro? No AndroidManifest.xml, definimos o filtro de intenção com a ação android.intent.action.MAIN
e a categoria android.intent.category.LAUNCHER
na primeira atividade a ser iniciada quando nosso aplicativo é aberto. finish()
é usado para destruir uma atividade e removê-la da pilha.
Flags de Intenção
As flags são como opções que podem ser definidas em intenções para personalizar o processo de inicialização. Se você iniciar a mesma atividade sempre, uma nova instância será criada e adicionada à pilha de atividades. Para evitar isso, você pode usar as flags: FLAG_ACTIVITY_SINGLE_TOP
– Se definido, a atividade não será iniciada se já estiver sendo executada no topo da pilha de atividades.
intent.flags = Intent.FLAG_ACTIVITY_SINGLE_TOP
Da mesma forma, usando a flag FLAG_ACTIVITY_CLEAR_TOP
, outra instância da atividade não será iniciada se já existir. Esta flag limpará todas as atividades acima da atividade chamada e a colocará no topo da pilha.
Passando Dados por Intenções
Para passar dados para as novas atividades, utilizamos pares chave-valor dentro da função putExtra
, putStringArrayListExtra
, etc. O putExtra geralmente passa os tipos básicos, como Int, Float, Char, Double, Boolean, String, junto com IntArray… etc.
val intent = Intent(this, OtherActivity::class.java)
intent.putExtra("keyString", "Androidly String data")
Esses campos Extras são encapsulados no objeto Bundle
, que, por sua vez, contém todos os dados a serem transmitidos. Para recuperar os dados na outra atividade, é necessário usar a propriedade extras
sobre os bundles
. Recuperando Dados na Nova Atividade
val bundle: Bundle? = intent.extras
val string: String? = intent.getString("keyString")
val myArray: ArrayList<String>? = intent.getStringArrayList("myArray")
intent
, extras
são equivalentes a getIntent()
, getExtras()
em Java. Utilizamos um tipo nulo Bundle?
para evitar NullPointerExceptions
quando não há dados. Da mesma forma, para os dados recuperados usando as chaves, usamos tipos nulos para evitar NPE que podem ocorrer quando a chave está incorreta.
Usando Dados Parcelable e Serializable
Às vezes, precisamos passar um objeto completo de uma atividade para outra. Isso só é possível se implementarmos a interface Parcelable ou Serializable. Diferença entre Parcelable e Serializable
- A interface Parcelable faz parte do SDK Android. Serializable é uma interface padrão do Java.
- No Parcelable, é necessário definir todos os dados que você precisa passar em um objeto Parcel e também sobrescrever os métodos writeToParcel(), entre outros. Na implementação de Serializable, apenas implementar a interface é suficiente para passar os dados.
- Parcelable é mais rápido que Serializable.
Envio de Dados Parcelable
O Kotlin oferece algumas anotações úteis para nos poupar de sobrescrever o método writeToParcel() para definir os dados no Parcelable. Em vez disso, podemos usar a anotação @Parcelize como mostrado abaixo:
@Parcelize
data class Student(
val name: String = "Anupam",
val age: Int = 24
) : Parcelable
Observação: Atualmente, no seu build.gradle, você deve adicionar o seguinte código para que a anotação @Parcelize funcione:
android {
androidExtensions {
experimental = true
}
//..
....
}
No seu Activity, faça:
val student = Student()
val intent = Intent(this, OtherActivity::class.java)
intent.putExtra("studentData", student)
startActivity(intent)
Envio de Dados 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)
Vamos usar o conhecimento acima no nosso projeto do Android Studio.
Estrutura do Projeto
Código do Layout
O código para o layout activity_main.xml
é fornecido abaixo:
<?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>
O código para o layout activity_other.xml
é fornecido abaixo:
<?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>
Código da Atividade
O código para a classe MainActivity.kt é fornecido abaixo:
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")
}
}
}
}
No código acima, nós utilizamos Botões para cada tipo de Intent. Utilizamos a expressão with
do Kotlin para evitar configurar dados sobre o objeto intent
toda vez. Além disso, criamos três intents diferentes além dos já discutidos anteriormente. Um intent de navegador é usado para abrir o URL presente no intent no aplicativo do navegador. Ele utiliza Intent(Intent.ACTION_VIEW, uri)
. Um intent de localização é usado para abrir a localização lat,lng no aplicativo de mapas. Ambos são intents implícitos. Por último, utilizamos um intent genérico no qual usamos as funções de extensão do Kotlin e expressões lambda para criar uma função abreviada para lançar um intent. Para isso, utilizamos as seguintes funções:
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)
}
startActivity é uma função de extensão que procura por uma função de ordem superior como seu parâmetro. Graças a isso, agora podemos iniciar intents em apenas algumas linhas, como: start<OtherActivity>
O código para a classe OtherActivity.kt é fornecido abaixo.
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 com dados
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 {
//Dados serializáveis
val blog = getSerializable("blogData") as Blog?
if (blog != null) {
textView.text = "Blog name is ${blog?.name}. Year started: ${blog?.year}"
}
}
bundle.apply {
//Dados parceláveis
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()
}
}
Usamos let
e apply
para lidar com tipos nulos e evitar a chamada de bundle.field em cada linha. A saída da aplicação acima em ação é apresentada abaixo: Isso encerra este tutorial sobre intents do Android em Kotlin. Você pode baixar o projeto a partir do link abaixo.