Xamarin.Android开发
使用 Visual Studio 生成第一个 Xamarin.Android 应用程序,并进一步了解使用 Xamarin 进行 Android 应用程序开发的基础知识。在此过程中,会介绍生成和部署 Xamarin.Android 应用程序所需的工具、概念和步骤。
第 1 部分:快速入门
在本指南的第一部分,用户将创建一个应用程序,该应用程序可将用户输入的字母数字电话号码转换为数字电话号码,然后呼叫该号码。
第 2 部分:深入了解
在本文档的第二部分,将回顾生成的应用程序,并从根本上了解 Android 应用程序的工作原理。
查看项目中的项,查看每个文件夹及其用途:
-
Properties – 包含 AndroidManifest.xml 文件,该文件描述了对 Xamarin.Android 应用程序的所有要求(包括名称、版本号和权限),此外还包括.NET 程序集元数据文件 AssemblyInfo.cs,可以在此文件中填写一些应用程序相关的基本信息。
-
引用 – 包含生成和运行应用程序所需的程序集。如果展开“引用”目录,可查看对 .NET 程序集(如 System、System.Core 和 System.Xml)的引用以及对 Xamarin 的 Mono.Android 程序集的引用。
-
Assets – 包含应用程序需要运行的文件(包括字体、本地数据文件和文本文件)。此处包括的文件可通过生成的
Assets
类访问。有关 Android 资产的详细信息,请参阅 Xamarin 使用 Android 资产指南。 -
Resources – 包含应用程序资源,例如字符串、图像和布局。可以通过生成的
Resource
类访问代码中的这些资源。 Android 资源指南提供有关“资源” 目录的更多详细信息。 应用程序模板在 AboutResources.txt 文件中还包含有“资源”的简明指南。
1、资源
“资源”目录包含 4 类文件夹(drawable、layout、mipmap 和 values),还有一个名为 Resource.designer.cs 的文件 。
-
drawable – 可绘制目录包含可绘制资源,如图像和位图。
-
mipmap – mipmap 目录包含适用于不同启动器图标密度的可绘制文件。 在默认模板中,drawable 目录包含应用程序图标文件“Icon.png” 。
- layout – 布局目录包含 Android 设计器文件 (.axml),该文件定义每个屏幕或活动的用户界面。 该模板创建名为 activity_main.axml 的默认布局 。
-
values – 此目录包含存储简单值(如字符串、整数和颜色)的 XML 文件。 该模板创建名为 Strings.xml 的文件,用于存储字符串值。
代码中使用:
string info = this.Resources.GetString(Resource.String.button_bundle_info);
-
Resource.designer.cs – 也称为
Resource
类,此文件是一个分部类,存放分配给每个资源的唯一 ID。它由 Xamarin.Android 工具自动创建,并在必要时重新生成。不应手动编辑此文件,因为 Xamarin.Android 将覆盖对其进行的任何手动更改。
更多查看:Android资源
2、应用基础知识和体系结构基础知识
Android应用程序不具有单一入口点;也就是说,应用程序中没有操作系统可调用来启动该应用程序的任何代码行。相反,当 Android 实例化应用程序的一个类时,会启动该应用程序,在此期间Android 将整个应用程序的进程加载到内存中。
设计复杂应用程序或与 Android 操作系统交互时,Android 的这一特有功能极其有用。 但是,这些选项也使 Android 在处理 Phoneword 应用程序等基本方案时变得复杂。 出于此原因,分两种情况来探索 Android 体系结构。本指南剖析使用 Android 应用最常见入口点(第一个屏幕)的应用程序。在了解 Android 多屏幕中,讨论了以不同方式启动应用程序,全面探讨了 Android 体系结构的复杂性。
在仿真器或设备中首次打开 Phoneword 应用程序时,操作系统会创建第一个Activity,Activity是特殊的 Android 类,对应于单个应用程序屏幕,负责绘制和支持用户界面。 Android 创建应用程序的第一个Activity时,会加载整个应用程序:
由于 Android 应用程序中没有线性发展(可以通过多个点启动应用程序),Android 采用一种独特方式来跟踪哪些类和文件组成应用程序。 在 Phoneword 示例中,将向名为“Android Manifest” 的特殊 XML文件注册组成应用程序的所有部分。 “Android Manifest” 的作用是跟踪应用程序的内容、属性和权限,并将这些信息告知 Android 操作系统。 可以将 Phoneword 应用程序当作单一活动(屏幕)和由 Android 清单文件捆绑在一起的资源文件和帮助程序文件的集合,如以下关系图所示:
以下几个部分将探索 Phoneword 应用程序各部分的关系;使你能更好地理解上面的关系图。 此探索先从用户界面开始,会讨论 Android 设计器和布局文件。
3、用户界面
activity_main.axml 是应用程序中第一个屏幕的用户界面布局文件 。 .axml 指示这是 Android 设计器文件(AXML 表示 Android XML,名称 Main 对 Android 而言是任意的 – 可将布局文件命名为其他名称。在IDE中打开activity_main.axml 时,会显示名为“Android Designer”的 Android 布局文件的可视编辑器 :
TranslateButton 的 ID 设置为 @+id/TranslateButton
:
设置 TranslateButton 的 id
属性时,Android Designer 会将 TranslateButton 控件映射到 Resource
类,并为其分配 TranslateButton
的资源 ID 。 通过将可视控件映射到类,可以找到并使用 TranslateButton 和应用代码中的其他控件。 当你剖析为控件提供支持的代码时,会更详细地了解这一内容。 此时,只需知道控件的代码表示形式是通过 id
属性链接到设计器中控件的可视表示形式即可。
源视图
在设计界面上定义的所有内容都会转换成 XML,以供 Xamarin.Android 使用。 Android 设计器提供源视图,此源视图包含从可视化设计器生成的 XML,可以切换到设计器视图左下角的“源” 面板以查看此 XML
4、Activities和活动生命周期
Activity
类包含为用户界面提供支持的代码,activity负责响应用户交互,并创建动态用户体验。
- Activity 类
Phoneword 应用程序只有一个屏幕(活动)。为屏幕提供支持的类称为MainActivity
,位于MainActivity.cs 文件中,名称 MainActivity
在 Android 中没有特别的意义 – 虽然约定是命名应用程序 MainActivity
中的第一个活动,但 Android 并不在意将其命名为其他名称。
打开 MainActivity.cs 时,可以看到,MainActivity
类是 Activity
类的子类 并且活动标有 Activity特性:向 Android 清单注册Activity,这能让 Android 知道此类是该清单所管理的Phoneword 应用程序的一部分。
特性中:Label
属性设置将显示在屏幕顶部的文本【在values文件夹下的string.xml中管理】;MainLauncher
属性告知 Android 在启动应用程序时显示此活动。 如了解 Android 多屏幕指南中所述,当你向应用程序添加更多活动(屏幕)时,此属性会变得很重要。
- 活动生命周期
在 Android 中,活动会根据与用户的交互经历生命周期的不同阶段。可以对活动进行创建、启动和暂停、恢复和销毁等操作。Activity
类包含这些方法,系统会在屏幕生命周期的特定时间点调用这些方法。
通过重写 Activity
生命周期方法,可以控制活动的加载方式和与用户的互动方式,甚至还可以控制活动从设备屏幕消失后会发生的情况。 例如,可以重写生命周期方法,以执行以下重要任务:
-
OnCreate – 创建视图、初始化变量,并执行在用户能看到活动之前其他必须完成的准备工作。只有将活动加载到内存时,才会调用此方法一次。【向用户显示屏幕之前】
-
OnResume – 执行每当活动返回到设备屏幕时必须发生的任何任务。【从主屏幕再次回到app也会调用此方法】
-
OnPause – 执行每当活动离开设备屏幕时必须发生的任何任务。【单击home键,离开app时】
在“Activity
”中将自定义代码添加到生命周期方法时,您将覆盖该生命周期方法的基本实现。 您可以利用现有的生命周期方法(已附加一些代码),然后使用自己的代码扩展该方法。 您可以从方法内部调用基本实现,以确保原始代码(base.OnCreate())在新代码之前运行。 下一部分将说明一个示例。
活动生命周期是Android的重要组成部分。 完成入门系列后,如果您想了解有关活动的更多信息,请阅读活动生命周期指南。
OnCreate
protected override void OnCreate (Bundle bundle) { base.OnCreate (bundle); // Set our view from the "main" layout resource SetContentView (Resource.Layout.activity_main); // Additional setup code will go here }
Bundle:从字符串值到各种可打包类型的映射
OnCreate中加载在 Android Designer中创建的用户界面:请调用 SetContentView
并向其传递布局文件的资源布局名称:activity_main.axml ,布局位于 Resource.Layout.activity_main
当 MainActivity
开始运行后,会基于 activity_main.axml 文件的内容创建一个视图 。
准备好布局文件后,可以开始查找控件,若要查找控件,请调用 FindViewById
,并传入控件的资源 ID。
// Get our UI controls from the loaded layout EditText phoneNumberText = FindViewById<EditText>(Resource.Id.PhoneNumberText); TextView translatedPhoneWord = FindViewById<TextView>(Resource.Id.TranslatedPhoneword); Button translateButton = FindViewById<Button>(Resource.Id.TranslateButton);
现在布局文件中已具有对控件的引用,可以开始对其进行编程,以响应用户交互。
响应用户交互
在 Android 中, Click
事件侦听用户的触控,在此应用中,Click
事件将由 lambda 处理,不过也可改用委托或命名事件处理程序。
translateButton.Click += (sender, e) => { // Translate user's alphanumeric phone number to numeric string translatedNumber = PhoneTranslator.ToNumber(phoneNumberText.Text); if (string.IsNullOrWhiteSpace(translatedNumber)) { translatedPhoneWord.Text = string.Empty; } else { translatedPhoneWord.Text = translatedNumber; } };
5、为不同的屏幕密度设置图标
Android 设备具有不同的屏幕大小和分辨率,不是所有图像都能清晰显示在屏幕上。
考虑到这一点,最好将不同分辨率的图标添加到 Resources文件夹。 Android 提供了不同版本的 mipmap 文件夹来处理不同密度的启动器图标,包括:
针对中等密度屏幕的 mdpi、针对高密度屏幕的 hdpi,以及针对超高密度屏幕的 xhdpi、xxhdpi 和 xxxhdpi 。 不同大小的图标存储在相应的 mipmap- 文件夹中 。
各文件夹下:
ic_launcher:桌面图标【可以在设置->更多->应用程序中查看】,
ic_launcher_background:背景图,
ic_launcher_foreground:前景图,
ic_launcher_round:圆形图【屏幕上的】,
并非每个人都具有可用于创建自定义图标和启动图像(让应用与众不同)的设计器。下面是几种生成自定义应用图像的备选方法:
-
Android Asset Studio – 是一个基于 Web 的浏览器生成器,针对所有类型 Android 图标,带有其他有用社区工具的链接。 在 Google Chrome 中性能最佳。
-
Visual Studio – 可以用于直接在 IDE 中为应用创建简单图标集。
-
Glyphish – 可免费下载和购买的高质量预生成图标集。
-
Fiverr – 从各种设计器中进行选择以便为你创建图标集(最低 5 美元)。 可以漫无目标,不过如果需要动态设计的图标,这是很好的资源。
有关图标大小和要求的详细信息,请参阅 Android 资源指南。
应用程序基础知识
可访问性
API级别
1、Android资源
新增加资源:eg,my_image_name .png【约定用下划线,小写】,并将其生成操作设置为:Android资源
2、活动生命周期方法
一个Activity活动就是一个界面的布局。参考:onResume什么时候执行,执行几次的问题
程序正常启动时:onCreate()->onStart()->onResume();
onCreate()在活动第一次创建时被调用,主要用于加载布局。
onStart()这个方法在活动由不可见变为可见的时候调用。
onResume()这个方法在活动准备好和用户进行交互的时候调用。此时的活动一定位于返回栈的栈顶,并且处于运行状态。
三种调用的场景:
一个Activity启动另一个Activity: onPause()->onStop(), 再返回:onRestart()->onStart()->onResume()
程序按back 退出: onPause()->onStop()->onDestory(),再进入:onCreate()->onStart()->onResume();
程序按home 退出: onPause()->onStop(), 再进入:onRestart()->onStart()->onResume();
OnCreate
OnCreate是创建活动时要调用的第一个方法,OnCreate
始终需要重写,以执行活动可能需要的任何启动初始化,如:
- 创建视图
- 初始化变量
- 将静态数据绑定到列表
OnCreate
采用一个绑定参数,该参数是在绑定不为 null 时用于存储和传递状态信息和活动之间的对象的字典,这表示活动正在重新启动,应从上一个实例还原其状态。
OnStart
OnCreate
完成后 , 系统始终会调用 OnStart。 如果活动在活动可见之前需要执行任何特定的任务,例如刷新活动中视图的当前值,则活动可能会重写此方法,调用此方法之后Android 将立即调用OnResume
。
OnResume
当活动准备好开始与用户交互时,系统将调用OnResume 【Activity 开始跟用户交互之前会调用 onResume()】。 活动应重写此方法以执行如下任务:
- 斜向上帧速率(游戏开发中的常见任务)
- 开始动画
- 侦听 GPS 更新
- 显示任何相关的警报或对话框
- 连接外部事件处理程序
OnPause
当系统要将活动放入背景中或活动被部分遮盖时,将调用OnPause 。 如果活动需要,则应重写此方法:
-
提交永久性数据的未保存更改
-
销毁或清理使用资源的其他对象
-
增加帧速率和暂停动画
-
注销外部事件处理程序或通知处理程序(即绑定到服务的程序)。 必须执行此操作以防止活动内存泄露。
-
同样,如果活动显示了任何对话框或警报,则必须用
.Dismiss()
方法对其进行清理。
在调用OnPause后,可能会调用以下两个方法:
1、OnResume:if the Activity is to be returned to the foreground.
2、OnStop:if the Activity is being placed in the background
OnStop
当用户不再显示该活动时,将调用OnStop 。 发生以下情况之一时,会发生这种情况:
- 正在启动新活动,并覆盖此活动。
- 现有活动将进入前台。
- 正在销毁活动。
OnDestroy
OnDestroy是在将其销毁并从内存中完全删除之前,在活动实例上调用的最终方法。
大多数活动将不会实现此方法,因为大多数清理和关闭操作都是通过OnPause和OnStop方法完成的;而需要重写OnDestroy方法一般是用来清理长时间运行的可能会泄漏的资源,例如 在OnCreate中启动的后台线程。
OnRestart
对于应该在OnRestart中实现哪种逻辑,没有通用的指导原则。这是因为无论正在创建活动还是重新启动活动,都始终调用OnStart,因此活动所需的任何资源都应该在OnStart中初始化,而不是在OnRestart中初始化。
3、Back vs. Home
程序按back 退出: onPause()->onStop()->onDestory(),再进入:onCreate()->onStart()->onResume();
程序按home 退出: onPause()->onStop(), 再进入:onRestart()->onStart()->onResume();
按Back键会销毁程序,home不会,只是退到后台线程。
4、Managing State Throughout the Lifecycle
在停止或者销毁时,系统提供一种保存状态(实例状态)的机会,三种方式:
- android采用称之为Bundle的字典(key/value) 来保存原始值
- Creating a custom class that will hold complex values such as bitmaps.
- 绕过配置更改生命周期,并承担维护活动状态的全部责任。
4.1、Bundle介绍
Bundle不适合保存复杂数据(如 位图)
活动提供一些方法来帮助在捆绑包中保存和检索实例状态:
-
OnSaveInstanceState–此操作由 Android 在活动被销毁时调用。 如果活动需要保留任何键/值状态项,则这些活动可以实现此方法。
-
OnRestoreInstanceState–在OnCreate方法完成后将调用此方法,并为Activity提供了另一个在初始化完成后恢复其状态的机会。
1.OnSaveInstanceState
当活动正在停止时,将调用OnSaveInstanceState 。 它将接收一个捆绑参数,活动可在其中存储其状态。 当设备遇到配置更改时【例如 改变屏幕方向,横屏竖屏】,活动可以使用Bundle
传入的对象通过重写OnSaveInstanceState
来保留活动状态。
保存活动中的暂时性数据【eg 计数器】,而控件的Text值不会改变。
参考:onSaveInstanceState()和onRestoreInstanceState()使用详解
当系统开始停止您的Activity时,它会调用onSaveInstanceState()(1),以便您可以指定要保存的其他状态数据,以防Activity必须重新创建实例。如果Activity被破坏并且必须重新创建相同的实例,则系统将(1)中定义的状态数据传递给onCreate()方法(2)和onRestoreInstanceState()方法(3)。
注意
1、如果是用户自动按下Home键,是不会触发onSaveInstanceState()和onRestoreInstanceState()的。
2、每次用户旋转屏幕时,您的Activity将被破坏并重新创建。当屏幕改变方向时,系统会破坏并重新创建前台Activity,因为屏幕配置已更改,您的Activity可能需要加载替代资源(例如布局)。即会执行onSaveInstanceState()和onRestoreInstanceState()的。
2.OnRestoreInstanceState
还原状态,一般在OnCreate中还原状态,而不用重写OnRestoreInstanceState。
4.2 保存复杂数据
通过重写OnRetainNonConfigurationInstance并返回包含要保存的数据的Java.Lang.Object
实例来保存数据。 使用OnRetainNonConfigurationInstance
保存状态有两个主要优点:
-
从
OnRetainNonConfigurationInstance
返回的对象对更大、更复杂的数据类型执行良好的工作,因为内存会保留此对象。 -
OnRetainNonConfigurationInstance
方法将按需调用,并且仅在需要时调用。 这比使用手动缓存更经济。
5、Service
- 绑定服务
实现和使用绑定服务
为了使 Android 应用程序使用绑定的服务,必须实现以下
- 新建服务。扩展
Service
类并实现生命周期回调方法 –此类将包含执行服务所需工作的代码。【eg:一个告诉用户服务启动的时间和运行时间的方法api】 - 创建一个实现
IServiceConnection
接口的类,这个接口提供供Android 调用的回调方法(OnServiceConnected),以便在服务连接发生更改时通知客户端,eg:客户端已连接或断开连接到服务.。服务连接还将提供对对象(Binder)的引用,客户端可使用该对象直接与服务交互。 - 创建一个实现
IBinder接口的类,
IBinder接口提供了客户端用来与服务进行通信的 API。 Binder 可以提供对绑定服务的引用、允许直接调用方法或Binder 可以提供一个客户端 API,用于封装和隐藏应用程序中的绑定服务。IBinder
必须提供远程过程调用所需的代码。 不需要(或建议)直接实现IBinder
接口。 相反,应用程序应该扩展Binder类型,它提供IBinder所需的大部分基本功能。 - 启动Activity并绑定到服务。一旦 service connection, binder, and service 被创建,Android 应用程序负责启动该服务并将其绑定到它。