《Android应用开发揭秘》连载3

《Android应用开发揭秘》连载3《Android应用开发揭秘》  书名:Android应用开发揭秘作者:杨丰盛出版社:机械工业出版社ISBN:9787111291954出版日期:2010年3月(1版2次)开本:16页码:515版次:1-2定价:69元豆瓣网讨论地址:http://www.douban.com/subject/4200822/China-pub预订地址:http://www.china-pub.

大家好,又见面了,我是你们的朋友全栈君。

《Android应用开发揭秘》

 

Android应用开发揭秘(国内首本基于Andriod 2.0的经典著作,5大专业社区一致鼎力推荐!)
 
书名:Android应用开发揭秘

作者:杨丰盛

出版社:机械工业出版社

ISBN:9787111291954

出版日期:2010 年3月(1版2次)

开本:16

页码:515

版次:1-2

定价:69元

豆瓣网讨论地址:
http://www.douban.com/subject/4200822/

China-pub预订地址:
http://www.china-pub.com/196305
 
 
第二部分 基础篇
 
第3章  Android程序设计基础
 
通过上一章的学习,我们对Eclipse+ADT开发流程有了初步的认识和了解,对初学者来说,这一章的内容比较繁琐,但是又必须掌握,这也是进行Android开发必须经过的第一步,有了这个基础,我们下面将进行正式开始Android应用程序设计。
 
3.1  Android程序框架

上一章我们建立了HelloAndroid项目,代码是由ADT插件自动生成的,我们没有对其进行编码,所以没有对其框架进行分析。其实每一个平台都有自己的结构框架,比如我们在最初学习Java或者C/C++时,第一个程序总是main函数,以及文件类型和存储方式等。这一节将对Android平台的目录结构、文件类型及其负责的功能和Android平台的main 函数进行剖析。
 
3.1.1  Android项目目录结构

有了前面两章的基础,现在我们再来打开上一章建立的HelloAndroid项目,分析其项目目录结构,对Android项目进一步地深入了解。首先启动Eclipse,展开“Package Explorer”导航器中的“HelloAndroid”项目,如图3-1所示。


1

 
 图3-1 HelloAndroid项目
 
与一般的Java项目一样,src文件夹是项目的所有包及源文件(.java),res文件夹中则包含了项目中的所有资源,比如:程序图标(drawable)、布局文件(layout)、常量(values)等。下面来介绍其他Java项目中没有的的gen文件夹中的R.java文件和每个Android项目都必须有的AndroidManfest.xml文件。
 
*  R.java是在建立项目时自动生成的,这个文件是只读模式,不能更改,R.java文件是定义该项目所有资源的索引文件。先来看看HelloAndroid项目的R.java文件,如代码清单3-1所示。
 
代码清单3-1  第2章/HelloAndroid/gen/com/yarin/Android/HelloAndroid/R.java
package com.yarin.Android.HelloAndroid;
public final class R {
    public static final class attr {
    }
    public static final class drawable {
        public static final int icon=0x7f020000;
    }
    public static final class layout {
        public static final int main=0x7f030000;
    }
    public static final class string {
        public static final int app_name=0x7f040001;
        public static final int hello=0x7f040000;
    }
}
 
可以看到这里定义了很多常量,这些常量的名字都与res文件夹中的文件名相同,这再次证明了R.java文件中所存储的是该项目所有资源的索引。有了这个文件,可以很快地找到要使用的资源,由于这个文件不能手动编辑,所以当在项目中加入了新的资源时,只需要刷新一下该项目,R.java文件便自动生成了所有资源的索引。
 
*  AndroidManfest.xml文件则包含了该项目中所使用的Activity、Service、Receiver,我们先来打开HelloAndroid项目中的AndroidManfest.xml文件,如代码清单3-2所示。
 
代码清单3-2  第2章/HelloAndroid/AndroidManfest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="com.yarin.Android.HelloAndroid"
      android:versionCode="1"
      android:versionName="1.0">
    <application android:icon="@drawable/icon" android:label="@string/app_name">
        <activity android:name=".HelloAndroid"
                  android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
    <uses-sdk android:minSdkVersion="5" />
