使用Kotlin在活动之间进行Android意向处理

时间:2020-02-23 14:29:00  来源:igfitidea点击:

在本教程中,我们将讨论Android Intent,并在应用程序中使用Kotlin实施它们。

Android意图

顾名思义,Intent是用于针对android应用程序流程执行某些操作的东西。
意图可用于:

  • 开始一个新的活动并传递一些数据。

  • 开始片段/片段之间的通信。

  • 启动/结束服务。

  • 从广播接收器启动活动

在本教程中,我们将主要关注处理活动的意图。

一个意图定义主要由当前活动的一个实例组成。
我们设置的组件名称可以是:要调用的活动的完全限定的类名称。
这种类型的意图是显式意图。
URL,电话号码,位置之类的操作。
它将显示这些类型的所有可用应用程序。
这属于隐式意图类别。

在Kotlin中,以下是创建活动的方法。

val intent = Intent(this, OtherActivity::class.java)
startActivity(intent)

startActivity将在活动堆栈上添加OtherActivity并启动它。
我们的应用程序如何实现首先调用哪个活动?在AndroidManifest.xml中,我们在应用程序打开时要启动的第一个活动上,通过操作android.intent.action.MAIN和类别android.intent.category.LAUNCHER设置了意图过滤器。

finish()用于破坏活动并将其从堆栈中删除。

意图标志

标志就像可以在意图上设置以自定义启动过程的选项。
如果您每次都启动相同的活动,则会创建一个新实例并将其添加到活动堆栈中。
为防止这种情况,您可以使用以下标志:FLAG_ACTIVITY_SINGLE_TOP–如果已设置,则该活动如果已经在运行,则不会启动活动堆栈的顶部。

intent.flags = Intent.FLAG_ACTIVITY_SINGLE_TOP

类似地,使用标记" FLAT_ACTIVITY_CLEAR_TOP"不会启动活动的另一个实例(如果已经存在)。
该标志将清除所调用活动上方的所有活动,并将其设置在堆栈顶部。

通过意图传递数据

为了将数据传递到新的活动中,我们在函数putExtra,putStringArrayListExtra等中使用键值对。
putExtra通常将基本类型(例如Int,Float,Char,Double,Boolean,String和IntArray…)传递给IntArray。
等等

val intent = Intent(this, OtherActivity::class.java)
intent.putExtra("keyString", "Androidly String data")

这些Extras字段位于" Bundle"对象的内部,该对象最终保存了所有要传递的数据。

要检索其他活动中的数据,我们需要在bundles上使用extras属性。

在新活动中检索数据

val bundle: Bundle? = intent.extras
val string: String? = intent.getString("keyString")
val myArray: ArrayList<String>? = intent.getStringArrayList("myArray")

intent,extras等同于Java中的getIntent(),getExtras()。
当没有数据时,我们使用了可为空的类型"捆绑包"来防止" NullPointerExceptions"。
同样,对于使用密钥获取的数据,我们使用了可为空的类型,以防止密钥不正确时发生NPE。

使用可打包和可序列化的数据

有时我们需要将一个完整的对象从一个活动传递到另一个活动。
除非我们实现Parcelable或者Serializable接口,否则不可能这样做。

可打包和可序列化之间的区别

  • 可打包界面是Android SDK的一部分。
    可序列化是Java的标准接口。

  • 在Parcelable中,您需要设置需要在Parcel对象中传递的所有数据,并覆盖writeToParcel()方法等。
    在可序列化的实现中,接口足以传递数据。

  • 可打包比可序列化更快。

发送软件包数据

Kotlin提供了一些方便的注释,使我们免于重写writeToParcel()方法来设置Parcelable上的数据。
相反,我们可以使用@Parcelize注释,如下所示:

@Parcelize
data class Student(
      val name: String = "Anupam",
      val age: Int = 24
) : Parcelable

注意:当前,在build.gradle中,您必须添加以下代码以使@Parcelize批注起作用:

android {
  androidExtensions {
      experimental = true
  }
//..
....
}

在您的活动中,您可以执行以下操作:

val student = Student()
val intent = Intent(this, OtherActivity::class.java)
intent.putExtra("studentData", student)
startActivity(intent)

发送可序列化的数据

data class Blog(val name: String = "Androidly", val year: Int = 2016) : Serializable

val blog = Blog("a", 1)
val intent = Intent(this, OtherActivity::class.java)
intent.putExtra("blogData", blog as Serializable)
startActivity(intent)

让我们在Android Studio项目中运用以上知识。

布局代码

下面给出了" activity_main.xml"布局的代码:

<?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>

下面给出了activity_other.xml布局的代码:

<?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>

活动代码

MainActivity.kt类的代码如下:

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 = 2016) : 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")
          }
      }
  }

}

在上面的代码中,我们为每种类型的Intent使用了Buttons。
我们使用Kotlin的with表达式来防止每次都在intent对象上设置数据。
此外,除了上面已经讨论过的目的之外,我们还创建了三种不同的目的。
浏览器意图用于启动浏览器应用程序中意图中存在的URL。
它使用Intent(Intent.ACTION_VIEW,uri)。
位置意图用于在地图应用程序中启动纬度,经度位置。
两者都是隐含的意图。
最后,我们使用中,我们使用科特林的扩展功能和lambda表达式创建速记功能,推出意图的一般意图。
为此,我们使用以下功能:

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是一个扩展函数,它会寻找一个更高阶的函数作为其参数。
因此,我们现在可以在如下几行中启动intent:start &lt;OtherActivity>

下面给出了OtherActivity.kt类的代码。

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 with data
              val string: String? = getString("keyString")
              textView.text = string

              val myArray: ArrayList<String>? = getStringArrayList("myArray")
              showToast(message = "MyArrayList size:${myArray?.size}")

              val arrayList: ArrayList<String>? = 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 {
              //Serializable Data
              val blog = getSerializable("blogData") as Blog?
              if (blog != null) {
                  textView.text = "Blog name is ${blog?.name}. Year started: ${blog?.year}"

              }
          }

          bundle.apply {
              //Parcelable Data
              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()
  }
}

我们已经使用letapply处理可为null的类型,并防止在每一行中执行bundle.field。