第3章 Activity和界面布局 Activity是Android应用程序最主要的展示窗口。本章首先介绍Android应用的组成 和有关Activity的基础知识;其次介绍基于XML文件完成Activity布局的方法、在Activit 中通过Java编程方式设定布局的方法,以及Android的资源管理与使用方法;最后介绍常用 y 的布局方式,内容涉及线性布局、相对布局、表格布局、网格布局、帧布局、约束布局。 3.1 Activity及其生命周期 3.1.1 Android 应用的基本组件 一般来说,Android应用程序由Activity、ContentProvider、Service、BroadcastReceiver等 组成。当然,有些应用程序可能只包含其中部分而非全部。它们在Andinfs.l清 单文件中以不同的XML标签声明后,才可以在应用程序中使用。 rodMaietxm 1.Activity Activity一般含有一组用于构建用户界面(UI)的Widget控件,如按钮Buton、文本 框EditText、列表ListView等,实现与用户的交互,相当于Windows应用程序的窗口或 网络应用程序的Web页面。一个功能完善的Android应用程序一般由多个Activity构 成,这些Activity之间可互相跳转,可进行页面间的数据传递。例如,显示一个E-mail通 讯簿列表的界面就是一个Activity,而编辑通讯簿界面则是另一个Activity。 2.ContentProvider ContentProvider是Android系统提供的一种标准的数据共享机制。在Android平 台下,一个应用程序使用的数据存储都是私有的,其他应用程序是不能访问和使用的。私 有数据可以是存储在文件系统中的文件,也可以是SQLite中的数据库。当需要共享数据 时,ContentProvider提供了应用程序之间数据交换的机制。一个应用程序通过实现一个 ContentProvider的抽象接口将自己的数据暴露出去,并且隐蔽了具体的数据存储实现, 这样既实现了应用程序内部数据的保密性,又能够让其他应用程序使用这些私有数据,同 时实现了权限控制,保护了数据交互的安全性。 第3章 Activity和界面布局 61 3.Service Service是相对于Activity独立且可以保持后台运行的服务,相当于一个在后台运行的 没有界面的Activity。如果应用程序并不需要显示交互界面但却需要长时间运行,就需要使 用Service。例如,在后台运行的音乐播放器,为了避免音乐播放器在后台运行时被终止而停 播,需要为其添加Service,通过调用Context.startService()方法,让音乐播放器一直在后台运 行,直到使用者再调出音乐播放器界面并关掉它为止。用户可以通过StartService()方法启 动一个Service,也可以通过bindService()方法来绑定一个Service并启动它。 4.BroadcastReceiver 在Android系统中,广播是一种广泛运用的在应用程序之间传输信息的机制。而 BroadcastReceiver是用来接收并响应广播消息的组件,不包含任何用户界面。它可以通 过启动Activity或者Notification通知用户接收重要信息。Notification能够通过多种方 法提示用户,包括闪动背景灯、振动设备、发出声音或在状态栏上放置一个持久的图标。 Activity、Service和BroadcastReceiver都是由Intent异步消息激活的。Intent用于 连接以上各个组件,并在其间传递消息。例如,广播机制一般通过下述过程实现:首先在 需要发送信息的地方,把要发送的信息和用于过滤的信息(如Action、Category)装入一个 Intent对象,然后通过调用Context.sendBroadcast()或sendOrderBroadcast()方法,把 Intent对象以广播方式发送出去。Android系统会对所有已经注册的BroadcastReceiver 检查其Intent过滤器(IntentFilter)是否与发送的Intent相匹配,若匹配就会调用其 onReceive()方法,接收广播并响应。例如,对于一个电话程序,当有来电时,电话程序就 自动使用BroacastReceiver取得对方的来电消息并显示。使用Intent还可以方便地实现 各个Activity间的跳转和数据传递。 3.1.2 什么是Activity Activity是Android四大组件中最基本的组件,是Android应用程序中最常用也是 最重要的部分。在应用程序中,用户界面主要通过Activity呈现,包括显示控件、监听和 处理用户的界面事件并做出响应。Activity在界面上的表现形式有全屏窗体、非全屏悬 浮窗体、对话框等。在模拟器上运行应用程序时,可以按HOME 键或回退键退出当前 Activity。 对于大多数与用户交互的程序来说,Activity是必不可少的,也是非常重要的。刚开 始接触Android应用程序时,可以暂且将Activity简单地理解为用户界面。新建一个 Android项目时,系统默认生成一个启动的主Activity,其默认的类名为MainActivity,源 码文件中的主要内容如代码段3-1所示。 代码段3-1 MainActivity 源代码 package edu.hebust.zxm.myfirstapplication; import androidx.appcompat.app.AppCompatActivity; //AppCompatActivit 是android.app.Activity 的子类 62 Android软件开发教程(第3版·微课版) import android.os.Bundle; //用于映射字符串值 public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //设置布局,它加载了res/layout/activity_main.xml 中定义的界面元素 } } 应用程序中的每个Activity都必须继承自android.app.Activity类并重写(Override) 其OnCreate()方法。 Activity通常要与布局资源文件(res/layout目录下的XML 文件)相关联,并通过 setContentView()方法将布局呈现出来。在Activity类中通常包含布局控件的显示、界 面交互设计、事件的响应设计以及数据处理设计、导航设计等内容。 一个Android应用程序可以包含一个或多个Activity,一般在程序启动后会首先呈 现一个主Activity,用于提示用户程序已经正常启动并显示一个初始的用户界面。需要 注意的是,应用程序中的所有Activity都必须在AndroidManifest.xml文件中添加相应 的声明,并根据需要设置其属性和<intent-filter>。例如,代码段3-2 含有对两个 Activity(MainActivity和SecondActivity)的声明,代码中有两个<activity>元素,第一 个为系统默认生成的MainActivity,第二个是新建的SecondActivity,其中的 MainActivity是程序入口。 代码段3-2 AndroidManifest.xml 文件中的声明 <application android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" > <activity android:name="edu.hebust.zxm.myfirstapplication.MainActivity" android:exported="true" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activity android:name="edu.hebust.zxm.myfirstapplication.SecondActivity" android:label="SecondActivity" > </activity> </application> 第3章Activity和界面布局63 3.1.3 Activity 的生命周期 所有Android组件都具有自己的生命周期,生命周期是指从组件建立到组件销毁的 整个过程。在生命周期中,组件会在可见、不可见、活动、非活动等状态中不断变化。 Activity的生命周期指Activity从启动到销毁的过程。生命周期由系统控制,程序 无法改变,但可以用onSaveInstanceState()方法保存其状态。了解Activity的生命周期 有助于理解Activity的运行方式和编写正确的Activity代码。 Activity在生命周期中表现为4种状态,分别是活动状态、暂停状态、停止状态和非 活动状态。处于活动状态时,Activity在用户界面中处于最上层,能完全被用户看到,并 与用户进行交互。处于暂停状态时,Activity在界面上被部分遮挡,该Activity不再位于 用户界面的最上层,且不能与用户进行交互。处于停止状态时,Activity在界面上完全不 能被用户看到,也就是说这个Activity被其他Activity全部遮挡。非活动状态指不在以 上3种状态中的Activity。 参考AndroidSDK官网文档中的说明,Activity生命周期如图3-1所示。该示意图中涉 及的方法被称为生命周期方法,当Activity状态发生改变时,相应的方法会被自动调用。 anriaAciiy类是Andod提供的基类, tvt dod.pp.tvtri应用程序中的每个Aciiy都继承 自该类,通过重写父类的生命周期方法来实现自己的功能。在代码段3-1中,@Overide 表示重写父类的onCreate()方法,Bundle类型的参数保存了应用程序上次关闭时的状 态,并且可以通过一个Activity传递给下一个Activity。在Activity的生命周期中,只要 离开了可见阶段(即失去了焦点),它就很可能被进程终止,这时就需要一种机制能保存当 时的状态,这就是其参数savedInstanceState的作用。 (1)启动Activity时,系统会先调用其onCreate()方法,然后调用onStart()方法,最 后调用onResume()方法,Activity进入活动状态。 (2)当Activity被其他Activity部分遮盖或被锁屏时,Activity不能与用户交互,会 调用onPause()方法,进入暂停状态。 (3)当Activity由被遮盖回到前台或解除锁屏时,会调用onResume()方法,再次进 入活动状态。 (4)当切换到新的Activity界面或按Home键回到主屏幕时,当前Activity完全不 可见,转到后台。此时会先调用onPause()方法,然后调用onStop()方法,Activity进入 停止状态。 (5)当Activity处于停止状态时,用户后退回到此Activity,会先调用onRestart()方 法,然后调用onStart()方法,最后调用onResume()方法,再次进入运行状态。 (6)当Activity处于被遮盖状态或者后台不可见,即处于暂停状态或停止状态时,如 果系统内存不足,就有可能杀死这个Activity。而后用户如果返回Activity,则会再依次 调用onCreate()方法、onStart()方法、onResume()方法,使其进入活动状态。 (7)用户退出当前Activity时,先调用onPause()方法,然后调用onStop()方法,最 后调用onDestroy()方法,结束当前Activity。 Activity生命周期可分为可视生命周期和活动生命周期。可视生命周期是Activity 64Android软件开发教程(第3版·微课版) 图3- 1 Activity生命周期 在界面上从可见到不可见的过程,开始于onStart(), 结束于onStop()。活动生命周期是 Activity在屏幕的最上层,并能够与用户交互的阶段,开始于onResume(), 结束于 onPause()。在Activity的状态变换过程中,onResume() 和onPause() 经常被调用,因此 这两个方法中应使用简单、高效的代码。 编程人员可以通过重写生命周期方法在Activity中定义当处于什么状态时做什么事 情。例如,当第一次启动一个Activity时,会调用onCreate() 方法,则初始化的操作可以 写在这个方法中。 第3章 Activity和界面布局 65 【例3-1】 示例工程Demo_03_ActivityLifeCycle用于验证Activity生命周期方法被 调用的情况,其主要代码如代码段3-3所示。 代码段3-3 验证Activity 生命周期方法的示例程序 //package 和import 语句省略 public class MainActivity extends AppCompatActivity { private static final String TAG = "生命周期示例"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Log.d(TAG, "-- onCreate()被调用--"); } @Override protected void onStart() { super.onStart(); Log.d(TAG, "-- onStart()被调用--"); } . //其余代码类似 } 运行这个Activity后,在Logcat面板中能看到给出的提示信息,从中可看到Activity 的生命周期是如何运行的。例如,启动这个Activity后Logcat面板输出的提示信息如 图3-2所示,onCreate()、onStart()和onResume()方法被依次调用;关闭这个Activity 时,Logcat面板输出的提示信息如图3-3所示,onPause()、onStop()和onDestroy()方法 被依次调用。 图3-2 Activity启动时调用的生命周期方法 图3-3 Activity结束时调用的生命周期方法 Activity的生 命周期 66Android软件开发教程(第3版·微课版) 3.1.4 Activity 的启动模式 主Activity在启动应用程序时就创建完毕,在其中可以显示XML 布局信息、指定处 理逻辑等。对于功能较复杂的应用程序,往往一个界面是不够用的,这就需要多个 Activity来实现不同的用户界面。在Android系统中,所有的Activity由堆栈进行管理, Activity栈遵循“后进先出”的规则。如图3-4所示,当一个新的Activity被执行后,它将 会被放置到堆栈的最顶端,并且变成当前活动的Activity,而先前的Activity原则上还是 会存在于堆栈中,但它此时不会在前台。Android系统会自动记录从首个Activity到其 他Activity的所有跳转记录并且自动将以前的Activity压入系统堆栈,用户可以通过编 程的方式删除历史堆栈中的Activity实例。 图3- 4 Activity栈示意图 Activity启动模式有4种,分别是standard、singleTop、singleTask、singleInstance。 可根据实际需求为Activity设置对应的启动模式,从而避免创建大量重复的Activity等 问题。设置Actiiy启动模式的方法是在Andrinfs.l里对应的<aciiy>标 vtodMaietxmtvtndroilaunchMod 签中设置ad:e属性,如图35所示。 图3在AXML 中设置Ay的启动模式 - 5 ndroidManifest.ctivit (1)standard是默认模式,如果Activity已经启动,再次启动会创建一个新的 第3章 Activity和界面布局 67 Activity实例叠在前一个Activity之上。此时Activity是在同一个任务栈里,只不过是 不同的实例。如果点击手机上的回退键会按照栈顺序依次退出。 (2)singleTop模式是可以有多个实例的,但是不允许多个相同的Activity叠加在一 起,如果Activity在栈顶时启动相同的Activity,则不会创建新的实例。 (3)singleTask 模式中每个Activity只有一个实例。在同一个应用程序中启动 Activity时,若它不存在,则会在当前创建一个新的实例;若存在,则会把任务列表中在其 之上的其他Activity取消并调用它的onNewIntent()方法。 (4)singleInstance模式只有一个实例,不允许有别的Activity存在,也就是说,一个 实例栈中只有一个Activity。 3.1.5 Context 及其在Activity 中的应用 Context的中文解释是“上下文”或“环境”,在Android中应该理解为“场景”。例如, 正在打电话时,场景就是用户所能看到的在手机里显示出来的拨号键盘,以及虽然看不 到,但是却在系统后台运行的对应着拨号功能的处理程序。 Context描述的是一个应用程序环境的上下文信息,是访问全局信息(如字符串资 源、图片资源等)的接口。通过它可以获取应用程序的资源和类,也包括一些应用级别操 作,如启动Activity、启动和停止Service、发送广播、接收Intent信息等。也就是说,如果 需要访问全局信息,就要使用Context。在代码段3-4中,this指的是这个语句所在的 Activity对象,同时也是这个Activity的Context。 代码段3-4 通过Context 获取Activity 上下文中的字符串信息 public class MainActivity extends AppCompatActivity { private TextView tv; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); tv = new TextView(this); //通过this 得到当前Activity 的上下文信息 tv.setText(R.string.hello_world); //通过Context 得到字符串资源 setContentView(tv); } } Android系统中有很多Context对象。例如,前述的Activity继承自Context,也就 是说每个Activity对应一个Context;Service也继承自Context,每个Service也对应一个 Context。 常用的Context对象有两种:一种是Activity的Context;另一种是Application的 Context。二者的生命周期不同:Activity的Context生命周期仅在Activity存在时,也 就是说,如果Activity 已经被系统回收了,那么对应的Context也就不存在了;而 Application的Context生命周期却很长,只要应用程序运行着,这个Context就是存在 的,所以要根据自己程序的需要使用合适的Context。 68Android软件开发教程(第3版·微课版) 3.布局及其加载 2 Activity主要用于呈现用户界面,包括显示UI控件、监听并处理用户界面事件并做 出响应等。从本节开始,将系统地介绍Android的界面布局及其加载与控制。 3.2.1 View 类和ViewGroup 类 在一个Android应用程序中,用户界面一般由一组View和ViewGroup对象组成。 View对象是继承自View基类的可视化控件对象,是Android平台上表示用户界面 的基本单元,如TextView、Buton、CheckBox等。View是所有可视化控件的基类,提供 了控件绘制和事件处理的属性和基本方法,任何继承自View的子类都会拥有View类的 属性及方法。表3-1给出了View类的部分常用属性的说明。View及其子类的相关属性 既可以在XML布局文件中进行设置,也可以通过成员方法在Java代码中动态设置。 表3- 1 View类的部分常用属性 XML属性在Java代码中对应的方法功能及使用说明 android:background setBackgroundResource(int) 设置背景颜色 android:clickable setClickable(boolean) 设置是否响应点击事件 android:focusable setFocusable(boolean) 设置View控件是否能捕获焦点 android:id setId(int) 设置View控件标识符 android:layout_width setWidth(int) 设置宽度 android:layout_height setHeight(int) 设置高度 android:text setText(CharSequence)/setText(int) 设置控件上显示的文字 android:textSize setTextSize(float) 设置控件上显示文字的大小 android:textColor setTextColor(int) 设置控件上显示文字的颜色 android:visibility setVisibility(int) 设置View控件是否可见 ViewGroup类是View类的子类,与View类不同的是,它可以充当其他控件的容 器。ViewGroup类作为一个基类为布局提供服务,其主要功能是装载和管理一组View 和其他ViewGroup,可以嵌套ViewGroup和View对象。Android用户界面中的控件都 是按照层次树的结构堆叠的,其关系如图3-6所示,而它们共同组建的顶层视图可以由应 用程序中的Activity调用setContentView()方法来显示。Android中的一些复杂控件 (如Galey、GridView等)都继承自ViewGroup。 一般很少直接用View和ViewGroup来设计界面布局,更多的时候是使用它们的子 类控件或容器来构建布局。常见用布局和UI控件的继承结构如图3-7所示。每个View 和ViewGroup对象都支持它们自己的各种属性。一些属性对所有View对象可用,因为 它们是从View基类继承而来的,如id属性;而有些属性只有特定的某一种View对象和 它们的子类可用,如TextView及其子类支持textSize属性。 第3章Activity和界面布局69 图3- 6 View 与ViewGroup的关系 图3- 7 常用布局和UI 控件的继承结构 3.2.2 XML 布局及其加载 在Android应用程序中,常见的布局方式有线性布局(LinearLayout)、相对布局 (RelativeLayout)、表格布局(TableLayout)、网格布局(GridLayout)、帧布局(FrameLayout)、 约束布局(ConstraintLayout)等。这些布局都通过ViewGroup的子类实现。 界面的布局可以在XML 文件中进行设置,也可以通过Java代码设计实现。如果采 用第一种方式,则需要在资源文件夹res\layout中定义相应的布局文件。这个XML 布 局文件由许多View对象嵌套组成。如果布局中有多个元素,那么最顶层的根节点必须 是ViewGroup对象;如果整个布局只有一个元素,那么最顶层元素就是唯一的元素,它可 以是一个单一的UI 对象。 代码段3-lyotxm在其中声明了布局的实例, 5是一个自定义的布局文件myau.l, 该例 采用线性布局,布局中包括一个TextView控件。 70 Android软件开发教程(第3版·微课版) 代码段3-5 自定义的XML 布局文件 <? xml version="1.0" encoding="utf-8"? > <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" > <TextView android:id="@+id/tvHello" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/hello" /> </LinearLayout> 定义了布局文件之后,需要在Activity 中的onCreate()回调方法中通过调用 setContentView()方法来加载这个布局,如代码段3-7所示。 代码段3-6 通过重写onCreate()方法加载用户界面的布局 public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.mylayout); //加载布局 } } 3.2.3 在Activity 中定义和加载布局 除了上述直接调用已经设定好的XML布局外,还可以在Java代码中直接定义并加 载某种布局,此时就不需要在工程的res文件夹下存放XML布局文件了。在将UI对象 实例化并设置属性值后,通过调用addView()方法可将其添加到设定的布局。 【例3-2】 示例工程Demo_03_DefineLayoutInActivity演示在Java代码中定义并引 用布局的方法。 此例是通过在MainActivity中添加线性布局而非通过XML 布局文件来设置布局 的。通过循环语句定义了3个按钮,并通过addView()方法将其添加到布局中,如代码 段3-7所示。 代码段3-7 在Activity 中设定布局 public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); LinearLayout myLayout=new LinearLayout(this); //通过上下文设定线性布局对象 第3章 Activity和界面布局 71 myLayout.setGravity(Gravity.CENTER_HORIZONTAL); myLayout.setOrientation(LinearLayout.VERTICAL); // 垂直布局 myLayout.setPadding(0,20,0,0); //设置左、上、右、下边距 setContentView(myLayout); //加载布局 Button myBtn; //定义按钮对象 for (int i=1; i<4; i++){ //添加几个按钮 myBtn=new Button(this); myBtn.setText("按钮" + i); myBtn.setTextSize(20); //设置字体大小 myBtn.setHeight(ViewGroup.LayoutParams.WRAP_CONTENT); //设置按钮高度 myLayout.addView(myBtn); //添加到布局中 myBtn.getLayoutParams().width=500; //设置按钮宽度 } } } 示例工程的运行结果如图3-8所示。 图3-8 示例工程的运行结果 3.2.4 资源的管理与使用 在Android系统中,对字符、颜色、图像、音频、视频等资源的使用与管理也是很方便的, 只要引用或设置资源文件夹res下的相关媒体文件或XML文件,就可以实现相关功能。 Android应用程序中,XML布局和资源文件并不包含在Activity的Java源码中,各 种资源文件由系统自动生成的R文件来管理。每个资源类型在R 文件中都有一个对应 的内部类。例如,类型为layout的资源项在R 文件中对应的内部类是layout,而类型为 string的资源项在R文件中对应的内部类就是string。R文件的作用相当于一个项目字 典,项目中的用户界面、字符串、图片、声音等资源都会在对应的内部类中创建其唯一的 id,当项目中使用这些资源时,会通过该id得到资源的引用。如果程序开发人员变更了 任何资源文件的内容或属性,R文件会随之变动并自动更新其相应的内部类,开发者不需 要也不能修改此文件。 在Java程序中通过R文件引用资源的方法是“R.资源类型.资源名称”,其中,资源类 型可以是图像、字符串或布局文件,资源名称是资源文件名或XML文件中的对象名。例 72 Android软件开发教程(第3版·微课版) 如:R.drawable.background表示引用资源文件夹中的res\drawable\background.png图 片文件;R.string.title表示引用资源文件res\values\string.xml中定义的title字符串;R. layout.activity_main表示引用资源文件夹中的res\layout\activity_main.xml布局文件; R.id.tv_result表示引用布局文件中id为tv_result的TextView对象。 1.图片资源的管理与使用 AndroidStudio工程项目提供了mipmap文件夹和drawable文件夹管理图片资源文 件。新建工程项目时,系统会在资源文件夹res中自动创建多个drawable或mipmap文 件夹,如drawable-hdpi、drawable-mdpi、mipmap-hdpi、mipmap-mdpi等,具体取决于 AndroidStudio的版本。当应用程序安装在不同显示分辨率的终端上时,程序会自适应 地选择加载某个文件夹中的资源。例如,一部屏幕密度为320 的手机,会自动使用 drawable_xhdpi文件夹下的图片。如果有默认文件夹drawable,则系统在其他dpi文件 夹下找不到图片时会使用drawable中的图片。 谷歌公司建议将应用程序的图标文件放在mipmap文件夹中,这样可以提高系统渲 染图片的速度,提高图片质量,减小GPU 压力。mipmap支持多尺度缩放,系统会根据当 前缩放范围选择mipmap文件夹中适当的图片,而不是像drawable文件夹根据当前设备 的屏幕密度选择恰当的图片。 【例3-3】 示例工程Demo_03_UseImageResource以设置ImageView的图片属性为 例演示了如何在XML文件中引用图片资源。 首先将图片文件复制到工程中的mipmap文件夹下,图3-9是把background.jpg复 制到工程中的效果。 图3-9 在工程中添加图片 在布局XML文件中,通过“@mipmap/图片文件名”的方式引用mipmap文件夹中 的图片文件,实现代码如代码段3-8所示,运行结果如图3-10所示。 代码段3-8 在XML 文件中引用图片资源 <? xml version="1.0" encoding="utf-8"? > 第3章 Activity和界面布局 73 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="在ImageView 中引用图片资源:" android:textColor="#000000" android:textSize="25sp" android:layout_margin="16dp"/> <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@mipmap/background" android:scaleType="centerCrop"/> </LinearLayout> 图3-10 设置ImageView 的图片属性 如果是在Java代码中,则通过“R.mipmap.图片文件名”的方式引用mipmap文件夹 中的图片文件。例如代码段3-9将mipmap文件夹中background.jpg设置为App的 背景。 代码段3-9 在Activity 中设定App 的背景 protected void onCreate(Bundle savedInstanceState) { 74 Android软件开发教程(第3版·微课版) super.onCreate(savedInstanceState); this.getWindow().setBackgroundDrawableResource(R.mipmap.background); //用指定图片作为背景 setContentView(R.layout.activity_main); } 2.字符串资源的管理与使用 字符串资源描述文件strings.xml一般位于工程res文件夹下的values子文件夹中。 如果需要在Activity 代码或布局文件中使用字符串,可以在strings.xml文件中的 <resources>标签下添加相应的<string>元素,定义字符串资源。<string>元素的基 本格式如下: <string name="字符串名称"> 字符串的内容</string> 代码段3-10是一个典型的strings.xml示例,其中定义了两个字符串资源。 代码段3-10 string.xml 代码示例 <? xml version="1.0" encoding="utf-8"? > <resources> <string name="app_name">MyFirstApplication</string> <string name="hello_world">Hello world!</string> </resources> 上述代码段的第1行定义了XML版本与编码方式;第2行以后在<resources>标 签下定义了两个<string>元素,定义了两个字符串,字符串的名称分别为app_name和 hello_world。如果需要在Java程序代码中使用这些字符串,可以用“R.string.字符串名 称”的方式引用。如果在XML文件中使用这些字符串,则用“@string/字符串名称”的方 式引用。Android解析器会从工程的res/values/strings.xml文件里读取相应名称对应 的字符串值并进行替换。 3.数组资源的管理与使用 与字符串资源类似,数组描述文件arrays.xml位于工程res文件夹下的values子文 件夹中。数组资源也定义在<resources>标签下,其基本语法如下: <数据类型-array name="数组名"> <item>数组元素值1</item> <item>数组元素值2</item> . </数据类型-array> 代码段3-11是一个典型的arrays.xml示例,在其中定义了两个字符串数组,数组名 第3章 Activity和界面布局 75 分别是citys和modes。 代码段3-11 arrays.xml 代码示例 <? xml version="1.0" encoding="utf-8"? > <resources> <string-array name="citys"> <item>北京</item> <item>天津</item> <item>上海</item> </string-array> <integer-array name="modes"> <item>1</item> <item>2</item> <item>3</item> </integer-array> </resources> 在XML中引用数组资源的方法是“@array/数组名称”,例如: <Spinner android:layout_width="match_parent" android:layout_height="wrap_content" android:entries="@array/citys"/> 在Java代码中引用数组资源的方法是“getResources().getXxxArray(R.array.数组 名称)”,例如: String[] citys=getResources().getStringArray(R.array.citys); int[] modes=getResources().getIntArray(R.array.modes); 4.颜色描述资源的管理与使用 颜色描述文件colors.xml位于工程res文件夹下的values子文件夹中,其典型内容 如代码段3-12所示。 代码段3-12 colors.xml 代码示例 <? xml version="1.0" encoding="utf-8"? > <resources> <color name="colorPrimary">#3F51B5</color> <color name="colorPrimaryDark">#303F9F</color> <color name="colorAccent">#FF4081</color> </resources> 在<resources>标签下添加相应的<color>元素,定义颜色资源,其基本格式如下: 76 Android软件开发教程(第3版·微课版) <color name="颜色名称"> 该颜色的值</color> 颜色值通常为8位的十六进制的颜色值,表达式顺序是#aarrggbb,其中aa表示 alpha值(00为完全透明,FF为完全不透明),aa可省略,此时表示一个完全不透明的颜 色;rr表示红色分量值;gg表示绿色分量值;bb表示蓝色分量值。例如,#7F0400FF表 示半透明的蓝色。任何一种颜色的值范围都是十六进制00~FF(0~255)。 在XML中引用颜色资源的方法是“@color/颜色名称”,例如: android:textColor="@color/colorAccent" 在Java代码中引用颜色资源的方法是“getResources().getColor(R.color.颜色名 称)”,或“ContextCompat.getColor(context,R.color.颜色名称)”,例如: TextView hello=(TextView)findViewById(R.id.hello); hello.setTextColor(getResources().getColor(R.color.colorPrimary)); 5.引用assets文件夹中的资源 同res文件夹相似,assets也是存放资源文件的文件夹,但res文件夹中的内容会被 编译器所编译,assets文件夹则不会。也就是说,应用程序运行的时候,res文件夹中的内 容会在启动的时候载入内存,assets文件夹中的内容只有在被用到的时候才会载入内存, 所以一般将一些不经常用到的大资源文件存放在该文件夹下,如应用程序中用到的音频、 视频、图片、文本等文件。 在程序中可以使用“getResources.getAssets().open("文件名")”的方式得到资源文 件的输入流InputStream 对象。 3.3 常用的布局 3.3.1 线性布局LinearLayout 线性布局将其包含的子元素按水平或者垂直方向顺序排列。布局方向由属性 android:orientation的值来决定,其值为vertical时子元素垂直排列,为horizontal时子元 素水平排列。同时,可使用android:gravity属性调整其子元素向左、向右或居中对齐,或 使用android:padding 属性来微调各子元素的摆放位置,还可以通过设置子元素的 android:layout_weight属性值控制各个元素在容器中的相对大小。 在XML布局文件中,线性布局的子元素定义在<LinearLayout></LinearLayout>标 签之间。每个线性布局的所有子元素,如果垂直分布则仅占一列,如果水平分布则仅占一 行。线性布局中如果子元素所需位置超过一行或一列,不会自动换行或换列,超出屏幕的 子元素将不会被显示。 第3章 Activity和界面布局 77 【例3-4】 示例工程Demo_03_LinearLayout演示了线性布局的用法。 在AndroidStudio中新建一个工程,选用空白的Activity模板,系统会自动为该 Activity建立一个位于res/layout/中的布局文件,自动建立的内容采用约束布局。可以 把这个约束布局直接修改为线性布局,如代码段3-13所示。本例中采用垂直布局,在布 局中添加了3个按钮,示例工程运行结果如图3-11所示。 图3-11 线性布局示例工程的运行结果 代码段3-13 线性布局示例 <? xml version="1.0" encoding="utf-8"? > <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center_horizontal" android:paddingTop="8dp"> <Button android:text="按钮1" android:id="@+id/button1" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <Button android:text="按钮2" android:id="@+id/button2" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <Button android:text="按钮3" android:id="@+id/button3" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </LinearLayout> 在代码段3-13中,为每个按钮对象定义了id属性。在XML布局文件中,可以通过 设置android:id属性来给相应的UI元素指定id,通常是以字符串的形式定义一个id,格 78 Android软件开发教程(第3版·微课版) 式如下: android:id="@+id/id 字符串" 这里在“@+id/”后面的字符是设定的id,@表示XML解析器应该解析id字符串并 把它作为id资源;+表示这是一个新的资源名字,它被创建后应加入R 资源文件中。通 过这个id,可在XML布局或Activity代码中引用相应的UI元素。引用id时不需要符号 +。例如,代码段3-14演示了在Activity中通过id引用布局中的第一个按钮,并设置其 text属性值。 代码段3-14 通过findViewById()方法引用布局中的UI 对象 @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button myButton=(Button)findViewById(R.id.button1); //取得Button 控件句柄,存储到myButton 对象中 myButton.setText("hello"); //字符串hello 显示在id 为button1 的按钮上面 } 另外,一般情况下每个View 对象都需要设置宽度和高度(layout_width和layout_ height)属性。可以指定宽度和高度的绝对值,如50dp,也可指定为match_parent或者 wrap_content。match_parent使UI元素对象扩展以填充布局单元内尽可能多的空间,如 果设置一个元素的layout_width和layout_height属性值为match_parent,它将被强制性 布满整个父容器。而wrap_content使UI元素对象扩展以显示其全部内容,例如, TextView和ImageView对象,设置其layout_width和layout_height属性值为wrap_ content,将恰好完整显示其内部的文本或图像,UI元素将会根据其内容自动更改大小并 包裹住文字或图片内容。除此之外,还可以通过设置UI元素对象的对齐方式、边距、边 界等,调整其在界面中的位置。例如,代码段3-13中,通过设置根布局的android:gravity 属性使3个按钮水平居中。 【例3-5】 示例工程Demo_03_BrowserByLinearLayout采用线性布局,实现了一个 简易浏览器界面。 本例演示了线性布局中子元素在容器中的相对大小比例的控制。本例使用了 android:layout_weight属性,该属性用于定义控件对象所占空间分割父容器的比例。 android:layout_weight属性只有在LinearLayout中才有效,其默认值为0。其含义 是一旦View对象设置了该属性,那么该对象的所占空间等于android:layout_width或 layout_height设置的空间加上剩余空间的占比。即LinearLayout如果显式包含layout_ weight属性,则会计算两次对象所占尺寸,第一次将正常计算所有View 对象的宽高,第 二次将结合layout_weight的值分配剩余的空间。例如,假设屏幕宽度为L,两个View 对象的宽度都为match_parent,其layout_weight的值分别是1和2,则两个View的原有 线性布局 LinearLayout 第3章 Activity和界面布局 79 宽度都为L,那么剩余宽度为L-(L+L)=-L,第一个View对象占比为1/3,所以总宽 度是L+(-L)×1/3=(2/3)L。 一般推荐当使用layout_weight属性时,将android:layout_width或layout_height 设为0dp,这样layout_weight值就可以简单理解为空间占比了。 示例工程的布局效果预览如图3-12(a)所示,运行结果如图3-12(b)所示。XML布 局文件的内容如代码段3-15所示。 图3-12 线性布局实现简易浏览器界面 代码段3-15 采用线性布局实现简易浏览器界面 <? xml version="1.0" encoding="utf-8"? > <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:padding="8dp"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <EditText android:id="@+id/editText"