</manifest>

 
代码清单3-2中,itent-filters描述了Activity启动的位置和时间。每当一个Activity(或者操作系统)要执行一个操作时,它将创建出一个Intent的对象,这个Intent对象能承载的信息可描述你想做什么,你想处理什么数据,数据的类型,以及一些其他信息。而Android则会和每个Application所暴露的intent-filter的数据进行比较,找到最合适Activity来处理调用者所指定的数据和操作。下面我们来仔细分析AndroidManfest.xml文件,如表3-1所示。
 
表3-1  AndroidManfest.xml分析


表3-1.1
表3-1.2

下面我们看看资源文件中一些常量的定义,如String.xml,如代码清单3-3所示。


代码清单3-3  第2章/HelloAndroid/String.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="hello">Hello World, HelloAndroid!</string>
    <string name="app_name">HelloAndroid</string>
</resources>

 

这个文件很简单,就定义了两个字符串资源,因此,我们可以在代码清单3-1中看到如下内容,即定义了“app_name”和“hello”两个常量,分别指向代码清单3-3中的两个字符串资源。
public static final class string {
    public static final int app_name=0x7f040001;
    public static final int hello=0x7f040000;
}
 
 那么如何在程序中使用我们所定义的这些资源呢?首先,通过Context的getResources实例化一个Resources对象,然后通过Resources的getString方法取得指定索引的字符串,代码如下:
Resources r = this.getContext().getResources();
String appname= ((String) r.getString(R.string.app_name));
String hello= ((String) r.getString(R.string.hello));

 
项目中所有使用的常量都可以通过这种XML文件的方式定义,比如,下面是我们通过XML文件定义的一些有关颜色的资源。
<?xml version="1.0" encoding="utf-8"?>
<resources>
 <color name="status_idle">#cccccc</color>
 <color name="status_done">#637a47</color>
 <color name="status_sync">#cc9900</color>
 <color name="status_error">#ac4444</color>
</resources>

 
现在来分析HelloAndroid项目的布局文件(layout),首先打开res->layout->main.xml文件,如代码清单3-4所示。

代码清单3-4  第2章/HelloAndroid/res/layout/main.xml
<?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:layout_width="fill_parent" 
    android:layout_height="wrap_content" 
    android:text="@string/hello"
    />
</LinearLayout>

 
代码清单3-4中,有以下几个布局和参数。

 *  <LinearLayout>:线性版面配置,在这个标签中,所有元件都是按由上到下的排列排成的。

 *  android:orientation:表示这个介质的版面配置方式,其中“vertical”代表从上到下垂直布局,而“horizontal”代表从左到右水平布局。

*  android:layout_width:定义当前视图在屏幕上所占的宽度,fill_parent即填充整个屏幕。

*  android:layout_height:定义当前视图在屏幕上所占的高度,fill_parent即填充整个屏幕。

*  wrap_content:随着文字栏位的不同而改变这个视图的宽度或高度。

layout_weight 用于给一个线性布局中的多个视图的重要度赋值。所有视图都有layout_weight 值,默认为零,即需要显示多大的视图就占据多大的屏幕空间。如果值大于零,则将父视图中的可用空间分割,分割大小具体取决于每一个视图的layout_weight 值和该值在当前屏幕布局的整体 layout_weight 值,以及在其他视图屏幕布局的layout_weight 值中所占的比例。

在这里,布局中设置了一个TextView,用来配置文本标签Widget,其中设置的属性android:layout_width为整个屏幕的宽度,android:layout_height可以根据文字来改变高度,而android:text则设置了这个TextView要显示的文字内容,这里引用了@string中的hello字符串,即String.xml文件中的hello所代表的字符串资源。hello字符串的内容“Hello World, HelloAndroid!”就是我们在HelloAndroid项目运行时看到的字符串。

最后,我们来分析HelloAndroid项目的主程序文件HelloAndroid.java,如代码清单3-5所示。

代码清单3-5  第2章/HelloAndroid/src/com/yarin/Android/HelloAndroid/HelloAndroid.java

