Skip to content

Files

Latest commit

e9cfa58 · Dec 26, 2021

History

History
407 lines (224 loc) · 47 KB

File metadata and controls

407 lines (224 loc) · 47 KB

三、意图及其分类

意图是异步消息,用于使用一个安卓组件激活另一个组件。这些意图用于在发生一些事件时触发安卓操作系统,并且应该采取一些行动。安卓操作系统根据接收到的数据,确定意图的接收者并触发它。

一般有两种意图: 显性隐性。顾名思义,明确的意图触发开发者明确指定的安卓操作系统的特定组件。然而,隐含的意图触发了安卓操作系统任何类别的通用组件。由安卓操作系统来决定需要激活哪个组件。如果有多个通用组件,则要求用户从所有组件的列表中选择一个组件。安卓操作系统中意图的这一特性使应用更具交互性,因为其他应用和开发人员也可以访问它。例如,您正在开发一个图片编辑安卓应用来编辑任何图像,对其应用过滤器,等等。因此,如果应用从安卓系统的任何来源接收图像,如电子邮件附件、图库图像、任何其他图像工具等,与从应用本身加载图像的应用相比,该应用将变得更具交互性和响应性。应用的这种交互,无论是通过电子邮件发送图像还是在编辑应用中接收图像,都是通过隐式意图来实现的。

在本章中,您将了解以下主题:

  • 意图类型
  • 明确的意图
  • 在安卓应用中使用明确的意图
  • 隐含的意图
  • 在安卓应用中使用隐式意图
  • 意图和安卓后期绑定

类型

意图的概念和它们的结构,如前一章所讨论的,是理解本章和后面章节的先决条件。如果你对这些没有一个基本的了解,那就去阅读第二章安卓意图介绍,这样才能继续前进。

这两种意图,即显式和隐式意图,在功能上有很大的不同。让我们先从最简单的意图开始。

明确的意图

最简单的意图类型是明确的意图。当开发人员知道使用哪个组件,并且不想向用户提供自由访问时,显式意图是最好的选择。这里,开发人员在意图声明中明确指定要触发的组件。该组件可以是任何活动、服务或广播接收器。例如,一个安卓应用通常包含一个以上对应功能的活动。为了从一个活动导航到另一个活动,使用了明确的意图。下面的代码片段显示了针对活动 A 中的活动 B 的明确意图的简单声明:

Explicit intents

意图是android.content.Intent类的实例。显式组件被指定为 Java 类标识符,每当开发人员发送意图时,安卓操作系统都会使用它们来激活它们。如前几章所述,意图的对象采用两个参数: 上下文。上下文参数接收源上下文,从中触发激活其他组件的意图。class 参数接受用于指定目标组件的特定类的类对象。在前面的代码片段中,intent对象将ActivityA类作为源组件,ActivityB作为要激活的目标组件。现在,在代码片段中以特定组件为目标声明的这个intent对象可以在任何地方使用。比如可以用来启动ActivityB,以ActivityA为父活动。下一节描述了使用显式意图来触发各种组件,如活动、服务等。

在安卓应用中使用显式意图

在这一节中,我们将讨论显式意图在安卓应用中的各种用途。如前所述,显式意图可用于激活其他组件,如活动、服务和广播接收器。我们将讨论两个明确的意图的例子;第一个从另一个活动开始一个活动,另一个在后台开始一个服务。

通过明确的意图开始一项活动

任何安卓应用至少包含一个或多个活动。因此,在多个活动的情况下,在它们之间导航对开发人员来说变得很重要。在本节中,我们将开发两个活动的示例,其中我们将从另一个活动开始一个活动,并在停止/完成当前打开的活动后返回该活动。下图显示了我们将要开发的示例的简单原型:

Starting an activity through an explicit intent

由上图可知,我们有两个活动:主活动第二活动。这两个活动都有一个导航按钮和一个显示活动名称的标题。现在,让我们开始第一个例子的开发。但是,为了开始这个例子,您需要构建一个安卓项目。您可以使用安卓工作室或 Eclipse(根据您的方便),但是在 Eclipse 的情况下,请确保您已经正确安装了 JDK、ADT 和安卓 SDK 及其兼容性。如果你不知道这些 ide 的区别,可以参考本书的第一章。在前一章中已经解释了在安卓工作室中创建项目。重复这些步骤会给你一个完整的安卓项目,其中包含一些预定义的文件和文件夹。

在创建了一个空的安卓项目之后,我们将实现显式意图的使用。在示例中,我们将编写或修改许多不同类型的文件,如 Java、XML 等。在示例中,每个文件都有自己的用途并执行自己的功能。现在让我们一个一个地浏览这些文件。

MainActivity.java 班

项目主文件为MainActivity.java。以下是将在该文件中实现的代码片段:

The MainActivity.java class

