跟随这个逐渐深入的实战教程,零基础学习移动应用程序开发。
预计时间:20分钟
这是Xamarin Android入门系列的第二篇文章,第一章是关于安装的。本文阐述如何使用Xamarin Android创建并部署Android应用程序。本文同时描述了默认应用程序模板,以及如何创建简单的Hello World应用程序。
Hello,Multiscreen Applications(多屏幕程序)
预计时间:20分钟
这是Xamarin Android入门系列的第三篇文章,讲述Android应用程序的组成,并介绍Android的Activity和Intent。然后阐述如何启动多屏幕应用程序的Activity。同时说明如何使用Intent在屏幕间传递数据。
Next Steps(下一步)
预计时间:20分钟
这是Xamarin Android入门系列的最后一篇文章。读者已经对使用Xamarin Android开发Android程序有了基本了解。本文讲述后续课程安排。
本文给开发者编写第一个“Hello,GLCube”应用程序提供了入门向导。涵盖了创建、生成、编译、调试这个“Hello,GLCube”OpenTK应用程序的所有步骤。
安装
安装Xamarin.Android
Windows
Windows安装的教程,点击here.
Mac
Mac安装的教程,点击here.
部署
Hello, Android
本文讲述如何创建、部署、运行一个Xamarin.Android 应用程序。首先,阐述如何在部署过程中使用默认的应用程序模板。接下来,讲述模板创建的android应用程序的各个部分。然后创建Hello world应用程序,讲述如何使用代码和XML创建用户界面。
作为入门程序,我们按步骤创建一个Xamarin Android应用程序。Xamarin Android可在OS X及Windows平台使用Xamarin Studio创建;也可在Windows的Visual Studio2010专业版(或更高版本)上创建。各个平台创建Xamarin Android应用程序的过程几乎相同。本教程假设您已经安装了Xamarin Android。如果没有安装,请先查看安装文档。
首先创建一个Xamarin.Android 解决方案。Xamarin.Android 包括几个创建项目的模板:
· Android Library Project – 可重用的Android .NET库项目
· Android Application – 具有单个Activity的基本项目
· Android OpenGL Application–OpenGL项目.
本教程使用AndroidApplication 模板。按如下步骤创建程序:
在File 菜单选择 New > Solution, 弹出如下窗口:
1. 在左侧树中展开C#项目
2. 选择Android,在右侧列表中选择Android Application 模板
3. 项目名称输入为Hello_World,点击OK按钮
在 File 菜单选择 New>Project , 弹出如下窗口:
1. 展开Templates下的Visual C#项
2. 选择Android项目,选择Android Application 模板
3. 项目名称输入 Hello_World,设置保存位置后,点击OK按钮
上一节创建了一个简单的Xamarin Android项目。Xamarin Android为我们生产了很多项目。现在看一下这些项目。
Hello_World项目包含三个目录,分别是Assets、Properties和Resources。详见下表:
目录 | 用途 |
Assets | 包含应用程序要打包的任何类型文件。这些文件在运行时使用Assets类访问。 |
Properties | 包含正常的.NET 程序集元数据(assembly metadata) |
Resources | 包含应用程序资源文件,如字符串和图像,以及用户界面定义XML文件。这些资源通过自动生成的Resource类访问。 |
项目模板同时在MainActivity.cx中生成一个叫做Activity1的类。Activity类模拟用户使用App可操作的动作,通常是一个用户界面。
概念上,一个Activity可认为是应用程序屏幕的映射。Activity与ASP.NET中的Page相似,每个Activity都有相应的生命周期。Activity包含每个生命周期会调用的函数。这些函数可以在子类中重写。例如,项目模板创建的Activity子类,重写了OnCreate方法,这个方法在Activity启动后被调用。更多信息请见ActivityLifecycle。
在实现HelloWorld App之前,我们启动由模板创建的应用程序。将应用程序部署到设备,请见Set Up Device for Development guide。在模拟器中运行App,请见Configure the Emulator guide。
从上面的描述可见,默认的应用程序模板为我们创建Helloworld应用程序提供了很好的起点。为了展示如何从零创建项目,我们创建一个包含TextView和Button的应用程序。点击Button可改变TextView上的文字。下图是应用程序在模拟器上运行的截图。
使用代码创建新的用户界面
现在使用代码创建用户界面。前面提到了Activity及其生命周期。Activity启动后,将调用OnCreate方法。这里是对应用程序初始化的恰当位置,比如为Activity加载用户界面。
对于这个应用程序,我们需要创建Button和TextView。当用户点击Button,修改TextView显示的信息。要实现这些,打开MainActivity.cs文件,替换OnCreate方法为:
1 |
protectedoverridevoid OnCreate (Bundle bundle) { base.OnCreate (bundle);
//Create the user interface in code var layout =newLinearLayout (this); layout.Orientation=Orientation.Vertical;
var aLabel =new TextView (this); aLabel.Text="Hello, Xamarin.Android";
var aButton =newButton (this); aButton.Text="Say Hello"; aButton.Click += (sender, e) => { aLabel.Text ="Hello from the button"; }; layout.AddView (aLabel); layout.AddView (aButton); SetContentView (layout); } |
我们将这些代码按行拆解。首先创建LinearLayout,并设置Orientation为Vertical:
1 |
var layout =new LinearLayout (this); layout.Orientation =Orientation.Vertical; |
Android使用布局类组织和定位屏幕上的控件。我们添加的组件,如Button和TextView都是布局的子控件。LinearLayout类用于一个接一个,水平或垂直排列子控件。
这与Silverlight中的StackPanel相似。
接下来创建Button和TextView,并设置Taxt属性:
1 |
var aLabel =newTextView (this); aLabel.Text ="Hello, Xamarin.Android";
var aButton =newButton (this); aButton.Text ="Say Hello"; |
用户点击按钮,修改TextView的文字。在Xamarin Android中,我们使用典型的.NET事件。要处理这些事件,我们可以使用事件处理器、匿名方法,或这里使用的Lambda表达式:
1 |
aButton.Click += (sender, e) => { aLabel.Text ="Hello from the button"; }; |
也可使用C#2.0的匿名方法:
1 |
aButton.Click +=delegate(object sender, EventArgs e) { aLabel.Text ="Hello from the button"; }; |
创建控件并设置事件处理器后,需要将他们加入到LinearLayout实例中。LinearLayout是ViewGroup的子类。ViewGroup是典型的用于显示其他视图并决定如何显示子控件的视图。ViewGroup类有一个addView方法用于添加子控件,例如:
1 |
layout.AddView (aLabel); layout.AddView (aButton); |
最后将布局添加到屏幕上。这里调用Activity的SetContentView方法,传参为一个布局对象:
1 |
SetContentView (layout); |
保存并运行应用程序,点击按钮后如下图所示:
从这段代码可见,对于.NET开发者,可以非常熟悉和容易的使用Xamarin Android开发应用程序。然而,管理资源的方式是Android独有的。这个例子中我们使用硬编码的方式完成全部工作。接下来我们修改为使用Android资源系统来实现,并展示如何使程序更简单和安全。
要设置自动以应用程序图标,首先下载本教程的XamarinAndroid Icons 压缩包。Icon存储在Resources目录,其中包含Android项目相关的图像和布局文件。首先,我们修改已存在的图标。在项目中,导航到Resources目录。从drawable目录中删除已存在的Icon.png。然后,右击drawable目录,选择add—>add files:
导航到解压的xamarin_android_icons 目录,选择drawable 目录:
接着,选择需要的icon.png文件:
在界面中选择Cope选项将文件复制到目录:
Android设备具有多种屏幕尺寸和分辨率。为了解决这个问题,最好在资源目录中加入不同分辨率的图标。重复上述过程,选择Add Existing Folder选项,将xamarin_android_icons目录中的子目录添加到项目中:
最后,我们需要在Android的Manifest文件中设置图标。要创建Manifest,双击项目,打开Project Options;然后,选择Android Application面板。如果Manifest文件不存在,点击按钮自动创建一个:
更多Android的Manifest 信息,请见Xamarin Workingwith the Android Manifest 向导
在Manifest 中,设置Application Icon 为 @drawable/icon:
创建字符串资源
我们已经知道,Android访问资源的方式比较独特。Android资源在Solution Explorer中的Resources目录下进行管理。为在代码中访问他们,自动生成的Resources类会针对Resources下所有的子目录进行更新。
下面看看把字符串硬编码改为字符串资源的例子。在Resources—>values目录中,打开Strings.xml文件。这个文件中包含应用程序字符串资源。修改文件为如下内容:
1 |
<?xml version="1.0" encoding="utf-8"?> <resources> <string name="helloButtonText">SayHello</string> <string name="helloLabelText">HelloXamarin.Android</string> </resources> |
我们在上面的xml中定义了两个字符串资源,一个名为helloButtonText,另一个名为helloLabelText。每个都含有一个字符串值。当我们在Strings.xml文件中加入这样的值,自动生成的Resources类将会在重新生成(rebuild)的时候更新,这样可以得到在代码中操作资源的机制。
Resources类在Resources.designer.cs文件中。由于是自动生成的,因此不要手动去修改。由于刚添加的字符串,会生成子类Strings。类中的整数字段标识出每个字符串,如下所示:
1 |
publicpartialclassString { // aapt resource value: 0x7f040000 publicconstint helloButtonText =2130968576;
// aapt resource value: 0x7f040001 publicconstint helloLabelText =2130968577;
privateString() { } } |
在代码中访问字符串,可以调用TextView和Button控件的SetText方法,传递相应的资源ID,使用SetText方法替换掉设置aLabel.Text属性的代码行:
1 |
aLabel.SetText(Resource.String.helloLabelText); |
同样调用SetText设置Button的Text:
1 |
aButton.SetText(Resource.String.helloButtonText); |
运行应用程序,效果与其他技术实现相同,但我们可以轻松的管理字符串值,而不必在代码中进行修改。
资源在我们使用XML声明用户界面的时候也会起到作用,下节阐述。
除了在代码中创建用户界面,Android也支持声明的基于XML的用户界面系统,与XAML和HTML技术类似。现在将范例修改为使用XML创建UI。
在Resources—>layout目录下有个Main.xml文件。这个文件是由Xamarin Android自动创建的。与字符串类似,Android会自动生成唯一的ID让我们在代码中访问布局文件。可在嵌入类Resources.Layout中找到具有布局文件名称的ID。下面的代码是Layout类的片段:
1 |
publicpartialclassResource { staticResource() { global::Android.Runtime.ResourceIdManager.UpdateIdValues(); }
publicpartialclassLayout {
// aapt resource value: 0x7f030000 publicconstintMain=2130903040;
static Layout() { global::Android.Runtime.ResourceIdManager.UpdateIdValues(); }
privateLayout() { } } } |
当我们需要在代码中引用Main.axml,可使用语法Resource.Layout.Main.
下面改造这个文件,包含LinearLayout,一个TextView和Button,和上面用代码创建的应用程序相同。可在Main.axml中加入如下xml来完成:
1 |
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent"> <TextView android:id="@+id/helloLabel" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/helloLabelText"/> <Button android:id="@+id/aButton" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/helloButtonText"/> </LinearLayout> |
如果我们加入了LinearLayout,Textview,Button这些XML元素,他们将在运行时被实例化。要在代码中访问他们,可以使用他们的资源ID。
资源ID
语法@+id/name告诉Android解析器为特定元素生成一个名称合法的资源ID。例如,有一个Textview类,其ID为@id/helloLabel,Resources类生成一个具有helloLabel整数字段的嵌入类Id。同样,Id类将包含名为aButton的字段,如下所示:
1 |
publicpartialclassId { // aapt resource value: 0x7f050001 publicconstint aButton =2131034113;
// aapt resource value: 0x7f050000 publicconstint helloLabel =2131034112;
privateId() { } } |
同时,语法@string/name允许我们引用在Strings.xml中创建的字符串资源。
同前面的字符串资源一样,我们也可以通过Resources类来获取控件,这时使用Id子类。现在返回Activity1.cs文件来实现。
在OnCreate方法中,仅需要用代码设置内容视图(contentview),以及事件处理器,LinearLayout、TextView、Button由我们创建的xml生成。修改OnCreate方法:
1 |
protectedoverridevoid OnCreate (Bundle bundle) { base.OnCreate (bundle);
SetContentView(Resource.Layout.Main);
var aButton =FindViewById<Button> (Resource.Id.aButton); var aLabel =FindViewById<TextView> (Resource.Id.helloLabel);
aButton.Click += (sender, e) => { aLabel.Text="Hello from the button"; }; } |
同样需要调用SetContentView方法,但这时我们传递的参数是布局资源Main.axml的ID,Resource.Layout.Main。
接下来,获取Button和TextView的实例,使用FindViewById方法,分别传递相应资源的Id。最后,按同样的方式设置事件处理器。
现在可以运行App了,效果和前面用代码实现的程序完全相同。
本文阐述了如果使用Xamarin Android创建部署Android应用程序。查看了由Xamarin Android应用程序模板创建的Android应用程序的各个部分。然后演示只使用代码创建简单的应用程序。最后,使用Android XML声明用户界面创建同样的应用程序。
Hello, 多界面应用程序
概述
本文讨论如何使用XamarinAndroid创建多界面应用程序,并按步骤创建一个简单的多界面App。将介绍Intent如何启动其他Activity。然而,在创建应用程序之前,先解释一下Android应用程序的构成。
Android应用程序与传统平台如Windows、Mac OS X,甚至移动平台如iOS客户端应用程序有很大不同。这些平台具有单一的应用程序入口—静态main函数,负责启动时创建应用程序实例,加载管理界面等等。相反,Android应用程序由一系列松散耦合的由Activity类管理的界面和长时间后台运行的Service类组成。这与Web应用程序很像(从URL启动),, 除了这些,甚至维护应用程序实例方面也与Web应用程序类似。
下图阐述了基本Android应用程序的组成:
这种松耦合架构产生了多界面程序交互的问题。由于每个Activity都是相互解耦的,需要一种方式来启动并向其传递数据。在Android中使用Intent完成。Intent类描述如下信息:期望的动作和随之传递的数据。
下面讲解Activity和Intent。
如上所述,Activity类提供了一个接口。Activity假设是连接到用户界面的窗体。因此,创建多屏幕应用程序包括创建多个Activity并在他们之间切换。
Activity类从Context类继承。
Context是Android最方便的对当前应用程序的引用,提供访问Android系统的机制。Context在Android中需要执行很多操作,如:
· 访问Android服务
· 访问首选项(preferences)
· 创建视图
· 访问设备资源
Android应用程序需要一个Context获取应用程序拥有的权限(permissions),如何创建控件,访问首选项(preferences)等等。由此才实现了Android的一系列松散耦合的activity和Service,而不是仅有一个静态应用程序Context存在。因此,每个Activity和Service都继承Context,可以获取应用程序需要的所有信息。创建Activity实例时,需要传递一个Context参数指定创建者。由于Activity从Context继承,也可以传递Activity实例。
每个Activity都有相应从创建到销毁的声明周期。Activity在任何时候都可由系统暂停和销毁。Android提供的生命周期机制,调用Activity各种生命周期函数,提供保存和加载状态的机会,保证界面持续正常操作。学习完这系列入门教程后,我们会详细讨论Activity生命周期(ActivityLifecycle )
Intent被Android用于触发事件并发送消息。Intent在应用程序中最多用于启动Activity。要启动新的Activity,我们创建新的Intent,设置Context和要启动的Activity类,然后告诉OS处理这个启动Activity的Intent。
另外,Intent也可以用于告诉OS启动外部Activity。例如,应用程序在用户点击按钮后需要启动电话拨号界面。应用程序使用一个Intent来告诉OS,他需要电话号码拨号的动作。然而,电话拨号实际上是由电话拨号应用程序的Activity来处理的。
每个Android应用程序都需要一个AndroidManifest.xml文件。这个文件包含如下应用程序信息:
· 注册组件—组成App的组件,Activity和Intent的注册。
· 所需许可(Permissions) - App需要的许可。
· OS版本兼容性- 应用程序支持的最低Android API版本号。
在本文的稍后按步解释应用程序的部分,会讨论更多manifest信息。
更多信息请参照Android开发者网站的 AndroidManifest.xml文档。
我们已经理解了Android应用程序独有的一些概念,现在就创建一个简单的多屏幕应用程序。
我们将创建一个新的应用程序,第一个Activity具有一个按钮。点击按钮将加载另一个Activity。本例中,我们将阐述显示的指定类来加载应用程序定义的第二个Activity。应用程序启动后的截图如下:
创建应用程序
首先,我们使用XamarinAndroid应用程序模板创建一个新的XamarinAndroid应用程序HelloMultiScreen。如果要预习如何创建新的项目,请参考Hello,Android教程。项目模板将创建一个应用程序,其中Activity1.cx中包含一个Activity的子类Activity1.打开文件并右击Activity1类名,在上下文菜单中选择Refactor—>Rename。将模板创建的Activity1类重命名为FirstActivity。如果使用Visual Studio,可以在解决方案管理器中重命名文件。
在添加代码前,我们解释一下添加到FirstActivity上的ActivityAttribute。
注意FirstActivity类被ActivityAttractive修饰,如下所示:
1 |
[Activity (Label="HelloMultiScreen", MainLauncher=true)] publicclassFirstActivity : Activity |
前面提到Activity必须在AndroidManifest.xml文件中注册,OS才能定位他们,ActivityAttribute就是完成这个过程的。在编译期,Xamarin Android生成过程收集项目中所有类文件,基于项目的AndroidManifest.xml生成新的AndroidManifest.xml,将属性数据合并进来。然后将新的manifest文件捆绑进apk文件中。
提供C#属性,Xamarin Android可在类定义的时候同时注册Activity,并同步为声明的信息。错误的添加Attribute将导致Android无法加载Activity,因为他们还没有注册到系统中。
另外,将属性将MainLauncher设置为true,将Activity注册为可启动的。当安装App时,Android将把可启动Activity的图标作为应用程序图标。
把ActivityAttribute 属性合并到manifest文件后,将于如下xml相似::
1 |
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="hellomultiscreen.hellomultiscreen"> <uses-sdk android:minSdkVersion="11"/> <application android:icon="@drawable/icon" android:label="HelloMultiScreen"> <activity android:name="FirstActivity" android:label="HelloMultiScreen"> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity> </application> </manifest> |
上面的XML中,FirstActivity在系统中注册为可使用Intent过滤器来启动,其Intent过滤器动作指定为 android.intent.action.MAIN,其目录为android.intent.category.LAUNCHER。Intent过滤器注册是Activity专有支持的,这样就可用来处理应用程序启动,并提供进入点。
在Hello, Android教程中,我们使用代码创建或使用XML(Android XML文件,保存为axml后缀)声明UI。这里也是用声明方式。
在LinearLayout中加入一个按钮控件。回想一下在Hello,Android教程中Android使用布局类在屏幕上组织和定位控件。加入的任意控件,如Button,都是布局的子控件。LinearLayout类用于按顺序排列控件,可以水平或垂直方向。
要在FirstActivity中加入按钮,需按如下所示在Main.axml中添加代码:
1 |
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent"> <Button android:id="@+id/showSecond" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="Load SecondActivity"/> </LinearLayout> |
现在已经定义好了UI,我们希望应用程序加载的时候由FirstActivity创建Main.axml中的按钮。
应用程序加载的时候,FirstActivity的OnCreate方法被调用。在这里我们可以使用代码或声明的方式创建UI,这里在Oncreate中加载UI。如下代码加载Main.axml UI并设置按钮的点击事件处理器:
1 |
protectedoverridevoidOnCreate (Bundle bundle) { base.OnCreate (bundle); //Use UI created in Main.axml SetContentView (Resource.Layout.Main);
var showSecond =FindViewById<Button> (Resource.Id.showSecond); showSecond.Click+= (sender, e) => { StartActivity (typeof(SecondActivity)); }; } |
我们将代码拆开分析。
代码首先加载Main.axml中定义的UI:
1 |
SetContentView (Resource.Layout.Main); |
接下来,使用FindViewById方法获取Main.axml中添加的showSecond 按钮引用:
1 |
var showSecond =FindViewById<Button> (Resource.Id.showSecond); |
给这个按钮引用添加事件处理函数,创建SecondActivity实例:
1 |
showSecond.Click+= (sender, e) => { StartActivity(typeof(SecondActivity)); }; |
代码使用StartActivity 的重载版本,接收一个要启动的Activity类型作为参数。内部实现是,创建了一个Intent,并将当前Activity作为第一个参数Context。这个方法是对如下低级Android API的优化:
1 |
var second =new Intent(this, typeof(SecondActivity)); StartActivity(second); |
这种形式可用于控制Intent实例,下面将用于在Activity直接传递数据。
在启动SecondActivity实例之前,我们先创建SecondActivity类。
SecondActivity类由应用程序定义。要创建它,使用Xamarin Studio创建一个新的Activity,点击File->New-->File菜单。然后,在New File对话框中,在左边面版中选择C#-->Android,选择Android Activity文件模板,如下所示:
文件命名为SecondActivity,点击New创建. 确保勾选Add to Project .
SecondActivity在LinearLayout中包含一个TextView控件。在Xamarin Studio的layout目录中添加一个Second.axml文件,并含有如下XML:
1 |
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent"> <TextView android:id="@+id/screen2Label" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="Hello second activity"/> </LinearLayout> |
最后,在SecondActivityOnCreate方法中调用SetContentView加载定义在Second.axml中的UI:
1 |
[Activity (Label="SecondActivity")] publicclassSecondActivity : Activity { protectedoverridevoid OnCreate (Bundle bundle) { base.OnCreate (bundle); // Create your application here SetContentView (Resource.Layout.Second); } } |
启动应用程序,点击showSecond按钮,弹出SecondActivity ,如下所示:
现在已经展示如何加载多个Activity了,下面讲解如何在Activity之间传递数据。
我们可以使用Intent类的PutExtra方法让Intent传递数据。例如,修改FirstActivity 的按钮点击事件:
1 |
showSecond.Click += (sender, e) => { var second =new Intent(this, typeof(SecondActivity)); second.PutExtra("FirstData", "Data from FirstActivity"); StartActivity(second); }; |
上面代码创建Intent实例并传递给StartActivity方法。并有一行新的代码:
1 |
second.PutExtra("FirstData", “data fromFirstActivity”); |
这个代码向Intent添加一个字符串。这种设计与Web应用程序在页面之间使用查询字符串或格式化数据来传递数据相似。另外,PutExtra方法也包含几个传递各种数据类型的重载方法。
在SecondActivity类中,我们使用TextView显示随Intent传递过来的数据:
1 |
var label =FindViewById<TextView> (Resource.Id.screen2Label); label.Text=Intent.GetStringExtra("FirstData") ?? “Data not available”; |
我们调用Intent.GetStringExtra(“FirstData”)取出从FirstActivity传递过来的数据。使用??操作是因为有可能获取到的Intent数据位空。
加载Activity后,将显示从FirstActivity传递过来的数据:
总结
本文讲述组成Android App的各个部分,以及如何协同工作。我们讲述了如何在Android应用程序中使用Activity表现界面,以及如何使用Inten在Activity直接导航。也介绍了AndroidManifest.xml文件,以及Xamarin Android如何使用C#属性在manifest中简单的注册Activity。最后,解释了如果让Intent在Activity之间传递数据。
恭喜,如果你已经跟随Xamarin Android入门系列教程学习到了这里,你应该对创建Android应用程序有了很好的了解。
更多教程请见NextSteps, 将深入讲解Android开发中的各个知识点。