...
 public void onCreate(Bundle savedInstanceState)
 {
  super.onCreate(savedInstanceState);
  /* 设置Activity要显示的布局为(R.layout.main) */ 
  setContentView(R.layout.main);
 }
...
 
主程序HelloAndroid类继承自Activity类,重写了void onCreate(Bundle savedInstanceState)方法。在onCreate方法中通过setContentView(R.layout.main)设置Activity要显示的布局文件(/layout/main.xml)。

到这里,是不是明白了为什么我们在创建项目时没有进行编码就可以直接运行程序呢?当然,这也是Android开发的特点,这样可以很轻松地将代码和UI分开,在国际化和程序维护方面有着巨大的作用。如果你的Android程序需要适应国际化,比如说多国语言等问题,那么就可以定义不同语言的UI布局,在程序装载时调用不同的布局。而且,如果我们需要修改UI的一些问题,就不必查看代码了,直接更改这些布局文件即可,是不是很方便?当然,这需要开发者在开发时使用这种MVC框架,尽量减少使用“硬编码”。笔者个人建议使用这种框架。

3.1.2  Android应用解析

上面我们了解了Android应用程序的目录结构和其中每个文件的功能,要进行应用开发,还需要对Android应用构造进行深入分析。Android应用程序由4个模块构造而成:Activity,Intent,Content Provider,Service。

当然,也不是每个Android应用程序都必须由这4部分组成,可以根据开发者需求进行组合,比如上面建立的HelloAndroid项目就只使用了Activity这一个模块。但是,任何一个应用程序都必须在AndroidManfest.xml文件中声明使用到的这些模块。

1.Activity

Activity是最基本的模块,我们在HelloAndroid项目中已经使用过。我们称之为“活动”,在应用程序中,一个Activity通常就是一个单独的屏幕。每一个活动都被实现为一个独立的类,并且从活动基类中继承而来,活动类将会显示由视图控件组成的用户接口,并对事件作出响应。例如HelloAndroid项目中的HelloAndroid.java即继承了Activity类。大多数的应用都是由多个Activity显示组成,例如,对一个文本信息应用而言,第一个屏幕用来显示发送消息的联系人列表,第二个屏幕用来写文本消息和选择收件人,第三个屏幕查看消息历史或者消息设置操作等。

这里的每一个屏幕就是一个活动,很容易实现从一个屏幕到一个新的屏幕,并且完成新的活动。当一个新的屏幕打开后,前一个屏幕将会暂停,并保存在历史栈中。用户可以返回到历史栈中的前一个屏幕,当屏幕不再使用时,还可以从历史栈中删除。

简单理解,Activity代表一个用户所能看到的屏幕,主要用于处理应用程序的整体性工作,例如,监听系统事件(按键事件、触摸屏事件等),为用户显示指定的View,启动其他Activity等。所有应用的Activity都继承于android.app.Activity类,该类是Android提供的基层类,其他的Activity继承该父类后,通过父类的方法来实现各种功能,这种设计在其他领域也较为常见。

2.Intent

Android用Intent这个特殊类实现在Activity与Activity之间的切换。Intent类用于描述应用的功能。在Intent的描述结构中,有两个最重要的部分:动作和动作对应的数据。典型的动作类型有MAIN、VIEW、PICK、EDIT等,而动作对应的数据则以URI的形式表示。例如,要查看一个人的联系方式,需要创建一个动作类型为VIEW的Intent,以及一个表示这个人的URI。

通过解析各种Intent,从一个屏幕导航到另一个屏幕是很简单的。当向前导航时,Activity将会调用startActivity(IntentmyIntent)方法。然后,系统会在所有已安装的应用程序中定义的IntentFilter中查找,找到最匹配myIntent的Intent对应的Activity。新的Activity接收到myIntent的通知后,开始运行。当startActivity方法被调用时,将触发解析myIntent的动作,该机制提供了两个关键好处:

*  Activity能够重复利用从其他组件中以Intent形式产生的请求。

*  Activity可以在任何时候被具有相同IntentFilter的新的Activity取代。

