1、刘 刚 主 编高伟南 副主编8.1 网络编程 网络编程是指多个设备之间通过网络进行数据交换,网络通信基于网络编程是指多个设备之间通过网络进行数据交换,网络通信基于“请求请求-响应模型响应模型”,即一台设备发送通信请求,另一台设备进行反馈。发,即一台设备发送通信请求,另一台设备进行反馈。发送请求端称为客户端,响应请求端称为服务端。例如常见的送请求端称为客户端,响应请求端称为服务端。例如常见的 QQ QQ 程序,用程序,用户打开户打开 QQ QQ 客户端程序之后,输入账号和密码,再单击客户端程序之后,输入账号和密码,再单击“登录登录”,即向腾,即向腾讯服务端发送登录请求,服务端把请求结果反馈到客户
2、端。讯服务端发送登录请求,服务端把请求结果反馈到客户端。Android Android 是基是基于于 Java Java进行开发的,所以进行开发的,所以 JDK JDK 中关于网络编程的中关于网络编程的 API API 在在 Android Android 中均中均可使用。可使用。8.1.1 TCP8.1.1 TCP、UDP UDP 协议基础协议基础 两台设备之间进行通信,一定要有通信协议,即客户端以一定的格式两台设备之间进行通信,一定要有通信协议,即客户端以一定的格式将数据发送出去,服务端接收到数据之后,可以根据同样的协议将数据的内将数据发送出去,服务端接收到数据之后,可以根据同样的协议将数
3、据的内容解析出来。在现有的网络中,通信方式有两种:容解析出来。在现有的网络中,通信方式有两种:TCP TCP 和和 UDP UDP。TCP TCP 是传是传输控制协议,提供的是面向连接、可靠的字节流服务,通信双方必须先建立输控制协议,提供的是面向连接、可靠的字节流服务,通信双方必须先建立一个一个 TCP TCP 连接,然后才能传输数据。而且连接,然后才能传输数据。而且 TCP TCP 协议还提供了超时重发、协议还提供了超时重发、数据校验、拥塞控制等功能,保证了数据的可靠传输。数据校验、拥塞控制等功能,保证了数据的可靠传输。UDP UDP 是用户数据报是用户数据报协议,只是简单地把数据报发送出去
4、,但是并不能保证数据能到达目的地,协议,只是简单地把数据报发送出去,但是并不能保证数据能到达目的地,不提供可靠性。所以不提供可靠性。所以 TCP TCP 应用于需要安全可靠传输数据的场景,但是开销应用于需要安全可靠传输数据的场景,但是开销比较大。比较大。UDP UDP 应用于对数据可靠性要求不是太高的传输,其优点是开销小,应用于对数据可靠性要求不是太高的传输,其优点是开销小,另外另外 UDP UDP 没有数据校验、拥塞控制等操作,故而传输比较快。没有数据校验、拥塞控制等操作,故而传输比较快。对于上层应用来说,无论是对于上层应用来说,无论是 TCP TCP 还是还是 UDP UDP,通信都包括客
5、户端和服务,通信都包括客户端和服务端,处理流程也具有一般性,具体如表端,处理流程也具有一般性,具体如表 8.1 8.1 所示。所示。表 8.1 客户端和服务端的处理流程8.1.2 Socket 8.1.2 Socket 通信通信 对于对于 TCP TCP 和和 UDP UDP 的通信,的通信,Java Java 均有对应的均有对应的 API API。现在我们以。现在我们以 TCP TCP 为例,为例,通过一个实例说明网络通信的方式。通过一个实例说明网络通信的方式。对于对于TCPTCP通信,通信,Java Java 中使用中使用 Socket Socket 类做客户端开发,使用类做客户端开发,使
6、用 ServerSocket ServerSocket 做服务端开发。做服务端开发。8.1.3 8.1.3 下载网络资源下载网络资源 在在 Android Android 开发中,经常需要请求网络资源,例如播放在线音乐,或开发中,经常需要请求网络资源,例如播放在线音乐,或者加载显示一张网络图片。者加载显示一张网络图片。Java Java 提供了提供了 HttpURLConnection HttpURLConnection 和和 HttpsURLConnection HttpsURLConnection,两者都,两者都可以基于可以基于 URL URL 实现简单的请求响应功能,区别在于是访问实现简
7、单的请求响应功能,区别在于是访问 http http 链接还是链接还是访问访问 https https 链接。链接。本例通过本例通过 HttpURLConnection HttpURLConnection 获取网络图片。获取网络图片。p 案例分析案例分析 代码中首先根据网络图片的链接地址创建了一个代码中首先根据网络图片的链接地址创建了一个 URL URL 对象,然后调用对象,然后调用 openConnection()openConnection()方法开启一个连接,接着设置相关参数,如超时时间等。方法开启一个连接,接着设置相关参数,如超时时间等。然后调用然后调用 getInputStream(
8、)getInputStream()方法获取输入流,后续就是输入方法获取输入流,后续就是输入/输出流的基输出流的基本处理,可以调用本处理,可以调用 BitmapFactory BitmapFactory 的的 decodeStream()decodeStream()方法将输入流解析方法将输入流解析成成 Bitmap Bitmap 图片。图片。8.2 图形图像和动画 对于一个应用来说,图片是一种很丰富的表达形式。对于一个应用来说,图片是一种很丰富的表达形式。Android Android 中也为中也为图片的处理提供了大量的图片的处理提供了大量的 API API,不仅包括图片的显示、绘制,还包括一些
9、,不仅包括图片的显示、绘制,还包括一些简单的动画效果。本节我们将介绍这些简单的动画效果。本节我们将介绍这些 API API 的使用方法。的使用方法。8.2.1 Bitmap 8.2.1 Bitmap 和和 BitmapFactory BitmapFactory Android Android 中提供了中提供了 Bitmap Bitmap 类用于图片处理,一个类用于图片处理,一个 Bitmap Bitmap 对象代表对象代表一张位图,存储了图片的宽高、颜色、像素点等信息,一张位图,存储了图片的宽高、颜色、像素点等信息,Bitmap Bitmap 类提供了大类提供了大量的方法,其常见的方法如表量的
10、方法,其常见的方法如表 8.2 8.2 所示。所示。表 8.2 Bitmap 常见的方法续表 BitmapFactory BitmapFactory 主要用于加载主要用于加载 Bitmap Bitmap 对象,可以从资源文件加载,对象,可以从资源文件加载,也可以根据图片的路径进行加载,还可以根据输入流对也可以根据图片的路径进行加载,还可以根据输入流对 Bitmap Bitmap 对象进行对象进行解析,相应的方法如表解析,相应的方法如表 8.3 8.3 所示。所示。表 8.3 BitmapFactory 的方法8.2.2 Android 8.2.2 Android 绘图基础绘图基础 除了显示已有
11、的图片之外,除了显示已有的图片之外,Android Android 还支持一些简单的二维绘图,其实还支持一些简单的二维绘图,其实对于对于 Android Android 的一些基本组件,如的一些基本组件,如 TextView TextView、Button Button 等,也都是系统绘制等,也都是系统绘制出来的,绘制的操作在出来的,绘制的操作在 View View 类的类的 onDraw(Canvas canvas)onDraw(Canvas canvas)方法中,每个组方法中,每个组件需要实现件需要实现 onDraw(Canvas canvas)onDraw(Canvas canvas)方
12、法进行自定义的绘制。所以,方法进行自定义的绘制。所以,Android Android 的绘图应该定义一个类继承自的绘图应该定义一个类继承自 View View 组件,并重新定义组件,并重新定义 onDraw(Canvas onDraw(Canvas canvas)canvas)方法。其中,参数方法。其中,参数 Canvas Canvas 可以理解为画布,绘制操作均在可以理解为画布,绘制操作均在 Canvas Canvas 上执行。上执行。Canvas Canvas 支持的一些方法如表支持的一些方法如表 8.4 8.4 所示。所示。表 8.4 Canvas 类支持的操作举例续表 从表从表 8.4
13、 8.4 中可以看到,每一个方法中都包含一个中可以看到,每一个方法中都包含一个 Paint Paint 类的参数,类的参数,Paint Paint 类代表画笔,它指定了画笔的颜色和粗细等,相关的方法如表类代表画笔,它指定了画笔的颜色和粗细等,相关的方法如表 8.5 8.5 所示。所示。表 8.5 Paint 类相关的方法 例说明绘制例说明绘制 API API 的方法,运行结果如图的方法,运行结果如图 8.1 8.1 所示。所示。图 8.1 图形的绘制8.2.3 8.2.3 补间动画补间动画 在在 Android Android 应用中,经常会出现一些动画效果,例如控件的滑入滑出、应用中,经常会
14、出现一些动画效果,例如控件的滑入滑出、图片的渐隐等。常见的实现方式有补间动画和属性动画,补间动画是指开发图片的渐隐等。常见的实现方式有补间动画和属性动画,补间动画是指开发者指定好控件的初始状态和结束状态,系统自动补齐显示控件的中间状态。者指定好控件的初始状态和结束状态,系统自动补齐显示控件的中间状态。Android Android 补间动画支持的效果比较简单,包括平移、缩放、旋转、透明度补间动画支持的效果比较简单,包括平移、缩放、旋转、透明度变化,对应的类如表变化,对应的类如表 8.6 8.6 所示。所示。表 8.6 补间动画对应的类本例在本例在 Activity Activity 上显示一张
15、图片和上显示一张图片和 4 4 个按钮,个按钮,4 4 个按钮分别用于触个按钮分别用于触发发 4 4 种动画,运行结果如图种动画,运行结果如图 8.2 8.2 所示。所示。图 8.2 补间动画实例8.2.4 8.2.4 属性动画属性动画 属性动画是在属性动画是在 API 11 API 11 之后加入的功能,它几乎可以作用在任何对象上,之后加入的功能,它几乎可以作用在任何对象上,而且不同于补间动画只能支持而且不同于补间动画只能支持 4 4 种变换,属性动画是在一定时间内将对象的种变换,属性动画是在一定时间内将对象的属性从一个初始值改变到另一个值,因此,只要是对象存在的属性,无论是可属性从一个初始
16、值改变到另一个值,因此,只要是对象存在的属性,无论是可见还是不可见的,都可以实现动画效果。属性动画可以通过见还是不可见的,都可以实现动画效果。属性动画可以通过 ObjectAnimator ObjectAnimator实现。下面我们通过一个实例说明一下属性动画的具体属性。实现。下面我们通过一个实例说明一下属性动画的具体属性。本例在本例在 Activity Activity 中定义了两个按钮,一个用于触发图片的平移,另一个用中定义了两个按钮,一个用于触发图片的平移,另一个用于触发图片的透明度的变换。于触发图片的透明度的变换。p 案例分析案例分析 单击按钮后,使用单击按钮后,使用 ObjectAn
17、imator ObjectAnimator 类实现属性动画,首先调用类实现属性动画,首先调用 ofFloat()ofFloat()方法获取方法获取 ObjectAnimator ObjectAnimator 对象,对象,ofFloat()ofFloat()方法第方法第 1 1 个参数为需要变换的对象,个参数为需要变换的对象,第第 2 2 个参数为需要改变的属性,个参数为需要改变的属性,“translationX”“translationX”表示水平方向上的平移,表示水平方向上的平移,“alpha”“alpha”为透明度的改变,其他的还有为透明度的改变,其他的还有“rotationX”“rota
18、tionY”“rotationX”“rotationY”等,设置等,设置好需要控制的对象和属性之后,调用好需要控制的对象和属性之后,调用 setDuration()setDuration()设置动画执行的时间,最后设置动画执行的时间,最后调用调用 start()start()方法执行动画。方法执行动画。8.3 多媒体应用开发 在在 Android Android 应用中,经常需要播放媒体资源,如音乐播放器、视频播应用中,经常需要播放媒体资源,如音乐播放器、视频播放器和游戏的背景音乐等。放器和游戏的背景音乐等。Android Android 提供了提供了 MediaPlayer MediaPla
19、yer 类,可以很简单类,可以很简单地实现播放本地存储或者网络上的音地实现播放本地存储或者网络上的音/视频文件。视频文件。8.3.1 MediaPlayer 8.3.1 MediaPlayer 类介绍类介绍 MediaPlayer MediaPlayer 提供了大量的方法可以控制音提供了大量的方法可以控制音/视频的播放、暂停、定位视频的播放、暂停、定位等,常见的方法如表等,常见的方法如表 8.7 8.7 所示。所示。表 8.7 MediaPlayer 类的方法续表 使用使用 MediaPlayer MediaPlayer 播放音播放音/视频时,首先调用视频时,首先调用 setDataSourc
20、e()setDataSource()方法方法设置需要播放的视频源,然后调用设置需要播放的视频源,然后调用 prepare()prepare()或或 prepareAsync()prepareAsync()方法做视方法做视频播放的准备工作。两个方法的区别在于频播放的准备工作。两个方法的区别在于 prepareAsync()prepareAsync()是异步方法,不是异步方法,不会阻塞程序的执行,可以通过调用会阻塞程序的执行,可以通过调用 setOnPreparedListener()setOnPreparedListener()方法监听播放方法监听播放器是否已经准备好,准备好之后就可以调用器是否
21、已经准备好,准备好之后就可以调用 start()start()方法进行播放。方法进行播放。8.3.2 8.3.2 使用使用MediaPlayerMediaPlayer和和SurfaceViewSurfaceView播放视频播放视频 播放的视频必须显示在播放的视频必须显示在ViewView上,而且视频的画面一直在改变,所以上,而且视频的画面一直在改变,所以ViewView需要一需要一直重绘。对于这种需要不断更新直重绘。对于这种需要不断更新ViewView内容的场景,内容的场景,Android Android 提供了提供了 SurfaceView SurfaceView 类类 ,SurfaceVi
22、ew SurfaceView 关联了一个关联了一个SurfaceHolder SurfaceHolder 对象,专门用于绘制对象,专门用于绘制SurfaceView SurfaceView 的内容。的内容。SurfaceHolder SurfaceHolder 会有会有 3 3 个回调方法反馈个回调方法反馈SurfaceView SurfaceView 的状态,具体如表的状态,具体如表 8.8 8.8 所示。所示。表 8.8 SurfaceHolder 的回调方法本例实现视频播放,单击本例实现视频播放,单击“暂停暂停”按钮时,暂停播放视频;单击按钮时,暂停播放视频;单击“播播放放”按钮时,重新
23、开始播放视频。最终可以退出播放页面。按钮时,重新开始播放视频。最终可以退出播放页面。p 案例分析案例分析 实例中首先获取了实例中首先获取了 SurfaceView SurfaceView 对象,然后调用对象,然后调用 getHolder()getHolder()方法获取方法获取关联的关联的 SurfaceHolder SurfaceHolder 对象,设置监听事件,当对象,设置监听事件,当 SurfaceView SurfaceView 创建完成之创建完成之后将后将 SurfaceHolder SurfaceHolder 设置给设置给 MediaPlayer MediaPlayer。然后调用。
24、然后调用setDataSource()setDataSource()方法方法设置视频源,为设置视频源,为 MediaPlayer MediaPlayer 设置监听事件。接着调用设置监听事件。接着调用 prepareAsync()prepareAsync()方方法做法做MediaPlayer MediaPlayer 的准备工作,准备完成之后会回调的准备工作,准备完成之后会回调 onPrepared()onPrepared()方法,在方法,在其中开始播放视频。单击其中开始播放视频。单击“暂停暂停”按钮时,调用按钮时,调用 pause()pause()方法暂停播放视频;方法暂停播放视频;单击单击“播
25、放播放”按钮时,调用按钮时,调用 play()play()方法重新开始播放视频。方法重新开始播放视频。退出播放页面时,调用退出播放页面时,调用 release()release()方法释放方法释放 MediaPlayer MediaPlayer 占用的资源。占用的资源。8.4 线程开发 线程在程序开发中是一个很重要的概念,在线程在程序开发中是一个很重要的概念,在 Android Android 中,线程分为主中,线程分为主线程和子线程。主线程又叫作线程和子线程。主线程又叫作 UI UI 线程,主要处理和界面有关的事情,用于线程,主要处理和界面有关的事情,用于界面的绘制和交互。对于用户来说,随时
26、都有可能操作页面,而且对响应速界面的绘制和交互。对于用户来说,随时都有可能操作页面,而且对响应速度要求较高,因此,主线程中不能做太耗时的操作,否则会给用户视觉上造度要求较高,因此,主线程中不能做太耗时的操作,否则会给用户视觉上造成卡顿的现象,甚至有可能会因为执行阻塞产生成卡顿的现象,甚至有可能会因为执行阻塞产生 ANR ANR(Application Not Application Not RespondingResponding)而导致应用异常退出。)而导致应用异常退出。Android Android 基于基于 Java Java 开发,所以对于线程也有很好的支持,除了开发,所以对于线程也有
27、很好的支持,除了 JDK JDK 中支持的一些中支持的一些 API API 之外,之外,Android Android 也针对自身的机制做了一些拓展。下面也针对自身的机制做了一些拓展。下面我们将介绍两个常用的类:我们将介绍两个常用的类:AsyncTask AsyncTask 和和 ThreadPoolExecutor ThreadPoolExecutor。8.4.1 AsyncTask 8.4.1 AsyncTask 及其使用及其使用 AsyncTask AsyncTask 是一个执行异步操作的类,它在主线程中创建和触发,但是是一个执行异步操作的类,它在主线程中创建和触发,但是在子线程中执行后
28、台任务,然后将执行的进度和最终结果传递给主线程并在在子线程中执行后台任务,然后将执行的进度和最终结果传递给主线程并在主线程中更新主线程中更新 UI UI。例如有一种很常见的操作,应用中如果要在。例如有一种很常见的操作,应用中如果要在 ImageView ImageView 控件中显示一张网络图片,首先需要从网络上下载图片,然后显示到控件上。控件中显示一张网络图片,首先需要从网络上下载图片,然后显示到控件上。图片的下载依赖于网络情况,如果在主线程中执行下载操作,可能会造成用图片的下载依赖于网络情况,如果在主线程中执行下载操作,可能会造成用户长时间等待,并且不能及时响应用户的操作。这时就可以使用户
29、长时间等待,并且不能及时响应用户的操作。这时就可以使用 AsyncTask AsyncTask类在子线程中执行下载操作,然后通知主线程下载完成并显示。类在子线程中执行下载操作,然后通知主线程下载完成并显示。AsyncTaskAsyncTask有有 3 3 个主要的回调方法,如表个主要的回调方法,如表 8.9 8.9 所示。所示。表 8.9 AsyncTask 的主要回调方法8.4.2 ThreadPoolExecutor 8.4.2 ThreadPoolExecutor 介绍介绍 当一个应用中需要创建多个线程时,可以将其放入线程池中进行管理,当一个应用中需要创建多个线程时,可以将其放入线程池中
30、进行管理,Java Java 中使用中使用 Executor Executor 做线程池的管理和线程的调度。做线程池的管理和线程的调度。Executor Executor 是一个接口,它的实是一个接口,它的实现类为现类为 ThreadPoolExecutor ThreadPoolExecutor。创建。创建 ThreadPoolExecutor ThreadPoolExecutor 对象时,可以向构造对象时,可以向构造函数传入一系列的参数来配置线程池。常用的构造函数有函数传入一系列的参数来配置线程池。常用的构造函数有 ThreadPoolExecutor ThreadPoolExecutor(
31、int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue workQueue,ThreadFactory threadFactory)unit,BlockingQueue workQueue,ThreadFactory threadFactory)。其中,其中,corePoolSizecorePoolSize是指线程池的核心线程数,是指线程池的核心线程数,m
32、aximumPoolSizemaximumPoolSize指线程池可以指线程池可以容纳的最大的线程个数,容纳的最大的线程个数,keepAliveTimekeepAliveTime指非核心线程在空闲时可以存活的时间,指非核心线程在空闲时可以存活的时间,TimeUnitTimeUnit是参数是参数keepAliveTimekeepAliveTime的单位,的单位,workQueueworkQueue是指线程队列,是指线程队列,ThreadFactoryThreadFactory指线程工厂,用于线程池创建线程。为了帮助读者理解以上各个参指线程工厂,用于线程池创建线程。为了帮助读者理解以上各个参数的含
33、义,下面我们说明一下线程池的工作步骤。数的含义,下面我们说明一下线程池的工作步骤。(1 1)当调用了)当调用了 ThreadPoolExecutor ThreadPoolExecutor 的的 execute()execute()方法要求执行一个任务方法要求执行一个任务时,线程池会判断当前的线程个数是否超过核心线程数,如果没有超过,则时,线程池会判断当前的线程个数是否超过核心线程数,如果没有超过,则启动一个核心线程来执行任务。启动一个核心线程来执行任务。(2 2)如果当前的线程个数超过了核心线程的个数,则将任务放到线程队列中)如果当前的线程个数超过了核心线程的个数,则将任务放到线程队列中排队等
34、候。排队等候。(3 3)如果线程队列已满,则启动一个非核心线程来执行任务。)如果线程队列已满,则启动一个非核心线程来执行任务。(4 4)如果线程的总个数已经达到了线程池最大的线程个数,则拒绝接收任务。)如果线程的总个数已经达到了线程池最大的线程个数,则拒绝接收任务。(5 5)如果线程池有线程处于空闲状态,则在指定)如果线程池有线程处于空闲状态,则在指定 keepAliveTime keepAliveTime 后回收线后回收线程,当线程个数等于核心线程数后,停止回收线程。程,当线程个数等于核心线程数后,停止回收线程。8.5 Fragment 为了更加动态和灵活地支持为了更加动态和灵活地支持 UI
35、 UI 设计,设计,Android Android 在在 API 11 API 11 版本之后引版本之后引入了入了 Fragment Fragment,它可以将,它可以将 UI UI 碎片化,也可以被复用。碎片化,也可以被复用。Fragment Fragment 必须显示必须显示在在 Activity Activity 中,可以被动态地添加、移除、替换,但是每个中,可以被动态地添加、移除、替换,但是每个 Fragment Fragment 也也都具有自己的生命周期方法,并且可以各自处理用户的输入事件。常见的微信都具有自己的生命周期方法,并且可以各自处理用户的输入事件。常见的微信界面就可以使用界
36、面就可以使用 Fragment Fragment 来实现,如图来实现,如图 8.3 8.3 所示,单击下面的所示,单击下面的“微信微信”,上面就会对应显示聊天记录,单击上面就会对应显示聊天记录,单击“通讯录通讯录”,上面就会显示联系人列表。如,上面就会显示联系人列表。如果使用果使用 Activity Activity 实现比较麻烦,但是如果使用实现比较麻烦,但是如果使用 Fragment Fragment 实现就比较简单,实现就比较简单,界面的整体定义为一个界面的整体定义为一个 Activity Activity,中间的内容区域设计为一个,中间的内容区域设计为一个 Fragment Fragm
37、ent,可以根据用户单击的可以根据用户单击的 Item Item 动态地更换显示区域的动态地更换显示区域的 Fragment Fragment。图 8.3 微信界面8.5.1 Fragment 8.5.1 Fragment 的创建的创建 和和 Activity Activity 类似,创建自定义的类似,创建自定义的 Fragment Fragment 需要继承自需要继承自 Fragment Fragment 类,并实现父类的相关回调方法。其中比较常见的是类,并实现父类的相关回调方法。其中比较常见的是 onCreate()onCreate()、onCreateView()onCreateView
38、()、onPause()onPause()方法。方法。onCreate()onCreate()方法在创建方法在创建 Fragment Fragment 时时会回调;会回调;onCreateView()onCreateView()在绘制在绘制 Fragment Fragment 视图的时候会回调,开发者需视图的时候会回调,开发者需要在该方法中加载要在该方法中加载 Fragment Fragment 需要显示的布局文件;需要显示的布局文件;onPause()onPause()方法在用户方法在用户离开离开 Fragment Fragment 时会回调。另外,时会回调。另外,Android Andro
39、id 通过通过 FragmentManager FragmentManager 类管类管理在理在Activity Activity 中的中的 Fragment Fragment,具体的操作在,具体的操作在 FragmentTransaction FragmentTransaction 中。中。首先可以通过首先可以通过 getFragmentManager()getFragmentManager()方法获取方法获取 FragmentManager FragmentManager 对象,对象,然后调用然后调用 FragmentManager FragmentManager 的的 beginTran
40、saction()beginTransaction()开启一个事务执行开启一个事务执行具体的操作。具体的操作。FragmentTransaction FragmentTransaction 类支持的常见操作如表类支持的常见操作如表 8.10 8.10 所示。所示。表 8.10 FragmentTransaction 类支持的操作本例运行结果如图本例运行结果如图 8.4 8.4 所示,单击下面的按钮,上面的内容会动态所示,单击下面的按钮,上面的内容会动态改变。改变。图 8.4 Fragment 实例p 案例分析案例分析 本例为每一种内容都定义了一个本例为每一种内容都定义了一个FragmentFr
41、agment,在,在FragmentFragment的的onCreateView()onCreateView()方方法中可以加载自己想要显示的布局文件,布局的处理并无特殊。然后在法中可以加载自己想要显示的布局文件,布局的处理并无特殊。然后在 Activity Activity 中可以动态地控制中可以动态地控制 Fragment Fragment 的显示,非常灵活。的显示,非常灵活。8.5.2 Fragment 8.5.2 Fragment 的生命周期的生命周期 Fragment Fragment 有自己的生命周期,但是其生命周期又依赖于有自己的生命周期,但是其生命周期又依赖于 Activity
42、 Activity。当当 Activity Activity 处于不可见状态时,处于不可见状态时,Fragment Fragment 也一定处于不可见状态。当也一定处于不可见状态。当 Activity Activity 处于销毁状态时,处于销毁状态时,Fragment Fragment 也一定处于销毁状态。也一定处于销毁状态。Fragment Fragment 在各个生命周期阶段也有不同的回调方法,具体流程如图在各个生命周期阶段也有不同的回调方法,具体流程如图 8.5 8.5 所示。所示。图 8.5 Fragment 的生命周期回调方法 从图从图 8.5 8.5 中可以看出,中可以看出,Fra
43、gment Fragment 的生命周期方法有些和的生命周期方法有些和 Activity Activity 一致,意义也一致。表一致,意义也一致。表 8.11 8.11 用于说明用于说明 Fragment Fragment 特有的生命周期方法的特有的生命周期方法的含义。含义。表 8.11 Fragment 的生命周期方法说明续表 本例在本例在ActivityActivity中显示两个按钮,单击其中一个按钮,将一个中显示两个按钮,单击其中一个按钮,将一个Fragment Fragment 添加到添加到 Activity Activity 中,单击另外一个按钮,将中,单击另外一个按钮,将 Frag
44、ment Fragment 从从 Activity Activity 中移中移除,界面如图除,界面如图 8.6 8.6 所示。所示。图 8.6 Fragment 生命周期实例界面p 案例分析案例分析 单击单击 Activity Activity 中的中的“add”“add”按钮,将按钮,将 Fragment Fragment 添加到添加到 Activity Activity 中,控制台打印如图中,控制台打印如图 8.7 8.7 所示。依次回调了所示。依次回调了 onAttach()onAttach()、onCreate()onCreate()、onCreateView()onCreateVie
45、w()、onActivityCreated()onActivityCreated()、onStart()onStart()、onResume()onResume()方法,方法,FragmentFragment处于前台可见。处于前台可见。按按“Home”“Home”键,使键,使 Activity Activity 处于后台不可见状态,处于后台不可见状态,Fragment Fragment 也随也随之处于不可见状态,控制台打印如图之处于不可见状态,控制台打印如图 8.8 8.8 所示。依次回调了所示。依次回调了 onPause()onPause()和和 onStop()onStop()方法。方法。
46、单击单击 Activity Activity 中的中的“remove”“remove”按钮,将按钮,将 Fragment Fragment 移除,控制台打移除,控制台打印如图印如图 8.9 8.9 所示,依次回调了所示,依次回调了onPause()onPause()、onStop()onStop()、onDestroyView()onDestroyView()、onDestroy()onDestroy()、onDetach()onDetach()方法。方法。图 8.7 添加 Fragment图 8.8 按“Home”键图 8.9 Fragment 销毁8.6 RecyclerView 在在 A
47、ndroid Android 应用中,很多时候需要显示大量的数据,例如音乐播放器的应用中,很多时候需要显示大量的数据,例如音乐播放器的歌曲列表,手机中可能存在大量的歌曲文件,但是同一时刻屏幕上只显示部分歌曲列表,手机中可能存在大量的歌曲文件,但是同一时刻屏幕上只显示部分歌曲,用户可以通过上下滑动查看更多音乐。这种应用场景可以使用歌曲,用户可以通过上下滑动查看更多音乐。这种应用场景可以使用 2.2 2.2 节节介绍的介绍的 ListView ListView 和和 GridView GridView 实现布局。实现布局。但是,但是,Android Android 还提供了一种更灵活的组件还提供了
48、一种更灵活的组件RecyclerViewRecyclerView,它可以,它可以动态地实现列表布局或网格布局,甚至每个动态地实现列表布局或网格布局,甚至每个 Item Item 都可以显示不同的布局文件。都可以显示不同的布局文件。而且,而且,RecyclerView RecyclerView 内部还实现了内部还实现了 View View 的复用,在用户上下滑动列表或网的复用,在用户上下滑动列表或网格时,格时,RecyclerView RecyclerView 并不会为每一个子项创建一个并不会为每一个子项创建一个 View View,而是创建若干个,而是创建若干个 View View 不断复用,
49、更新其中显示的数据。不断复用,更新其中显示的数据。8.6.1 RecyclerView 8.6.1 RecyclerView 相关类相关类 在在 RecyclerView RecyclerView 视图中,每个子项都被表示为视图中,每个子项都被表示为 ViewHolder ViewHolder 对象,对象,开发者必须继承开发者必须继承RecyclerView.ViewHolder RecyclerView.ViewHolder 类实现自定义的类实现自定义的 ViewHolder ViewHolder 类。每个类。每个 ViewHolder ViewHolder 都可以有一个布局文件用于显示一个
50、子项。例如,都可以有一个布局文件用于显示一个子项。例如,如果用如果用 RecyclerView RecyclerView 显示音乐列表,则每一个显示音乐列表,则每一个 ViewHolder ViewHolder 可以代表一可以代表一首歌曲,用来显示歌曲的歌手信息、时长信息等,还可以响应单击和长按事首歌曲,用来显示歌曲的歌手信息、时长信息等,还可以响应单击和长按事件等。件等。RecyclerView RecyclerView 通过通过 Adapter Adapter 管理管理 ViewHolder ViewHolder,开发者必须继承,开发者必须继承 RecyclerView.AdapterRe