具有多个活动的应用必须有一个主要活动。这个主要活动定义了应用的起点。在我们的示例中,MainActivity.java类是项目的主要活动。为了给活动提供一个布局文件进行可视化表示,我们将在活动的onCreate()方法中调用setContentView(R.layout.activity_main)activity_main.xml文件是包含在项目目录的layout文件夹中的布局文件,代表应用的主屏幕。在设置了活动的视图之后,我们通过从布局文件中获取它们的视图来获取将在活动中使用的所有组件。要获得任何组件的视图,我们将使用findViewById()方法,该方法获取视图的标识,并返回一个View对象,该对象是根据我们的要求铸造的类型。在这个文件中,我们已经在布局文件中获得了 ID 为button1的按钮的View对象,并且我们已经将Button类型投射到它并将其引用到我们的按钮。任何按钮都应该有监听器,以便用户与视图交互并自定义视图的行为。在我们的文件中,我们只为按钮设置View.OnClickListener来获得按钮的点击/轻击。

Android SDK 中有两个OnClickListener类。一个在View班。它用于视图,如按钮、文本字段等。另一个在DialogInterface类中,用于检测对话框中的点击。开发人员在导入类及其包时应该小心。

我们已经使用Button对象的setOnClickListener()方法设置了按钮的OnClickListener对象。我们在方法的参数中引用了一个匿名侦听器,并重写了onClick()方法。在onClick()方法中,我们必须提供当按钮被点击时我们想要显示的行为。

类型

匿名对象是没有开发人员指定的对象名称的对象。并且因为这个原因,开发人员不能在代码中直接访问对象。您也可以通过创建界面对象并通过setOnClickListener()方法传递来设置视图的OnClickListener对象。

我们已经创建了一个意图对象,其中MainActivity作为源上下文,SecondActivity类是要激活的目标组件。这里需要注意的一点是,我们没有传递SecondActivity的对象,而是明确传递了SecondActivity的 Java 类表示。这就是我们声明显式意图对象的方式。现在,通过这个意图对象,我们可以根据我们的需求执行许多不同的任务,但是在我们的例子中,选项是有限的。我们创造了一个明确的意图,包含非常具体的信息;因此,出于这个原因,这种意图只能用于有限的目的。在这个例子中,通过调用startActivity()方法,意图被用来在一个栈顶开始另一个活动。此方法采用具有源上下文和目标活动信息的显式意图对象的参数。

类型

要在类中声明意图而不是运行活动,我们可以使用应用上下文来传入意图构造函数的上下文参数。这可以通过getApplicationContext()方法进入。

所以,这里总结一下文件的功能,文件代表显示按钮的应用的起点。点击按钮后,应用导航到另一个活动。活动之间的这种导航是通过一个明确的意图对象实现的。实现MainActivity文件后,让我们在SecondActivity.java文件中实现我们的第二个活动。

SecondActivity.java 班

SecondActivity.java类是我们明确意图的目的地活动。以下是在这个文件中实现的代码片段:

The SecondActivity.java class

同样,这个类是从Activity类扩展而来的,并且遵循活动生命周期。它还应该覆盖生命周期回调方法。在onCreate()方法中,我们通过调用setContentView()方法来设置活动的视图,这次我们使用R.layout.activity_main2传递了文件的引用。该 XML 文件放置在res下的layout文件夹中。我们再次通过调用findViewById()方法从布局文件中获取按钮的View组件,并将其类型转换为Button。然后我们将按钮的OnClickListener()设置为匿名监听器,并覆盖onClick()方法来定义按钮点击的行为。这一次,我们把Activity.finish()方法称为onClick()方法中的。这个方法只是从后面的堆栈中取出上面的活动。

当我们从MainActivity开始SecondActivity 时,当我们完成SecondActivity时,我们将再次看到MainActivity

类型

安卓设备中的后退按钮只是简单地调用finish()方法进行活动,或者调用dismiss()方法进行对话框。

我们也可以用SecondActivity的上下文和MainActivity.java的目标类创建意图对象。但是,这将在后面的堆栈中创建一个新的MainActivity实例,并将它推到SecondActivity之上。在这种情况下,我们将有两个MainActivity实例和一个SecondActivity实例放在后面堆栈中这些活动的中间。

如果我们将AndroidManifest.xml文件中MainActivity<activity>标签中的android:noHistory属性设置为true,启动MainActivity的新实例将导致已经创建的实例被放置在后堆栈的顶部,从而避免创建新对象。开发人员在创建应用流和导航控件时应该更加小心,因为这种类型的流会在应用中形成循环。这可能会导致一个没完没了的应用问题。

我们应该注意到,两个活动文件MainActivity.javaSecondActivity.java包含几乎相同的代码,除了MainActivity的按钮监听器使用显式意图启动新活动,以及SecondActivity的按钮监听器通过自动按下安卓手机的后退按钮来拉回应用。

到目前为止,我们已经了解了如何使用明确的意图来导航到另一个活动。但是,这里应该记住,这两个活动使用不同的布局文件进行可视化表示,其中包含执行这两个任务的操作按钮。需要注意的是,布局文件在显式意图的导航中没有任何作用。这些文件只是显示活动的视觉内容,使用户交互变得容易。现在,让我们专注于这些布局文件。

activity _ main . XML 文件

activity_main.xml 文件是MainActivity.java的布局文件,用 xml 编写。以下代码显示了布局文件的实现:

The activity_main.xml file

从布局文件中,我们在布局中有LinearLayout<Button>视图。记住这个按钮是在将活动视图设置为OnClickListener后由MainActivity 文件提取的。