下面我们举例说明两个Activity之间的切换。运行效果:当应用程序启动时显示布局main.xml,如图3-2所示,当点击“切换”按钮时,屏幕显示布局main2.xml,如图3-3所示,再点击“切换”按钮,又回到如图3-2所示界面。就这样通过Intent完成了两个Activity之间的切换。

            


2   
3

 

                 图3-2  Activity01                              图3-3  Activity02

下面我们来分析一下代码的具体实现,我们知道该项目是由两个Activity构成,在这两个Activity中分别显示了一个文本标签和一个按钮,关于界面的布局会在本书第4章进行详细讲解,要实现两个Activity的跳转,我们可以将要跳转的Activity类名绑定到Intent对象中,然后通过startActivity方法激活Intent对象中所指定的Activity。关键代码如代码清单3-6所示。

代码清单3-6  第3章/Examples_03_01/src/com/yarin/android/Examples_03_01/Activity01.java
/* 监听button的事件信息 */
button.setOnClickListener(new Button.OnClickListener() {
 public void onClick(View v)
 {
  /* 新建一个Intent对象 */
  Intent intent = new Intent();
  /* 指定intent要启动的类 */
  intent.setClass(Activity01.this, Activity02.class);
  /* 启动一个新的Activity */
  startActivity(intent);
  /* 关闭当前的Activity */
  Activity01.this.finish();
 }
});

 
然后,我们要从Activity02跳转到Activity01时,就只是需要在Activity02.java中使用同样的方法返回Activity01中。大家可以参考本书所附代码:第3章/Examples_03_01/src/com/yarin/android/

Examples_03_01/ Activity02.java。值得注意的是,该项目中我们使用了两个Activity,每一个Activity都需要在AndroidManifest.xml文件中进行声明,声明方法如代码清单3-7所示。

代码清单3-7  第3章/Examples_03_01/AndroidManifest.xml
<activity android:name=".Activity01"
          android:label="@string/app_name">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>
<activity android:name="Activity02"></activity>
    

 

如果希望Android应用能够对外部事件(如当电话呼入时,或者数据网络可用时,或者到了晚上时)做出响应,可以使用IntentReceiver。虽然IntentReceiver在感兴趣的事件发生时会使用NotificationManager通知用户,但它并不能生成UI。IntentReceiver在AndroidManifest.xml中注册,但也可以在代码中使用Context.registerReceiver()进行注册。当IntentReceiver被触发时,应用不必对请求调用IntentReceiver,系统会在需要时启动应用。各种应用还可以通过使用Context.broadcastIntent()将它们自己的IntentReceiver广播给其他应用。

3.Content Provider

Android应用能够将它们的数据保存到文件和SQLite数据库中,甚至是任何有效的设备中。当想将应用数据与其他的应用共享时,Content Provider就可以发挥作用了。因为Content Provider类实现了一组标准的方法,能够让其他的应用保存或读取此内容提供器处理的各种数据类型。

数据是应用的核心。在Android中,默认使用鼎鼎大名的SQLite作为系统数据库。但是在Android中,使用方法有点不一样。在Android中,每一个应用都运行在各自的进程中,当一个应用需要访问其他应用的数据时,也就是数据需要在不同的虚拟机之间传递,这样的情况操作起来可能有些困难(正常情况下,不能读取其他应用的db文件),Content Provider正是用来解决在不同的应用包之间共享数据的工具。

在Android中,Content Provider是一个特殊的存储数据的类型,它提供了一套标准的接口用来获取和操作数据。并且,Android自身也提供了现成的Content Provider:Contacts、Browser、CallLog、Settings、MediaStore。应用可以通过唯一的ContentResolver界面来使用具体的某个Content Provider,然后就可以用ContentResolver提供的方法来使用你需要的Content Provider了。其中,ContentResolver提供的方法包括query()、insert()、update()等。要使用这些方法,还会涉及URI。你可以将它理解成string形式的Content Provider的完全路径。

