[Android] Step 2: 第一个应用 Hello Android
Hi folks,在上一个Step里,我给大家整理了如何在Windows平台搭建一个Android开发环境,在这里我将会给大家介绍如何去创建我们的第一个Android App,并实现一个简单的Activity间传值操作。
所涉及的内容
- 如何新建一个Android Application Project
- 如何在Android设备上调试Android App
- Android Application Project的结构
- 创建一个简单的用户界面
- 简单的Activity间传值
新建Android Application Project
1. 打开eclipse后,在eclipse菜单选择 File –》New –》Project
2. 在弹出的窗体中选择Android下的Android Application Project
3. 点击Next按钮后,会弹出一个如下图所示的窗体,用户可以填写Project的基本信息
- Application Name:这个Name是当Application在Android设备上安装后显示的名字。
- Project Name:这个Name是当这个Application被建立后包含这个Application源代码的文件夹的名字,同时也是在eclipse中显示的名字。
- Package Name:这个命名就是当前Application的Namespace,如果有同学对Namespace很陌生,建议不明白的同学还是去网上搜索一下。另外在.Net平台,微软自己产品的命名空间一般是公司域名+产品名,比如说Microsoft.com.Office,这是微软的Office产品的命名空间,而在Java里,似乎命名规范正好相反,会变成了com.microsoft.office,Android官方也建议遵循Java的Namespace命名规范。 另外,如果一个Android App的Namespace是以com.example开头的话,是无法在Google Play中发布的,当然,我们仅仅是学习用,就无所谓了。
- Build SDK:指定当前建立的Android App在哪个版本的Android下编译,默认是使用当前环境中最高版本的Android,而我个人的建议是选择一个当前Android设备支持的版本,以避免代码编译通过,当前设备却不支持的尴尬局面。
- Minimum Required SDK:指定这个Android App的核心功能所支持的最低Android版本。如果当前Android设备的系统版本低于这个值,Android系统会提示系统版本过低,无法运行这个App。如果App的其他功能所使用的Android版本高于这个最低Android版本,建议在使用其他功能前判断当前的系统版本再决定是否启用该功能,这样就不会影响到App的核心功能。我这里选择API 14是因为有时候eclipse自动生成的代码是需要API 14,不然无法编译通过,默认是Android 2.2(API 8)。
4. 所有的必填项都填好后,点击Next按钮,进入App的图标选择窗口,在这个窗口里可以给自己的App选择一个漂亮的图标。
5. 图标选择完成后继续点击Next按钮,会进入创建Activity窗口,保持默认,直接Next。
6. 最后进入Activity的基本信息填写窗口,提示用户填写一些所创建Activity的基本信息,继续保持默认,点Finish按钮。
7. 稍等片刻,eclipse就会为我们创建好一个Android Application Project,而此时eclipse的界面,也应该如下图所示。图片左边,是Android Project的源文件,里面一些文件的内容和用途,稍后会向大家讲解,右边,则是我们编写代码和设计界面的工作区,至此,一个Android Application Project就正式创建完成了。
在Android设备上调试App
1. 确定当前的Android设备处于可调试的状态,具体操作是,打开当前Android设备的 系统设置-》开发人员选项,将里面的“USB调试”开关开启。
2. 确保当前Android设备的驱动在操作系统中正确安装,一般来说,当我们买来一台Android手机或平板电脑,都需要在我们的操作系统中安装相对应的驱动,我们的操作系统才能正确识别出Android设备。相关的驱动,可以去手机或平板电脑的生产制造商的官方主页上下载并参照说明安装。
3. 用USB数据连接线连接上电脑和Android设备,此时Android设备应该会提示用户此设备开启USB调试功能,如下图所示。到了这一步,就说明Android设备已经为调试App做好了准备,我们可以开始在Android设备上调试我们的App了。
4. 在eclipse菜单上选择 Project-》Build All,或者是按快捷键CTRL+B,将整个App编译一下,确保编译通过,再在eclipse菜单栏上选择 Run-》Run,或者是按快捷键CTRL+F11将App启动起来,如果是第一次启动,会提示用户使用哪个设备启动App,如下图所示。
提示:因为我的手机Android版本是4.0.4,而我新建的App的Android版本是4.1.2,所以在Target 4.0.4前面有一个小红叉叉,意思是该设备的Android版本不支持当前App,不过因为仅仅是个Demo App,并没有用到4.1.2的最新API,而App的最低版本需求是4.0,所以App在4.0.4的版本上也能正常启动。另外如果不想每次在启动App的时候都弹出这个选择界面,可以勾上Use same device for future launches,这样每次都会默认从这个设备启动App。
6. 选择当前的Android设备并点击OK按钮后,eclipse就会将整个App打包,然后传输到Android设备上进行安装,整个过程会视Android设备硬件配置而花费几秒钟,安装成功后,会在Android设备桌面上生成这个App的图标,并自动启动该App。
到了这里,一个简单的Android App就已经创建完成并启动成功了,下面将为大家介绍Android Project中的树状结构,讲讲里面都是些什么内容。
Android Application Project的结构
当我们建立一个Android Application Project后,在eclipse窗体左边的Package Explorer窗体中,会出现这个Android Project的树状结构图,显示出了包含在这个Android Project中的所有文件夹和文件,我这里只是简单介绍下这个结构,更多的相关信息,请参照Android官方Managing Projects章节。
- src:src目录主要存放着Project中的源文件(Source File),也就是说所有的JAVA文件都会存放在这里,例如我们在新建Project时候默认建立的Activity的代码文件MainActivity.java,就存放在这里。
- res:res目录主要存放着Project所需要的各种资源文件(Resource File),下面会分开介绍子目录的内容。
- drawable:drawable的目录分几个,有hdpi(High dpi)、ldpi(Low dpi)、mdpi(Medium dpi)、xhdpi(Extra High dpi),分别代表着存放着在高分辨率,低分辨率,中等分辨率和超高分辨率的Android设备上,需要显示的不同大小尺寸的图片。总所周知,Android设备不像Apple设备,Apple设备的厂商只有Apple自己,所以Apple设备的分辨率都是固定的,而Android不同,Android作为一个开源(Open Source)系统,拥有众多的设备生产厂商,而生产出来的Android设备,分辨率也会各不相同,当App上线时,为了让我们的App在不同分辨率的Android设备上正常显示,需要将大小不同的图片文件存放在对应的文件夹中。
- layout:layout文件夹下的文件,定义了整个App的窗体布局,我们在应用程序中看到的每一个按钮,以及其他的控件,都是在这个文件夹下对应的xml文件里设置长度,宽度,高度,以及所处位置的。默认创建的MainActivity的布局文件activity_main.xml,也在这个文件夹下。
- menu:menu文件夹里每个xml文件里定义了每个Activity的菜单栏,也就是这个Activity的菜单里都有哪些项。
- values:values文件夹里定义了App里所需要的资源数据(Source Data),也就是类似于字符串,颜色,主题等等这些App公用的资源数据,这些资源数据,都是以xml的格式保存的。比如说,我们需要App既支持中文,又支持英文,我们就需要在字符串的资源文件里做一个切换,当Android系统是中文的时候,读取中文字符资源数据,这时候App就会显示成中文的,而当Android系统是英文的时候,我们就读取英文字符资源数据让App显示成英文,至于具体怎么切换,已经超出了Step 2的范围,后面的Step里我会和大家介绍。
- AndroidManifest.xml:这个xml文件中存放的是App的一些基本配置和组件配置,类似于.Net平台下的以.cfg结尾的配置文件,稍后会和大家介绍里面的详细内容。
简单的用户界面
说到用户界面,就不得不说View和ViewGroup,在Android里,Google将View和ViewGroup都定义为控件,比如说,一个按钮(Button),或者是一个复选框(Checkbox),都是一个View,而对于ViewGroup,Google的定义是,一些定义了子控件布局的不可见的控件容器,比如说网格布局控件(Grid)或者是列表布局控件(List),具体View和ViewGroup的关系,可以通过下图显示出来,一个ViewGroup里,可以包含多个View甚至是另外的ViewGroup。
或许,View和ViewGroup这种抽象的东西对于新手来说过于生硬,实在难以理解,我们可以从Android应用的界面定义文件入手,一步步的去了解Android的控件布局,而这些界面定义文件,其实就是一个个的XML文件,通过这些XML文件,定义各自控件的长宽高,排版方式,来达到布局的效果。下面要做的,就是如何去修改这个XML文件,以达到我们所期望的效果。
1. 在eclipse左边的Package Explore中,打开res\layout\目录下的activity_main.xml文件。
2. 在eclipse的主要工作区,此时会显示MainActivity的主要界面,在这个界面的下方,有两个Tab,一个是Graphical Layout,另外一个是activity_main.xml,点击activity_main.xml,切换到XML编辑界面。
3. 切换到XML编辑模式后,回看到如下代码。
<RelativeLayout 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" > <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerHorizontal="true" android:layout_centerVertical="true" android:text="@string/hello_world" tools:context=".MainActivity" /> </RelativeLayout>
4. 将RelativeLayout标签更改为LinearLayout标签,并给LinearLayout添加android:orientation属性,并且设置为horizontal,并设置android:layout_with和android:layout_height为match_parent。
LinearLayout就是个标准的ViewGroup布局控件,它可以针对自身android:orientation属性的值,将布局内的控件显示成水平排列(horizontal)或者垂直排列(vertical)。另外,它还有另外两个属性,android:layout_with和android:layout_height用来定义它的长和高。match_parent的意思就是,它会自动扩充或缩小以适应父控件的大小。在这里因为LinearLayout是父控件,所以当长和高设置为match_parent后,LinearLayout实际上会撑满整个屏幕。
5. 将TextView从LinearLayout中删除,更换为EditText,并添加android:id,android:layout_width,android:layout_height和android:hint属性,完成后,整个activity_main.xml的内容如下。
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="match_parent" > <EditText android:id="@+id/txtEdit" android:layout_width="wrap_content" android:layout_height="wrap_content" android:hint="@string/edit_text"/> </LinearLayout>
android:id,这个属性定义了该控件在整个应用程序中的唯一标识,我们在整个应用程序中,可以通过这个id值,去使用这个控件,例如读取控件中的值或操作这个控件。首先,在Xml中引用任何资源对象的时候,@符号是必须的,+符号只是在第一次定义id的时候使用,当Android SDK编译代码后,会向gen/R.java中添加一个对象的引用,并且以/符号后面的id命名。
wrap_content,这是android:layout_width属性和android:layout_height属性的一个值,这两个属性我在上面已经提到了,就不多说,而wrap_content的意思就是控件自动适应控件的内容,用在这里的意思就是,这个EditText的长和高会根据里面的内容自动撑大。具体效果,可以在android设备上将这个app运行起来,然后输入一长串字符,这样就能看到wrap_content的效果了。
android:hint,当程序首次运行的时候,如果文本框内并没有用户的任何输入,将显示一个默认值来提示用户输入,而这个默认值,就是通过android:hint属性设置的。这里设置的是读取string资源文件下的edit_text指定的值。
6. res文件夹下的values文件夹,在前面已经给大家介绍过了,现在需要做的,是打开res/values/strings.xml文件,向该文件中添加一个name为edit_text的字符串,用于上面提到的EditText拿去当默认显示的字符。修改完成后的strings.xml文件内容如下。
<resources> <string name="app_name">My First App</string> <string name="hello_world">Hello world!</string> <string name="menu_settings">Settings</string> <string name="title_activity_main">MainActivity</string> <string name="edit_text">Enter a message</string> </resources>
7. 在activity_main.xml中添加一个button,并为button的Text添加string资源,经历了上次添加EditText,这次添加控件,就简单得多了,添加完成后的代码如下所示,strings.xml文件的内容我就不贴出来了,毕竟只是像刚才一样添加一个name为send_text的字符串。
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="match_parent" > <EditText android:id="@+id/txtEdit" android:layout_width="wrap_content" android:layout_height="wrap_content" android:hint="@string/edit_text"/> <Button android:id="@+id/btnSend" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/send_text"/> </LinearLayout>
8. 编译整个App,并运行,eclipse会自动将打包好的App拷贝到我们当前的android设备并安装,安装成功后自动运行,运行后的截图如下。
9. 其实到这一步,主界面的控件布局就已经告一段落了,但是还有个问题就是,因为EditText的长和高都设置为wrap_content,所以当我们输入过多的字符串后,EditText会将Button压缩甚至挤走,压缩后的界面如下图所示。
这里我们将使用一个新的概念去解决这个问题,即权重(weight)。权重,就好比说将一定的空间分割成几分,再按比例分配给里面各自的控件,而这里,将EditText的android:layout_weight更改为1,并将android:layout_width更改为0dp,就可以解决这个问题。更改后的main_activity.xml文件内容如下。
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="match_parent" > <EditText android:id="@+id/txtEdit" android:layout_weight="1" android:layout_width="0dp" android:layout_height="wrap_content" android:hint="@string/edit_text"/> <Button android:id="@+id/btnSend" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/send_text"/> </LinearLayout>
简单的Activity间传值
1. 首先,我们需要新建另外一个Activity,用来接收我们传过去的值。单击eclipse工具栏最左边的New按钮,然后在弹出的新建窗体中选择Android Activity,然后点击Next按钮。
2. 后面的窗体大伙都应该很熟悉了,因为在新建Android Application Project的时候都见过。在接下来的窗体中选择BlankActivity,点击Next,然后填写Activity的基本信息,完成后点击Finish按钮。这里注意将Hierarchical Parent选择MainActivity,作为当用户点击后退按钮时将要返回的默认Activity。
3. 在activity_main.xml中为btnSend按钮添加onClick属性,并在MainActivity.java文件中添加sendMessage方法。
4. 在sendMessage方法中,创建一个Intent并通过putExtra方法保存当前用户输入的text,最后启动另外一个Activity。 activity_main.xml和MainActivity.java的内容如下。
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="match_parent" > <EditText android:id="@+id/txtEdit" android:layout_weight="1" android:layout_width="0dp" android:layout_height="wrap_content" android:hint="@string/edit_text"/> <Button android:id="@+id/btnSend" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/send_text" android:onClick="sendMessage"/> </LinearLayout>
package com.example.myfirstapp; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.view.Menu; import android.view.View; import android.widget.EditText; public class MainActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.activity_main, menu); return true; } public void sendMessage(View view){ Intent intent = new Intent(this,DisplayMessageActivity.class); EditText editText = (EditText)this.findViewById(R.id.txtEdit); String message = editText.getText().toString(); intent.putExtra("Message", message); this.startActivity(intent); } }
5. 在DisplayMessageActivity中,通过Intent的getStringExtra方法接收从MainActivity传递过来的message。activity_display_message.xml和DisplayMessageActivity.java的内容如下。
<RelativeLayout 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" > </RelativeLayout>
package com.example.myfirstapp; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.support.v4.app.NavUtils; import android.view.Menu; import android.view.MenuItem; import android.widget.TextView; public class DisplayMessageActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_display_message); getActionBar().setDisplayHomeAsUpEnabled(true); Intent intent = this.getIntent(); String message = intent.getStringExtra("Message"); TextView textView = new TextView(this); textView.setTextSize(40); textView.setText(message); this.setContentView(textView); } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.activity_display_message, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case android.R.id.home: NavUtils.navigateUpFromSameTask(this); return true; } return super.onOptionsItemSelected(item); } }
6. 至此,所有的编码工作就已经完成了,我们可以在我们的设备上运行这个App了,首先会显示第一个Activity,然后随便输入一些字符,例如Hello Android,然后点击Send按钮,这时第二个Activity会显示出来,并且在屏幕上显示从上一个Activity传递过来的Hello Android。
OK,第一个App就讲到这里,在下面一个Step里,我会给大家介绍Activity的生命周期