在 XML 文件中声明的所有布局和视图的引用在R.java文件中自动生成。在 Java 中,我们可以使用R类,以静态方式访问组件。例如,我们可以使用R.layout.layout_name进行布局。但是,在 XML 中,R可以通过放置@来访问。例如,要访问一种颜色,我们可以使用android:name="@color/my_custom_color"

描述activity_main.xml文件,有<LinearLayout>带有关于heightwidthorientation的某些参数。可以看到,<LinearLayout>的标签里面,声明了通过活动中的findViewById()方法带入 Java 代码的<Button>标签。该标签还带有某些参数,如将出现在布局中的idwidthheighttext

activity _ main 2 . XML 文件

activity_main2.xml 文件布局是SecondActivity类的可视化表示。以下代码显示了该文件:

The activity_main2.xml file

除了按钮的文本值不同之外,该布局与activity_main布局相同。这是在SecondActivity.java类中使用的。同样,按钮通过活动中的findViewById()方法从 XML 引用到 Java,并且OnClickListener被设置为定义点击按钮时要执行的自定义操作。

没有AndroidManifest文件,任何安卓应用都是不完整的。我们已经实现了我们的示例应用的可视化布局,以及它们使用显式意图从一个活动导航到另一个活动的功能。但是,当任何应用有多个活动时,安卓操作系统必须被告知所有这些活动及其属性。为了通知安卓操作系统使用了多少活动类,使用了AndroidManifest文件。让我们看看这个文件是如何通知安卓操作系统活动的。

AnDroidManifest . XML 文件

AndroidManifest.xml文件包含应用的所有设置和首选项。当处理多个活动时,开发人员在声明活动时应该小心。只有一个定义应用起点的活动应该有一个启动器的意图过滤器。所有活动都应该保存在这里。关于活动的信息可以包括活动的标签、活动的主题、方向等等。对于活动来说,AndroidManifest.xml文件就像一个注册中心,应用中的所有组件,比如Activity类、Service类等等,都应该在这里注册。如果任何活动没有在AndroidManifest.xml文件中注册,安卓操作系统将在调用该活动时抛出ActivityNotFoundException异常。

关于我们的明确意图 app,活动MainActivitySecondActivity、都注册在 AndroidManifest.xml文件中。这里需要注意的是MainActivity有一个<intent-filter>的子标签,声明MainActivity 为整个应用的启动或启动器活动。以下是在AndroidManifest.xml文件中实现的代码:

The AndroidManifest.xml file

有了AndroidManifest.xml文件,我们的显式意图示例就完成了。在这个例子中,我们定义了两个活动;其中一项是主要活动。主活动使用显式意图,通过显式声明其名称来启动另一个活动。该示例包含两个布局文件,用于活动和清单文件的可视化表示,以注册应用的所有活动和基本设置。运行项目时,应看到如下图所示的画面过渡:

The AndroidManifest.xml file

在下一节中,我们将了解服务中显式意图的另一种用法。我们将学习如何使用活动中的意图显式启动服务。

通过明确的意图启动服务

与活动不同,服务在后台执行特定的任务和动作。服务没有任何视觉布局或用户界面。必须注意的是,服务运行在 app 的主线程上;所以,当 Android 有内存需求时,它会停止不运行或处于暂停状态的服务。在下一个例子中,我们将使用显式意图启动一个服务,并且我们也将使用相同的显式意图停止它。因此,为了开始这个例子,使用任何安卓 IDE 创建一个空项目,比如带有 ADT 插件或安卓工作室的 Eclipse。在这个项目中,我们将实现使用显式意图来启动/停止服务。由于我们的主要焦点是明确的意图,我们将创建一个非常简单的服务,在活动开始或停止时显示祝酒词。我们已经修改/实现了示例应用中的四个部分。让我们一个接一个地看看这些文件是做什么的。

ServiceExample.java 班

由于我们的示例应用包括一个服务,因此该文件是我们的应用中使用的服务类的表示。下面的代码显示了服务的实现:

public class ServiceExample extends Service {

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        Toast.makeText(this,"Service Created",300);
    }

    @Override
    public void onStart(Intent intent, int startId) {
        super.onStart(intent, startId);
        Toast.makeText(this,"Service start",300);
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {

        Toast.makeText(this,"task perform in service",300);
        return super.onStartCommand(intent, flags, startId);
    }
}

在安卓开发中,我们必须从Service类扩展我们的类,并根据我们的定制覆盖和实现所需的方法来创建任何服务。首先onBind()方法是由开发者来实现的,这是一个强制性的方法。这个方法是Service的一个抽象方法。此方法用于在运行时绑定正在运行的服务。那么,onCreate()onStart()的方法和活动课一样。开发人员在这些方法中进行必要的初始设置。在我们的例子中,我们只是显示祝酒词来通知用户方法调用。onStartCommand()方法是一个非常重要的方法,这是我们做所有后台工作的地方。需要注意的是,服务在主线程中运行。所以,如果你想做繁重的处理任务,你应该在这个方法中创建一个单独的线程并启动它。这就是后台处理是如何以标准方式完成的。

我们也可以在主线程中创建线程,在后台执行我们繁重的处理;那么为什么我们需要服务来创建线程呢?在安卓操作系统中,服务是做后台处理的标准方法。当安卓操作系统内存不足时,它可以停止空闲服务来获取内存。但是它不能停止线程;因此,使用服务是在后台持续执行任务的更好方式。