下面我们通过一个例子来学习Content Provider的使用,该例子主要实现通过Content Provider获得电话本中的数据,然后显示到一个TextView中,在运行程序之前我们先看看电话本中存储的电话号码,如图3-4所示,然后再运行程序看看我们获得的数据,如图3-5所示,并看看我们通过Content Provider获得的数据是否正确。

          


4   
5

 

               图3-4  电话本数据                  图3-5  通过ContentProvider获得电话本数据

下面我们来分析一下如何实现通过ContentProvider取得电话本的数据,首先通过getContentResolver方法来取得一个ContentResolver对象,然后通过其query方法查询出符合标准的电话本记录,最后将这些数据都显示在一个TextView中即可,如代码清单3-8所示。

代码清单3-8  第3章/Examples_03_02/src/com/yarin/android/Examples_03_02/Activity01.java

public class Activity01 extends Activity
{
 public void onCreate(Bundle savedInstanceState)
 {
  TextView tv = new TextView(this);
  String string = "";  
  super.onCreate(savedInstanceState); 
  //得到ContentResolver对象
 ContentResolver cr = getContentResolver();  
 //取得电话本中开始一项的光标
 Cursor cursor = cr.query(ContactsContract.Contacts.CONTENT_URI, null, null,
 null, null);
 //向下移动光标
 while(cursor.moveToNext()) 
 { 
  //取得联系人名字
  int nameFieldColumnIndex = cursor.getColumnIndex(PhoneLookup.
  DISPLAY_NAME);     
  String contact = cursor.getString(nameFieldColumnIndex); 
  //取得电话号码
  int numberFieldColumnIndex = cursor.getColumnIndex(PhoneLookup.
  NUMBER);    
  String number = cursor.getString(numberFieldColumnIndex);
  
  string += (contact+":"+number+"/n");
 }
 cursor.close();
  //设置TextView显示的内容
  tv.setText(string);
  //显示到屏幕
  setContentView(tv);
 }
}
 
前面强调过,要使用这些模块,需要在AndroidManifest.xml声明,本例中我们使用了读取联系人的API,因此,声明方式如下所示:
<uses-permission 
 android:name="android.permission.READ_CONTACTS">
 </uses-permission>
 
4.Service

Service即“服务”的意思,既然是服务,那么Service将是一个生命周期长且没有用户界面的程序。比如一个正在从播放列表中播放歌曲的媒体播放器,在这个媒体播放器应用中,应该会有多个Activity,让使用者可以选择歌曲并播放歌曲。然而,音乐重放这个功能并没有对应的Activity,因为使用者会认为在导航到其他屏幕时音乐应该还在播放。在这个例子中,媒体播放器这个Activity会使用Context.startService()来启动一个Service,从而可以在后台保持音乐的播放。同时,系统也将保持这个Service一直执行,直到这个Service运行结束。另外,还可以通过使用Context.bindService()方法连接到一个Service上(如果这个Service当前还没有处于启动状态,则将启动它)。当连接到一个Service之后,还可用Service提供的接口与它进行通信。以媒体播放器为例,我们还可以执行暂停、重播等操作。

下面通过一个例子来学习Service的使用,该例子通过Service来播放一首MP3,如图3-6所示。当用户点击“开始”按钮,音乐开始播放;点击“停止”按钮,停止音乐播放。当然,这里需要在资源文件中添加一首MP3歌曲,如图3-7所示。

要实现音乐的播放,需要在界面中放置两个按钮,用来控制音乐的播放和停止。而我们的音乐播放是通过一个服务来实现的,所以我们可以通过startService和stopService方法来开启和停止这个播放音乐的服务,如代码清单3-9所示。

   


6     
7

         

               图3-6  使用Service播放音乐                       图3-7  test.mp3

代码清单3-9  第3章/Examples_03_03/src/com/yarin/android/Examples_03_03/ Activity01.java
//开始按钮
private OnClickListener start = new OnClickListener()
{
    public void onClick(View v)
    {   
        //开启Service
        startService(new Intent("com.yarin.Android.MUSIC"));
    }
};
//停止按钮
private OnClickListener stop = new OnClickListener()
{
    public void onClick(View v)
    {
            //停止Service
        stopService(new Intent("com.yarin.Android.MUSIC"));       
    }
};

 
下面是该例子的核心内容。如何通过Service来播放音乐,其实也很简单,首先创建一个MusicService继承自Service,然后通过start和stop方法来控制音乐的播放,如代码清单3-10所示。具体实现请参见本书所附代码:第3章/Examples_03_03。

