第1章 HelloWorld
转载请注明出处:http://blog.csdn.net/itismelzp/article/details/50352771
任何一个编程语言写出的第一个程序毫无疑问都会是Hello World,这已经是自20世纪70年代一直流传下来的传统,在编程界已成为永恒的经典,那我们当然也不会搞例外了。
接下来是几个下拉选择框,Minimum Required SDK是指程序最低兼容的版本,这里我们选择Android 4.0。Target SDK是指你在该目标版本上已经做过了充分的测试,系统不会再帮你在这个版本上做向前兼容的操作了,这里我们选择最高版本Android 4.4。Compile With是指程序将使用哪个版本的SDK进行编译,这里我们同样选择Android 4.0。最后一个Theme是指程序UI所使用的主题,我个人比较喜欢选择None。全部都选择好的界面如图1所示。
图1
现在我们可以点击Next了,下一个界面是创建项目的一些配置,全部保持默认配置就好,如图2所示。
图2
直接点击Next进入到启动图标的配置界面,在这里配置的图标就会是你的应用程序安装到手机之后显示的图标,如图3所示。
图3
如果你程序的Logo还没设计好,别着急,在项目里面也是可以配置启动图标的,这里我们就先不配置,直接点击Next。
然后跳转到的是创建活动界面,在这个界面你可以选择一个你想创建的活动类型,这里我们就选择Blank Activity了,如图4所示。
图4
继续点击Next后,我们需要给刚刚选择的Blank Activity起一个名字,然后给这个活动的布局也起一个名字。Activity Name就填入HelloWorldActivity,Layout Name就填入hello_world_layout吧,如图5所示。
图5
然后点击Finish,项目终于创建完成了!
图6
好了,确认完模拟器在线后,点击Eclipse工具栏右侧的Java选项,回到之前的视图,然后我们来运行一下项目吧。右击HelloWorld项目→Run As→Android Application。等待大约几秒钟的时间,你的项目就会运行起来了。现在快去看看你的模拟器吧,结果应该和图7中显示的是一样的。
图7
HelloWorld项目运行成功!并且你会发现,你的模拟器上已经安装上Hello World这个应用了。打开启动器列表,如图8所示。
图8
这个时候你可能会说我坑你了,说好的第一行代码呢?怎么一行还没写,项目就已经运行起来了?这个只能说是因为ADT太智能了,已经帮我们把一些简单内容都自动生成了。你也别心急,后面写代码的机会多着呢,我们先来分析一下HelloWorld这个项目吧。
图9
一开始看到这么多陌生的东西,你一定会感到有点头晕吧。别担心,我现在就对上图中的内容一一讲解,你很快再看这张图就不会感到那么吃力了。
1. src
毫无疑问,src目录是放置我们所有Java代码的地方,它在这里的含义和普通Java项目下的src目录是完全一样的,展开之后你将看到我们刚才创建的HelloWorldActivity文件就在里面。
2. gen
这个目录里的内容都是自动生成的,主要有一个R.java文件,你在项目中添加的任何资源都会在其中生成一个相应的资源id。这个文件永远不要手动去修改它。
3. assets
这个目录用得不多,主要可以存放一些随程序打包的文件,在你的程序运行时可以动态读取到这些文件的内容。另外,如果你的程序中使用到了WebView加载本地网页的功能,所有网页相关的文件也都存放在这个目录下。
4. bin
这个目录你也不需要过多关注,它主要包含了一些在编译时自动产生的文件。其中会有一个你当前项目编译好的安装包,展开bin目录你会看到HelloWorld.apk,把这个文件拷到手机上就可以直接安装了。
5. libs
如果你的项目中使用到了第三方Jar包,就需要把这些Jar包都放在libs目录下,放在这个目录下的Jar包都会被自动添加到构建路径里去。你可以展开上图中Android 4.0、Android Private Libraries、Android Dependencies这些库,其中显示的Jar包都是已经被添加到构建路径里的。
6. res
这个目录下的内容就有点多了,简单点说,就是你在项目中使用到的所有图片、布局、字符串等资源都要存放在这个目录下,前面提到的R.java中的内容也是根据这个目录下的文件自动生成的。当然这个目录下还有很多的子目录,图片放在drawable目录下,布局放在layout目录下,字符串放在values目录下,所以你不用担心会把整个res目录弄得乱糟糟的。
7. AndroidManifest.xml
这是你整个Android项目的配置文件,你在程序中定义的所有四大组件都需要在这个文件里注册。另外还可以在这个文件中给应用程序添加权限声明,也可以重新指定你创建项目时指定的程序最低兼容版本和目标版本。由于这个文件以后会经常用到,我们用到的时候再做详细说明。
8. project.properties
这个文件非常地简单,就是通过一行代码指定了编译程序时所使用的SDK版本。我们的HelloWorld项目使用的是API 14,你也可以在这里改成其他版本试一试。
这样整个项目的目录结构就都介绍完了,如果你还不能完全理解的话也很正常,毕竟里面有太多的东西你都还没接触过。不用担心,这并不会影响到你后面的学习。相反,等你学完整本书后再回来看这个目录结构图时,你会觉得特别地清晰和简单。
接下来我们一起分析一下HelloWorld项目究竟是怎么运行起来的吧。首先打开AndroidManifest.xml文件,从中可以找到如下代码:
<activity android:name="com.test.helloworld.HelloWorldActivity" android:label="@string/app_name" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity>
那HelloWorldActivity具体又有什么作用呢?我在介绍Android四大组件的时候说过,活动是Android应用程序的门面,凡是在应用中你看得到的东西,都是放在活动中的。因此你在图1.15中看到的界面,其实就是HelloWorldActivity这个活动。那我们快去看一下它的代码吧,打开HelloWorldActivity,代码如下所示:
public class HelloWorldActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.hello_world_layout); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.hello_world, menu); return true; } }
首先我们可以看到,HelloWorldActivity是继承自Activity的。Activity是Android系统提供的一个活动基类,我们项目中所有的活动都必须要继承它才能拥有活动的特性。然后可以看到HelloWorldActivity中有两个方法,onCreateOptionsMenu()这个方法是用于创建菜单的,我们可以先无视它,主要看下onCreate()方法。onCreate()方法是一个活动被创建时必定要执行的方法,其中只有两行代码,并且没有Hello world!的字样。那么图1.15中显示的Hello world!是在哪里定义的呢?
其实Android程序的设计讲究逻辑和视图分离,因此是不推荐在活动中直接编写界面的,更加通用的一种做法是,在布局文件中编写界面,然后在活动中引入进来。你可以看到,在onCreate()方法的第二行调用了setContentView()方法,就是这个方法给当前的活动引入了一个hello_world_layout布局,那Hello world!一定就是在这里定义的了!我们快打开这个文件看一看。布局文件都是定义在res/layout目录下的,当你展开layout目录,你会看到hello_world_layout.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" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context=".HelloWorldActivity" > <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/hello_world" /> </RelativeLayout>
现在还看不懂?没关系,后面我会对布局进行详细讲解的,你现在只需要看到上面代码中有一个TextView,这是Android系统提供的一个控件,用于在布局中显示文字的。然后你终于在TextView中看到了hello world的字样,哈哈终于找到了,原来就是通过android:text= "@string/hello_world"这句代码定义的!咦?感觉不对劲啊,好像图1.15中显示的是Hello world!,这感叹号怎么没了,大小写也不太一样。
其实你还是被欺骗了,真正的Hello world!字符串也不是在布局文件中定义的。Android不推荐在程序中对字符串进行硬编码,更好的做法一般是把字符串定义在res/values/strings.xml里,然后可以在布局文件或代码中引用。那我们现在打开strings.xml看一下,里面的内容如下:
<resources> <string name="app_name">Hello World</string> <string name="action_settings">Settings</string> <string name="hello_world">Hello world!</string> </resources>
这个时候我无意中瞄到了这个文件中还有一个叫做app_name的键。你猜对了,我们还可以在这里通过修改app_name对应的值,来改变此应用程序的名称。那到底是哪里引用了app_name这个键呢?打开AndroidManifest.xml文件自己找找去吧!
图10
到这么多的文件夹不用害怕,其实归纳一下,res目录就变得非常简单了。所有以drawable开头的文件夹都是用来放图片的,所有以values开头的文件夹都是用来放字符串的,layout文件夹是用来放布局文件的,menu文件夹是用来放菜单文件的。怎么样,是不是突然感觉清晰了很多?之所以有这么多drawable开头的文件夹,其实主要是为了让程序能够兼容更多的设备。在制作程序的时候最好能够给同一张图片提供几个不同分辨率的副本,分别放在这些文件夹下,然后当程序运行的时候会自动根据当前运行设备分辨率的高低选择加载哪个文件夹下的图片。当然这只是理想情况,更多的时候美工只会提供给我们一份图片,这时你就把所有图片都放在drawable-hdpi文件夹下就好了。
知道了res目录下每个文件夹的含义,我们再来看一下如何去使用这些资源吧。比如刚刚在strings.xml中找到的Hello world!字符串,我们有两种方式可以引用它:
1. 在代码中通过R.string.hello_world可以获得该字符串的引用;
2. 在XML中通过@string/hello_world可以获得该字符串的引用。
基本的语法就是上面两种方式,其中string部分是可以替换的,如果是引用的图片资源就可以替换成drawable,如果是引用的布局文件就可以替换成layout,以此类推。这里就不再给出具体的例子了,因为后面你会在项目中大量地使用到各种资源,到时候例子多得是呢。另外跟你小透漏一下,HelloWorld项目的图标就是在AndroidManifest.xml中通过android:icon="@drawable/ic_launcher"来指定的,ic_launcher这张图片就在drawable文件夹下,如果想要修改项目的图标应该知道怎么办了吧?
通过上一节的学习,你已经成功创建了你的第一个Android程序,并且对Android项目的目录结构和运行流程都有了一定的了解。现在本应该是你继续前行的时候,不过我想在这里给你穿插一点内容,讲解一下Android中日志工具的使用方法,这对你以后的Android开发之旅会有极大的帮助。
图11
然后选中LogCat,点击OK,这样你就成功将LogCat添加到Eclipse中了。
这个方法用于打印程序中的错误信息,比如程序进入到了catch语句当中。当有错误信息打印出来的时候,一般都代表你的程序出现严重问题了,必须尽快修复。对应级别error,比warn高一级。
其实很简单,一共就五个方法,当然每个方法还会有不同的重载,但那对你来说肯定不是什么难理解的地方了。我们现在就在HelloWorld项目中试一试日志工具好不好用吧。
打开HelloWorldActivity,在onCreate()方法中添加一行打印日志的语句,如下所示:
protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.hello_world_layout); Log.d("HelloWorldActivity", "onCreate execute"); }
Log.d方法中传入了两个参数,第一个参数是tag,一般传入当前的类名就好,主要用于对打印信息进行过滤。第二个参数是msg,即想要打印的具体的内容。
现在可以重新运行一下HelloWorld这个项目了,仍然是右击HelloWorld项目→Run As→Android Application。等程序运行完毕,可以看到LogCat中打印信息如图12所示。
图12
其中你不仅可以看到打印日志的内容和Tag名,就连程序的包名、打印的时间以及应用程序的进程号都可以看到。如果你的LogCat中并没有打印出任何信息,有可能是因为你当前的设备失去焦点了。这时你只需要进入到DDMS视图,在Devices窗口中点击一下你当前的设备,打印信息就会出来了。
另外不知道你有没有注意到,你的第一行代码已经在不知不觉中写出来了,我也总算是交差了。
为什么System.out.println()方法会这么遭大家唾弃呢?经过我仔细分析之后,发现这个方法除了使用方便一点之外,其他就一无是处了。方便在哪儿呢?在Eclipse中你只需要输入syso,然后按下代码提示键,这个方法就会自动出来了,相信这也是很多Java新手对它钟情的原因。那缺点又在哪儿了呢?这个就太多了,比如日志打印不可控制、打印时间无法确定、不能添加过滤器、日志没有级别区分……
听我说了这些,你可能已经不太想用System.out.println()方法了,那么Log就把上面所说的缺点全部都做好了吗?虽然谈不上全部,但我觉得Log已经做得相当不错了。我现在就来带你看看Log和LogCat配合的强大之处。
首先在LogCat中是可以很轻松地添加过滤器的,你可以在图13中看到我们目前所有的过滤器。
图13
目前只有两个过滤器,All messages过滤器也就相当于没有过滤器,会把所有的日志都显示出来。com.test.helloworld过滤器是我们运行HelloWorld项目时自动创建的,点击这个过滤器就可以只看到HelloWorld程序中打印的日志。那可不可以自定义过滤器呢?当前可以,我们现在就来添加一个过滤器试试。
点击图1.21中的加号,会弹出一个过滤器配置界面。我们给过滤器起名叫data,并且让它对名为data的Tag进行过滤,如图1.22所示。
图14
点击OK,你就会发现你已经多出了一个data过滤器,当你点击这个过滤器的时候,你会发现刚才在onCreate()方法里打印的日志没了,这是因为data这个过滤器只会显示Tag名称为data的日志。你可以尝试在onCreate()方法中把打印日志的语句改成Log.d("data", "onCreate execute"),然后再次运行程序,你就会在data过滤器下看到这行日志了。
不知道你有没有体会到使用过滤器的好处,可能现在还没有吧。不过当你的程序打印出成百上千行日志的时候,你就会迫切地需要过滤器了。
看完了过滤器,再来看一下LogCat中的日志级别控制吧。LogCat中主要有5个级别,分别对应着我在上一节介绍的5个方法,如图15所示。
图15
当前我们选中的级别是verbose,也就是最低等级。这意味着不管我们使用哪一个方法打印日志,这条日志都一定会显示出来。而如果我们将级别选中为debug,这时只有我们使用debug及以上级别方法打印的日志才会显示出来,以此类推。你可以做下试验,如果你把LogCat中的级别选中为info、warn或者error时,我们在onCreate()方法中打印的语句是不会显示的,因为我们打印日志时使用的是Log.d()方法。
日志级别控制的好处就是,你可以很快地找到你所关心的那些日志。相信如果让你从上千行日志中查找一条崩溃信息,你一定会抓狂的吧。而现在你只需要将日志级别选中为error,那些不相干的琐碎信息就不会再干扰你的视线了。
关于Android中日志工具的使用我就准备讲到这里,LogCat中其他的一些使用技巧就要靠你自己去摸索了。今天你已经学到了足够多的东西,我们来总结和梳理一下吧。