除了像服务类的其他方法一样显示祝酒词之外,我们没有在onStartCommand()方法中做任何事情。关于服务类没有什么特别要提的。和前面的例子一样,这个示例 app 中最重要的部分是主活动。现在让我们详细看看主要的活动类。

ServiceDemoActivity.java 班

ServiceDemoActivity.java 课程是我们的应用的主要活动,它定义了应用的起点。下面的代码显示了类的实现:

The ServiceDemoActivity.java class

这个类是从Activity类扩展而来的,我们已经覆盖了它的一些方法。在onCreate()方法中,我们通过参考存储在应用的res/layout文件夹中的布局来设置活动的内容视图布局。然后,我们提取布局中的按钮,并为这些按钮设置OnClickListener。在前面的例子中,我们使用了按钮的OnClickListener界面的匿名对象。在本例中,我们通过在setOnClickListener()方法的参数中传递this来提供Activity类作为我们的OnClickListener。参数传入的对象必须实现OnClickListener接口,并覆盖onClick()方法。因此,我们的活动类实现了这个接口以及扩展活动。我们在这个类中也覆盖了onClick()方法。由于这次我们有两个按钮,并且两者只有一个OnClickListener界面,我们必须首先找到哪个按钮被按下,然后采取相应的行动。

一种方法是获取被按下的视图的标识,并将其与资源中的标识进行比较。所以,getId()方法返回视图的 ID 给我们;我们在开关块中传递标识,并将其与按钮的标识进行比较。在这两种情况下,我们都在创建一个明确的意图,传递活动的上下文和我们的服务类名作为要激活的目标组件,就像我们在活动示例中所做的那样。应注意服务是如何通过startService()方法启动和通过stopService()方法停止的。这些方法采取明确的意图,包括关于哪个服务必须被启动或停止的信息。这个类向我们展示了使用显式意图来启动或停止任何活动中的任何服务是多么容易。像往常一样,这个主要活动是使用两个按钮StartStop的,这两个按钮是从安卓项目目录的资源文件夹中放置的布局中提取的。让我们看看这个布局文件包含什么。

activity _ main . XML 文件

activity_main.xml 文件是ServiceDemoActivity 的布局文件,用 xml 编写。下面的代码显示了文件的实现:

The activity_main.xml file

布局中有一个<LinearLayout>元素和两个按钮视图。请记住,这些按钮是由ServiceDemoActivity 文件提取的,以便在设置活动视图后为两个按钮设置OnClickListener

我们可以用 XML 和 Java 创建布局。安卓推荐用 XML 创建所有的布局,因为在安卓中使用 Java 进行处理。如果我们用 Java 创建布局,布局的创建也会被处理;这可能会使该应用更加消耗电池。仅将 Java 用于动态布局,如游戏中使用的布局。

描述activity_main.xml文件时,有一个<LinearLayout>元素,带有关于heightwidthorientation的某些参数。正如你所看到的,在<LinearLayout>标签里面,有被声明的按钮,它们被带入到 Java 的代码中。该标签还附带了布局中将出现的关于heightwidthidtext的某些参数。最后但同样重要的是,安卓清单文件是为应用设置的。像活动一样,开发人员必须在清单文件中注册应用中实现的所有服务。让我们看看这个文件,看看我们是如何在示例应用的清单文件中注册我们的服务的。

AnDroidManifest . XML 文件

要注册服务,我们必须提供标签内的代码。以下代码显示了AndroidManifest.xml文件的完整实现:

The AndroidManifest.xml file

可以注意到,在<activity>标签之后,我们已经放置了属性名为的<service>标签来定义我们正在注册的服务。如果我们没有在AndroidManifest.xml文件中注册我们的服务,我们将得到一个ServiceNotFoundException异常抛出,我们将得到错误日志,如“Unable to start service (service package with name): not found”。

日志猫在安卓工作室的 DDMS 视图中找到。LogCat 记录在连接的设备或仿真器中正在进行的所有活动。任何抛出的Force Close崩溃异常都记录在 LogCat 中,开发者可以从中找到崩溃的原因并解决。

到目前为止,我们一直专注于显式意图,我们创建了两个使用显式意图的简单应用。现在,让我们转到另一种叫做隐含意图的意图。

隐含的意图

与显式意图不同,当开发人员不知道使用哪个组件时,隐式意图是一个很好的选择。隐式意图并不像显式意图那样直接指定要激活的安卓组件,而是只指定需要执行哪些操作。安卓操作系统会选择要触发的组件。如果有多个组件可以触发,安卓操作系统会提供选项给用户选择一个组件。例如,我们想在浏览器中打开一个 web 链接。如果我们使用明确的意图,我们有一个选择是开发我们自己的自定义浏览器,并从我们的应用中明确触发它来查看网页链接。另一种更好的方法是使用隐式意图来打开手机中任何已经安装的浏览器。如果手机中安装了多个浏览器,用户将可以选择其中一个来执行操作,即查看网页链接。隐式意图的这一特性作为一种通用形式来执行安卓操作系统中的任何操作。我们在意图中指定数据和动作,安卓根据这些数据选择合适的组件。下面的代码片段显示了一个隐式意图的简单声明,它提供了一个链接,在这种情况下,安卓可以在最合适的组件或浏览器中浏览该链接:

Implicit intents

像显式意图一样,两个参数在隐式意图的构造函数中传递。你可以在第二章安卓意图介绍中了解更多意图构造器。第一个参数是安卓操作系统要执行的动作;我们在这里将其指定为ACTION_VIEW。这将告诉安卓系统有东西即将被查看。另一个参数是数据,大多以URI格式定义。我们已经使用了 PacktPub 网站作为例子。安卓操作系统会在手机的默认浏览器中打开这个网址。如果找到一个以上的浏览器,用户将获得一个所有可用浏览器的列表,以选择一个浏览器来查看地址。

任何 URI 都可以在这里传递,而不仅仅是一个网址。例如,手机中任何联系人的 URI 或图库中的任何图像也可以传递,安卓操作系统将对 URI 传递的数据采取最合适的动作。

这个意图的一般行为被证明是安卓开发中一个非常重要的特性。开发人员通过制作通用应用节省了大量时间。这对开发者来说是有益的,因为他们只需要发送信息。其余的由安卓操作系统定义,用户可以根据自己的意愿选择执行操作。开发人员不仅可以为用户提供选择其他应用来执行隐含意图的操作的功能,还可以开发自己的自定义应用来添加到选择列表中。例如,我们开发了一个图像编辑应用。我们希望该应用的运行方式是,当用户从任何其他应用中选择图像时,我们的应用会出现在选项列表中,以便用户可以从手机的任何位置轻松导航到我们的应用来编辑图像。这可以通过隐含的意图来实现。但这次不同的是,我们不会发送隐含的意图;相反,我们会收到来自其他应用的隐含意图。我们可以通过在我们的AndroidManifest.xml文件中注册意图过滤器来做到这一点,在这里我们必须定义我们的应用将执行的操作。这一特性使得一个应用与其他应用的交互性更强,不同应用与安卓操作系统的集成对开发者来说变得非常容易。在接下来的部分中,我们将开发两个隐式意图的例子,并看看我们可以用这些例子做什么。

在安卓应用中使用隐式意图

本节将讨论在安卓应用中隐式意图的各种用途。如前所述,与显式意图不同,隐式意图可以以一般形式用于与其他安卓组件的通信。让我们用下面两个例子来看看隐含的意图在起作用:一个使用隐含的意图共享内容,另一个使用隐含的意图从其他安卓应用获取内容。

使用隐含意图共享内容

如今,社交网络让任何应用都病毒化,并将其推广给其他用户。由于社交网络种类繁多,很难将所有共享功能都放入应用中。大多数情况下,开发人员会在他们的应用中添加脸书、推特和 Instagram,但在应用中添加这些 SDK 有时会给开发人员和用户带来麻烦。例如,多个 SDK 在构建文件中增加了一些大小;一个应用由于很多特性而变得复杂。

幸运的是,我们可以通过隐式意图使用几行代码来解决这个问题。让我们看看如何通过创建一个简单的内容共享示例来实现这一点。首先使用任何安卓 IDE 创建一个空项目,例如带有 ADT 插件或安卓工作室的 Eclipse,或者打开任何您想要添加共享功能的现有项目。我们现在将实现在社交网络上共享任何数据时使用隐含意图。

我们实现了一个简单的单线共享应用,要求用户选择共享方法,并在该网络上共享线路。在任何空项目中都有两个被修改的主文件。让我们逐一研究这两个文件。

activity _ main . XML 文件

activity_main.xml 文件是我们简单的行共享应用的视觉布局。下面的代码片段显示了这个文件的实现:

The activity_main.xml file

我们有一个相对的布局,其中我们放置了两个视图:一个带有text作为Share的按钮和一个<EditText>标签,用于从用户那里获取输入行。在前面的例子中,我们使用<LinearLayout>来对齐屏幕上的视图。这一次,我们使用<RelativeLayout>来添加视图。在相对布局中,我们相对于其他视图放置视图。例如,android:layout_alignLeft获取任意视图的 ID,并将该视图放在主视图的左侧。同样,我们也使用了Share按钮中的android:layout_below属性将其放置在文本字段下方。关于文本字段,这是屏幕上的第一个视图;因此,这可以相对于父视图放置。

layout_ android:layout_alignParentLeftandroid:layout_alignParentTop布尔标志将EditText视图放置在父视图的左上角,即屏幕上。使用相对布局对齐视图是安卓操作系统中最推荐的方法。

这是我们的单线共享应用的视觉表现。现在,让我们看看在主活动文件中如何使用隐式意图。

MainActivity.java 班

MainActivity.java 类文件是线路共享应用的主要活动。下面的代码展示了文件的实现:

public class MainActivity extends Activity {

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    Button shareBtn = (Button) findViewById(R.id.button1);
    shareBtn.setOnClickListener(new OnClickListener() {
      @Override
      public void onClick(View arg0) {
        // TODO Auto-generated method stub
        String msg = ((EditText) findViewById(
          R.id.editText1)).getText().toString();

        Intent in = new Intent(Intent.ACTION_SEND);
        in.putExtra(Intent.EXTRA_TEXT, msg);
        in.setType("text/html");
        startActivity(Intent.createChooser(in, "Share via..."));
      }
    });
  }
}