代码清单3-10  第3章/Examples_03_03/src/com/yarin/android/Examples_03_03 /MusicService.java
public class MusicService extends Service
{
 //MediaPlayer对象
 private MediaPlayer player;
 public IBinder onBind(Intent arg0)
 {
  return null;
 }
 public void onStart(Intent intent, int startId)
 {
  super.onStart(intent, startId);
  //这里可以理解为装载音乐文件
  player = MediaPlayer.create(this, R.raw.test);
  //开始播放
  player.start();
 }
 public void onDestroy()
 {
  super.onDestroy();
  //停止音乐—停止Service
  player.stop();
 }
}

 
我们使用Service时同样需要在AndroidManifest.xml中声明,声明方式如代码清单3-11所示。

代码清单3-11  第3章/Examples_03_03 /AndroidManifest.xml
<service android:name=".MusicService">
    <intent-filter>
        <action android:name="com.yarin.Android.MUSIC" />
        <category android:name="android.intent.category.default" />
    </intent-filter>
</service>   

 

3.2  Android的生命周期

在前面的几个例子中,我们发现所有继承自Activity的类都重写了onCreate方法,程序运行就会自动进入这个方法。其实Activity类中还有很多类似于onCreate的方法,比如onStart、onResume、onPause、onDestroy等,而这些方法都是系统自动调用,从名字上大概就可以看出这是一些关于生命周期的方法,那么这些方法被调用的先后顺序是怎样的呢?Android应用的生命周期又是如何呢?下面通过一个例子来进一步分析。

当应用程序启动时,进入如图3-8所示的Activity01界面,此时,点击“Activity02”按钮,进入Activity02界面,如图3-9所示。再点击“Activity01”按钮,返回Activity01界面,最后点击“Exit”按钮退出整个应用程序。

        


8        
9

 

               图3-8  Activity01界面                        图3-9  Activity02界面

我们在这些类似于onCreate的方法中都加入了log函数,输出不同的信息,以便我们能更好地跟踪程序运行的过程,具体实现参见本书所附代码:第3章/Examples_03_04。

首先,我们需要在程序启动所默认的第一个界面中,加入一些Log函数,用于显示和输出Log信息,以帮助我们分析程序的执行流程,如代码清单3-12所示。

代码清单3-12  第3章/Examples_03_04/src/com/yarin/android/Examples_03_04 /Activity01.java

public class Activity01 extends Activity
{
 private static final String TAG = "Activity01";
 public void onCreate(Bundle savedInstanceState)
 {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.main);
  Log.v(TAG, "onCreate");
  
  Button button1 = (Button) findViewById(R.id.button1);
  /* 监听button的事件信息 */
  button1.setOnClickListener(new Button.OnClickListener() {
   public void onClick(View v)
   {
    /* 新建一个Intent对象 */
    Intent intent = new Intent();
    /* 指定intent要启动的类 */
    intent.setClass(Activity01.this, Activity02.class);
    /* 启动一个新的Activity */
    startActivity(intent);
    /* 关闭当前的Activity */
    Activity01.this.finish();
   }
  });
  /******************************/
  Button button3 = (Button) findViewById(R.id.button3);
  /* 监听button的事件信息 */
  button3.setOnClickListener(new Button.OnClickListener() {
   public void onClick(View v)
   {
    /* 关闭当前的Activity */
    Activity01.this.finish();
   }
  });
 }
 public void onStart()
 {
  super.onStart();
  Log.v(TAG, "onStart");
 }
 
 public void onResume()
 {
  super.onResume();
  Log.v(TAG, "onResume");
 }
 
 public void onPause()
 {
  super.onPause();
  Log.v(TAG, "onPause");
 }
 
 public void onStop()
 {
  super.onStop();
  Log.v(TAG, "onStop");
 }
 public void onDestroy()
 {
  super.onDestroy();
  Log.v(TAG, "onDestroy");
 }
 public void onRestart()
 {
  super.onRestart();
  Log.v(TAG, "onReStart");
 }
 
}
 
在第二个界面中,同第一个界面一样,加入一些可以区分的不同的Log信息。

同样需要在AndroidManifest.xml文件中声明所使用的两个Activity模块,如代码清单3-13所示。具体实现请参见本书所附代码:第3章/Examples_03_04。

代码清单3-13  第3章/Examples_03_04/AndroidManifest.xml
<activity android:name=".Activity01"
          android:label="@string/app_name">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>
<activity android:name="Activity02"></activity>
    

 
当在Debug该项目时,切换到DDMS标签即可以看到所打印出来的Log信息,这样就可以很清楚地分析程序的运行过程。

当程序第一次启动时,打印的Log信息如图3-10所示。我们看到程序的运行顺序为:Activity01 onCreate→Activity01 onStart →Activity01 onResume。这里我们可以看到,当一个Activity启动时,不是“创建”之后“开始”就完了,而是要经过“创建”,然后“开始”,最后“重绘”。


10

 
图3-10  第一次启动进入Activity01界面

当我们进入Activity02界面时,打印出的Log信息如图3-11所示。我们看到程序的运行顺序为:Activity01 onPause→Activity02 onCreate→Activity02 onStart→Activity02 onResume→Activity01 onStop→Activity01 onDestroy。这里我们看到,当程序从Activity01界面进入Activity02界面时,并不是马上将Activity01销毁,而是待Activity02启动之后将Activity01停止并销毁。

当我们返回Activity01界面时,打印出的Log信息如图3-12所示。我们看到程序的运行顺序为:Activity02 onPause→Activity01 onCreate→Activity01 onStart→Activity01 onResume→Activity02  onStop→Activity02 onDestroy。这里我们看到,当程序从Activity02界面返回Activity01界面时,并不是马上将Activity02销毁,而是待Activity01启动之后将Activity02停止并销毁。

     


11  
12

 

             图3-11  进入Activity02界面                      图3-12  返回Activity01界面

最后,当我们点击“Exit”按钮退出应用程序时,打印出的Log信息如图3-13所示。这里我们看到程序的运行顺序为:Activity01 onPause→Activity01 onStop→Activity01 onDestroy。这里我们看到当一个应用程序在退出时,并不是马上“停止”且“销毁”,而是经过“暂停”,到“停止”,然后再“销毁”。


13

 
图3-13  退出应用程序

通过上面的例子,我们得出Android应用程序的生命周期如图3-14所示。


14

 
图3-14  Android应用的生命周期

3.3  Android程序UI设计

在前面章节的例子中,我们已经接触了一些UI控件,比如TextView、Button等,其实这里所说的UI就是在我们所说的布局文件,UI是一个应用程序的脸面,一个应用程序要想受用户喜爱, UI可不能差。自从Android SDK 1.0_r2版本开始,ADT提供了UI预览的功能。现在我们只需要打开一个Android项目的“/res/ layout/main.xml”并右键单击,依次选择“Open With”→“Android layout Editor”菜单命令,或者直接双击main.xml文件,即可以切换到UI设计界面,如图3-15所示。


15

 
图3-15  Android Layout Editor命令

左边的Layouts标签的内容则是一些线性布局,可以使用它轻松地完成对布局的排版,比如横向或者纵向布局。Views标签则是一些UI控件,可以将这些控件直接拖动到右边的窗口进行编辑,如图3-16所示。


16

 
图3-16  Android Layout Editor

当然,还可以点击右下角的main.xml标签来切换到XML编辑器,对代码进行编排,如图3-17所示。将这些功能配合起来使用,基本可以满足开发者需求。

除了这个还不算太成熟的UI编辑器之外,笔者曾经使用过一个第三方的工具DroidDraw,DroidDraw是一个公开了源代码的UI设计器,可以根据自己的开发需要进行修改。www. DroidDraw.org提供了在线使用DroidDraw的功能,当然也可以下载到本地来进行编辑,下载地址:
http://code.google.com/p/droiddraw/