该课程是从Activity课程扩展而来的,目的是使其成为一项活动。像往常一样,我们覆盖onCreate()方法,通过setContentView()方法设置活动的内容视图,并引用一个布局,该布局是我们放在res/layout目录中的activity_main.xml文件。设置布局后,我们通过findViewById()方法从布局中获取按钮引用,并将其View.OnClickListener界面设置为匿名对象。

设置视图后,让我们设置监听器并定义可触摸视图的功能。为此,我们已经覆盖了监听器的onClick()方法,并在这个方法中放置了我们的主要功能代码,因为我们想要在一个按钮点击上共享该行。我们首先从文本字段中获取消息文本,方法是获取其引用,然后获取字段中的文本。我们创建了一个Intent对象,并将ACTION_SEND作为其类别传递给构造器。ACTION_SEND意图将数据传递给安卓操作系统。

请记住,我们没有像在显式意图中那样指定数据的接收者,在显式意图中,我们习惯于显式地指定目标。这个动作的接收者负责询问用户数据应该发送到哪里,这是通过选择器对话框来实现的。

创建意图实例后,我们在意图中添加额外的消息行数据。我们选择了EXTRA_TEXT键来添加我们的数据。比如我们要发短信,就要把数据插入短信正文,要发邮件,就要把数据放入邮件正文。所以,我们选择了一个通用的文本类型,安卓操作系统会检测到一个合适的地方来放置数据。