17

 
图3-17  XML编辑器

DroidDraw的功能比较强大,可以直接拖动控件到窗口,然后设置其属性、参数等,这样便可以随心所欲地设计自己需要的UI,然后点击“Generate”按钮即可生成出对应的布局代码,同时也可以点击“Load”按钮来载入已经编辑好的布局文件,如图3-18所示。


18

 
图3-18  DroidDraw操作界面
 
3.4  小结

本章主要介绍了Android应用程序框架及其生命周期,以及UI设计。首先彻底分析了上一章的HelloAndroid项目,从其项目目录结构、文件功能等方面分析了Android应用程序的基本框架,其次逐一分析了构成Android应用程序的4个模块(Activity、Intent、Content Provider、Service),分别通过示例程序演示了其功能的运用。接着通过一个示例程序验证了Android应用程序的运行流程,从而得出Android应用程序的生命周期流程图。最后介绍了两个有关UI设计的工具,使得程序界面更加漂亮。

相信通过本章的学习,你已经开始“喜欢”上Android了,有你的这份热情和执着,加上每一章的示例,让你边学边做,理论加实践,轻轻松松学会Android应用开发。加油吧!后面的内容更精彩。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/132878.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

  • PHOTOSHOP MAC快捷键

    PHOTOSHOP MAC快捷键工具箱(多种工具共用一个快捷键的可同时按【Shift】加此快捷键选取)矩形、椭圆选框工具【M】裁剪工具【C】移动工具【V】套索、多边形套索、磁性套索【L】魔棒工具【W】喷枪工具【J】画笔工具【B】像皮图章、图案图章【S】历史记录画笔工具【Y】像皮擦工具【E】铅笔、直线工具【N】模糊、锐化、涂抹工具【R】减淡、加深、海棉工

  • c#中int16 int32 int64区别[通俗易懂]

    c#中int16 int32 int64区别[通俗易懂]c#intInt32Int64的区别int16=short;int32=int;int64=long;Int16值类型表示值介于-32768到+32767之间的有符号整数。Int32值类型表示值介于-2,147,483,648到+2,147,483,647之间的有符号整数。Int64值类型表示值介于-9,223,372

  • fedora系统登录密码_fedora 命令行自动登录

    fedora系统登录密码_fedora 命令行自动登录情况类似于fedora10,fedora11.打开系统工具-终端输入命令:su-输入root密码(此时密码不显示,直接输入)输入命令:gedit/etc/pam.d/gdm在文本编辑器中注释掉”authrequiredpam_succeed_if.souser!=rootquiet”这一行(在这一行前面加上”#”,即改成#authrequ

  • 监督学习——决策树理论与实践(下):回归决策树(CART)[通俗易懂]

    监督学习——决策树理论与实践(下):回归决策树(CART)

  • 完全卸载flash浏览器插件_浏览器内置flash卸载

    完全卸载flash浏览器插件_浏览器内置flash卸载前天晚上不知道点到了什么东西,弹出来个窗口说浏览器的Flash插件需要升级,当时也没多想就确定了,结果发现QQ2009一开就崩溃,囧……找了半天发现问题出在升级的这个Flash插件上面,于是在控制面板中卸载掉,重新安装——一开QQ继续崩溃,继续囧……再查,发现在控制面板中卸载Flash插件之后,在C:\Windows\System32\Macromed\Flash\依旧存在两个文…

    2022年10月15日
  • JAVA中的二维数组的定义及使用[通俗易懂]

    JAVA中的二维数组的定义及使用[通俗易懂]二维数组其实是一位数组的嵌套(每一行看做一个内层的一维数组) 两种初始化形式  格式1:动态初始化数据类型数组名[][]=new数据类型[m][n]数据类型[][] 数组名=new数据类型[m][n]数据类型[] 数组名[]=new数据类型[m][n]举例:int[][] arr=new int[5][3]; 也可以理解为“5行3例…

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号