到目前为止,我们已经设置了类别和数据,但是我们还必须设置数据的类型。安卓操作系统会根据数据类型将应用放入我们的选择器对话框。比如我们已经将类型设置为text/html;这将把所有支持文本或 HTML 格式数据的应用,如电子邮件、脸书、推特等,放在选择器中。如果我们将this设置为image/png,所有的应用,如图像编辑器、图库等,都将被放入选择器列表。此外,我们可以通过在一个斜线后面加上一个星号来定义支持的图像的一般类型。例如,image/*将把所有的图像应用,不仅限于巴布亚新几内亚,放在选择器列表中。

最后,我们通过调用startActivity()方法,以此目的开始活动。您应该注意在startActivity()方法或Intent.chooser()方法中传递意图。如果我们通过startActivity中的意图,我们将获得ActivityNotFoundException例外。我们不知道具体要开始哪个活动才能分享 app 因此,在这种情况下,我们将创建一个选择器列表,用户将决定启动哪个活动。这就是我们如何为用户提供选择他或她喜欢的方法来共享内容的选项。

现在,运行该项目,您将看到一个屏幕,该屏幕将有一个共享按钮和一个文本字段来编写要共享的内容。按下共享按钮,您将看到一个对话框,要求您选择共享方式。该对话框将包括所有用于共享文本的应用,如短信、短信、彩信、电子邮件和脸书。下图显示了应用屏幕:

The MainActivity.java class

在这个例子中,我们使用了隐式意图来与安卓操作系统的其他应用进行通信。在下一个例子中,我们将看到其他应用如何与我们的应用通信。

注册您的应用以获取共享意图

在前面的例子中,我们从我们的应用中触发了其他共享应用。在这个例子中,我们将为共享意图注册我们的应用,以便其他应用开发者和用户可以使用隐式意图与我们的应用进行通信。两个例子都使用了隐含的意图,但是在两种情况下使用的方法是不同的。

在前面的例子中,我们在 Java 文件中使用了隐式意图,并在OnClickListener中触发了点击按钮的意图。在这个例子中,为了注册我们的应用的任何意图,比如在这种情况下的共享意图,我们必须将我们的代码放在AndroidManifest.xml文件的 XML 文件中。只是稍微修改一下,AndroidManifest.xml文件管理我们应用的所有设置。现在让我们学习如何实现该应用。

为了开始这个例子,使用任何安卓 IDE 创建一个空项目,例如带有 ADT 插件或安卓工作室的 Eclipse,或者打开任何您想要接收发送意图的现有项目。在这个项目中,我们将探索使用隐式意图来获取应用中其他应用共享的数据。我们首先将我们的应用注册为文本共享应用。然后,所有在安卓中共享任何text/html类型文件的应用都可以触发我们的应用,如果用户选择它的话。为了执行这个任务,我们修改了三个文件中的代码:布局文件、活动文件和清单文件。现在让我们一个一个地看那些文件。

activity _ main . XML 文件

activity_main.xml 文件是我们共享应用的可视化布局。下面的代码显示了这个文件的实现:

The activity_main.xml file

我们有一个相对布局,其中有一个文本视图组件。我们已经将文本视图的初始文本值设置为“Hello World”;请记住,我们在共享时从其他应用获得的数据将打印在此文本视图内容中。视觉表现之后,总会涉及到 app 的处理和编码逻辑。让我们看看下一个活动文件,它执行主要任务并实现使用隐式意图的逻辑。

MainActivity.java 班

MainActivity.java 类文件是显示从任何其他应用共享的数据的活动。下面的代码展示了这个文件的实现:

The MainActivity.java class

当用户从共享对话框中选择我们的应用时,该活动将被打开。我们还将此活动设置为我们的主要活动。应用中的任何活动都可以注册,以获取共享意图。获取共享数据不需要注册主要活动。我们已经设置了活动的布局,然后,我们获得了意图数据。这是将从任何其他共享应用抛出的数据。我们首先通过调用getIntent()方法得到意图。意图有很多种类型;我们必须确保我们的活动只对我们在AndroidManifest.xml文件中注册的意图类型有效。为了检测这个意图是否是为了共享而发送的,我们必须检查意图操作。因此,我们通过调用Intent.getAction()方法获得了意图动作。我们可以使用Intent.getType()方法获得意图的类型。然后,我们检查类型和动作。

如果意图动作为Intent.ACTION_SEND,类型为text/html,则意味着我们可以在 app 中显示该类型的数据。如果两个条件都是true,我们将文本视图内容设置为我们从意图中获得的数据。我们可以通过Intent.getStringExtra()方法从意图中获取数据。此方法将数据类型作为输入参数或参数。在这个例子中,我们得到了Intent.EXTRA_TEXT数据类型,它代表了意图中的任何文本或消息数据,通常用于电子邮件、脸书帖子或短信中的正文文本。

通过对MainActivity类的理解,我们看到我们只得到了意图,并且进行了检查。但是另一个问题是其他应用如何识别我们的应用,以及他们如何知道我们的应用可以显示text/html数据。如果我们从同一应用的另一个活动中显式打开此活动,将收到相同的意图,并检查相同的条件。但是这一次,没有条件会是true,所以在布局中没有文字会被改变。为了让其他应用可以看到该应用,我们必须注册一个意图过滤器。这在AndroidManifest.xml文件中完成。

AnDroidManifest . XML 文件

为了使我们的应用对于共享内容可见,我们必须在该文件中用意图过滤器注册该应用的接收活动。下面的代码显示了这个文件的实现:

The AndroidManifest.xml file

MainActivity<activity>标签中,我们插入了两个意图过滤器。第一个意图过滤器是使这个活动成为应用的主要活动和启动器。第二个意图过滤器是一段执行应用核心功能的代码。我们插入了一个意图过滤器,动作为android.intent.action.SEND,而mimeTypetext/html。这个告诉安卓操作系统,每当任何带有Send动作的意图被触发,并且它包含带有text/html类型的数据,应用就可以处理这个意图。这是我们的应用在应用的选择器对话框中的显示方式。

现在,运行该项目,您将看到 Hello World 屏幕。关闭应用并运行我们之前的示例应用共享内容。在文本框中输入内容,点击分享按钮。在选择器对话框中,您将在列表中看到我们的应用 GettingSharedData,。选择这个 app 会打开活动,而这次,不是 Hello World ,你会在文本栏看到另一个 app 分享的数据。以下截图显示了该应用的演示:

The AndroidManifest.xml file

到目前为止,我们已经看到了两个隐含意图的例子。在一个例子中,我们与电子邮件、短信、脸书等其他应用共享了一些数据。在另一个例子中,其他应用与我们的应用共享他们的内容,我们接收并显示这些数据。但是,隐含的意图并不仅限于分享内容。有很多选项和选择可以使用隐含的意图来执行,包括打电话、发送文本、显示地图、搜索任何东西、拍照、显示和编辑联系人等。

在下一个例子中,我们将学习如何从图库中选择任何图像并在活动中显示。

通过隐含意图选择图像

在这个项目中,我们将实现使用隐式意图从图库中拾取任何图像。我们将在应用中放置一个显示图像的图像视图。该图片将由用户从图库中选择。现在就实施吧!为了开始这个例子,使用任何安卓 IDE 创建一个空项目,例如带有 ADT 插件或安卓工作室的 Eclipse,或者打开您想要添加图像拾取功能的任何现有项目。

我们会将我们的应用注册为图片分享应用,然后所有在安卓操作系统中分享任何图片的应用都可以触发我们的应用,如果用户选择的话。我们在三个文件中修改了代码:布局文件、活动文件和清单文件。让我们看看这些文件是做什么的。

activity _ main . XML 文件

就像所有的安卓应用一样,activity_main.xml 文件代表主活动的布局文件。下面的代码显示了这个文件的实现:

The activity_main.xml file

我们放置了两个视图组件;点击“视图”按钮打开图库,点击“图像视图”显示图像。与其他应用不同,我们在布局文件中设置了按钮监听器。修改最后一个方法我们通过调用活动类文件中的button.setOnClickListener()方法来设置点击监听器。在这个例子中,我们在<Button>标签中使用了android:onClick属性,并在属性的另一边提供了监听器的名称。我们必须提供一个方法名,应该使用这个布局在活动文件中定义。

类型

安卓操作系统建议您在 XML 布局文件中设置监听器。但是,如果布局被多个活动使用,开发人员应该小心,因为属性值是一个方法名,应该在活动文件中定义。这意味着要么所有使用布局文件的活动都应该定义该方法,要么所有活动都应该在 Java 文件而不是 XML 中设置侦听器。

布局文件中的另一个视图组件是图像视图。此图像视图将显示从图库或其他图像共享应用中选取的图像。我们已经将图像视图的源设置为启动器图标图像作为默认图像。

开发完这个应用的布局,我们来关注一下这个应用的逻辑。MainActivity文件显示了应用如何从其他应用获取图像并显示。

MainActivity.java 班

MainActivity.java 类是主活动 Java 文件,它执行应用中的所有功能。下面的代码片段是这个文件的实现:

The MainActivity.java class

我们从onCreate()方法开始,在这里我们首先将活动的内容视图设置到我们的布局文件中。我们在班上创建了三个私有字段。第一个是REQUEST_CODE常量,它是一个整数值。该常量用作从任何其他安卓应用获取任何数据的请求代码。当我们从图库中挑选图像时,我们需要一个请求代码来识别正确的结果和数据。第二个字段是位图。该位图用于以位图格式存储拾取的图像。活动类的第三个也是最后一个字段是图像视图。这用于在 XML 文件中引用图像视图。

pickImage()方法是在<Button>标签的 XML 布局文件中设置的按钮监听器。该方法应采用View 参数。此参数包含运行时点击的视图。根据我们的应用要求,我们希望通过点击按钮打开图库;因此,要打开画廊,在这个方法中会触发一个隐含的意图。我们使用无参数构造函数创建一个空的意图对象。然后,我们使用image/*.将其类型设置为任何图像格式,之后,我们将其意图动作设置为Intent.ACTION_GET_CONTENT。这告诉安卓操作系统显示所有共享内容的应用。

现在,我们已经告诉安卓操作系统,通过设置类型,我们只需要图像内容;因此,安卓操作系统将只显示那些共享图像的应用,如图库。我们将类别设置为Intent.CATEGORY_OPENABLE。这是用来表示GET_CONTENT意图只想要可以和ContentResolver.openInputStream一起开的 URIs。

最后,我们通过调用startActivityForResult()方法开始活动。请记住,我们在之前的应用中使用了startActivity()方法。

类型

startActivity()startActivityForResult()方法的区别在于startActivityForResult()方法在被停止后返回一些结果给父活动,而startActivity()不返回任何结果。

由于我们需要从图库获取任何图像,图库应用将返回我们将在应用中显示的图像的 URI。为了在我们的活动中得到结果,我们需要覆盖我们类中的 onActivityResult()方法。该方法采用三个参数。第一个是一个整数值的请求代码。该值定义了我们用来启动活动的请求标识。我们在类中为此值使用了一个常量私有字段REQUEST_CODE ;因此,在我们的onActivityResult()方法中,我们将把请求代码与这个常量值进行比较以进行确认。第二个参数RESULT_CODE是一个整数值。这个值告诉我们我们得到的结果是否正确,是否可以使用。第三个也是最后一个参数是意图;这包含我们将在应用中使用的结果数据。

onActivityResult()方法中,我们创建了一个InputStream对象,然后我们比较了我们的请求代码和结果代码,以确认我们是否应该处理意图数据。如果一切顺利,我们通过调用 Intent.getData()方法获得所选图像的 URI,并将其传递给本活动内容解析器的openInputStream()。任何活动的内容解析器都可以通过调用Activity.getContentResolver()方法来检索。获取 URI 的流后,我们通过调用BitmapFactory.decodeStream()方法将其解码为位图,并将输出位图设置为我们的活动位图字段。然后,我们在图像视图中设置位图。在try/catch街区的最后一段,我们关闭了我们的溪流。

现在,运行项目,您将看到如下截图所示的屏幕。用户点击按钮,画廊就会显示出来。然后,用户选择要显示的他最喜欢的照片,应用将它显示在应用屏幕上:

The MainActivity.java class

总结整个隐含意图部分,我们实现了三个例子。在第一个例子中,我们学习了如何与其他应用共享我们的数据。在第二个例子中,我们学习了其他应用如何与我们的应用共享数据。最后,在第三个也是最后一个例子中,我们学习了如何从图库中获取图像并在我们的应用中使用它。在本章的下一节,也是最后一节,我们将讨论 Android 后期绑定。

意图与安卓后期绑定

众所周知,安卓应用最核心的三个组件是活动、服务和广播接收器。这些组件通过消息传递进行通信和触发。这个信息是通过意图完成的。意图消息传递是用于相同或不同应用中组件之间的后期运行时绑定(后期绑定)的工具。在每种情况下,安卓系统都会找到合适的组件,例如要触发的活动、服务或接收器,并在必要时立即触发它们。这些意图之间没有重叠。例如,广播接收机意图只发送给广播接收机,而从不发送给任何活动或服务。另一个例子是,在 startActivity()startActivityForResult()方法中传递的意图永远不会发送到任何组件,如服务或接收器,而只会发送到某个活动。

在本章使用的示例中,隐式意图总是执行开发人员不确定如何执行这些操作以及使用什么应用的操作。这种将动作分配给组件的运行时行为被称为安卓后期运行时绑定,这可以通过隐式意图轻松完成。

总结

在本章中,我们讨论了意图、隐含意图、显式意图和后期绑定的类别。本章还提供了安卓意图的一些重要实现,其中我们与其他应用共享数据,其他应用与我们的应用共享数据,从图库中选择任何图像,通过明确的意图开始活动或服务,等等。

在下一章中,我们将学习移动组件(如相机)如何被意图触发,以及它们如何在我们的应用中使用。