图像

图像视图ImageView

图像视图的图片通常位于res/drawable目录

  • 在Layout文件中通过属性android:src来指定图片资源,如android:src="@drawable/splash"
  • 在Activity文件中通过调用setImageResource()方法指定图片资源,如setImageResource(R.drawable.splash)

ImageView本身默认图片居中显示,如果要改变图片显示方式,可以通过scaleType属性设置

  • fitXY:拉伸填充满视图
  • fitStart:保持比例,拉伸使其位于视图上方或左侧
  • fitCenter:保持比例,拉伸居中
  • fitEnd:保持比例,拉伸使其位于视图下方或右侧
  • center:尺寸不变,居中
  • centerCrop:拉伸充满视图,居中
  • centerInside:保持比例,缩小图片位于视图中间
1
2
3
4
5
6
7
8
9
10
11
12
13
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">

<ImageView
android:id="@+id/iv_scale"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/splash"
android:scaleType="fitXY"/>

</LinearLayout>
1
2
3
4
5
6
7
8
9
10
11
12
public class ImageScaleActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_image_scale);

ImageView iv_scale = findViewById(R.id.iv_scale);
// iv_scale.setImageResource(R.drawable.splash);//获取图片视图
// iv_scale.setScaleType(ImageView.ScaleType.FIT_XY);//拉伸使其填充满视图
}
}

图像按钮ImageButton

ImageButton是可以显示图片的按钮,它继承自ImageView,而非继承Button

ImageButton和Button之间的区别如下:

  • Button既可以显示文本也可以显示图片,ImageButton只能显示图片不能显示文本;
  • ImageButton上的图像可以按比例缩放,Button通过背景设置图像会拉伸变形;
  • Button只能显示一张背景图,而ImageButton可以分别设置前景图和背景图,从而实现叠加效果。

在某些场景下,特殊字符或字体无法通过输入法输入,就需要先切图再放到ImageButton中

ImageButton和ImageView的区别如下:

  • ImageButton有默认的按钮背景,ImageView默认无背景
  • ImageButton默认的缩放类型是center,ImageView默认的缩放类型是fitCenter

文本与图像同时显示

同时展示文本与图像的方法有:

  1. 利用LinearLayout对ImageView和TextView组合布局
  2. 通过按钮控件Button的drawable**属性设置文本周围的图标
    • drawableTop:指定文字上边的图片
    • drawableBottom:指定文字下边的图片
    • drawableLeft:指定文字左边的图片
    • drawableRight:指定文字右边的图片
    • drawablePadding:设置图片和文字的间距

简单计算器开发

布局

计算器界面分为两大部分:表达式显示部分、按键部分

控件

  • LinearLayout:整体布局从上到下线性排列
  • GridLayout:按键部分网格排列
  • ScrollView:表达式区域如果超出屏幕就需要滚动
  • TextView:计算结果用文本视图显示
  • Button:数字和运算符按键
  • ImageButton:开根运算符

Activity

启停活动页面

Activity的启动和结束

从当前页面调转到新的页面:

  • startActivity(new Intent(源页面.this, 目标页面.class));

从当前页面返回到上一个页面,相当于关闭当前页面:

  • finish()

\src\main\java\com\example\chapter04\ActStartActivity.java

1
2
3
4
5
6
7
8
9
10
11
12
public class ActStartActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_act_start);
Button btn_act_next = findViewById(R.id.btn_act_next);
btn_act_next.setOnClickListener(view -> {
startActivity(new Intent(this,ActFinishActivity.class));//跳转到一个新的Activity
});
}
}

\src\main\java\com\example\chapter04\ActFinishActivity.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class ActFinishActivity extends AppCompatActivity implements View.OnClickListener {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_act_finish);

findViewById(R.id.iv_back).setOnClickListener(this);
findViewById(R.id.btn_back).setOnClickListener(this);
}

@Override
public void onClick(View view) {
finish();//结束当前Activity
}
}

Activity的生命周期

Activity的生命周期指的是Activity从被创建到被销毁之间的一系列状态、阶段。在不同的状态、阶段下适用于处理不同的事物。

Activity生命周期 说明
onCreate 创建活动。页面被加载进内存,进入初始状态。
onStart 开始活动。页面展示在屏幕上,进入就绪状态。
onResume 恢复活动。页面进入活跃状态,可以触发各种交互事件,如允许点击、输入。
onPause 暂停活动。页面进入暂停状态,无法触发交互事件。
onStop 停止活动。页面进入停止状态,不在屏幕上显示。
onDestroy 销毁活动。回收活动占用的所有系统资源,页面从内存中销毁。
onRestart 重启活动。重新加载内存中的页面数据。
onNewIntent 重用已有的活动实例。

如果一个Activity已经启动过,并且存在于当前应用的Activity任务栈中,启动模式为singleTasksingleInstancesingleTop(任务栈顶),那么在此启动回到这个Activity的时候,不会创建新的实例,也就是不执行onCreate()方法,而是执行onNewIntent()方法。

各状态之间的切换过程:

  • 打开新页面的调用顺序为:
    • onCreate:arrow_right:onStart:arrow_right:onResume
  • 关闭页面的调用顺序为:
    • onPause:arrow_right:onStop:arrow_right:onDestroy​

Activity的启动模式

Activity活动存储在任务栈(TaskStack)中,打开一个页面时就意味着一个活动入栈了,关闭一个页面就意味着一个活动出栈了。

在配置文件中指定启动模式

1
2
3
4
5
<activity
android:name=".ActStartActivity"
android:exported="true"
android:launchMode="standard">
</activity>
  1. 默认启动模式standard

    该模式下,启动的Activity会依照启动顺序一次压入Task栈中

  2. 栈顶复用模式singleTop

    在该模式下,如果栈顶Activity为目标Activity,那么就不会重复创建新的Activity。

适合开启渠道多、多应用开启调用的Activity,通过这种设置可以避免已经创建过的Activity被重复创建,多数情况通过动态设置使用。

  1. 栈内复用模式singleTask

    与singleTop模式相似,只不过singleTop模式是针对栈顶的活动,而singleTask模式下,如果task栈内存在目标Activity实例,则将task内的对应Activity实例之上的所有Activity弹出栈,并将对应Activity置于栈顶获得焦点。

  2. 全局唯一模式singleInstance

    该模式下会为目标Activity创建一个新的Task栈,将目标Activity放入新的Task,并让目标Activity获得焦点。新的Task有且只有一个Activity实例。如果已经创建过目标Activity实例,则不会创建新的Task,而是将已创建的Activity唤醒。

动态设置启动模式
  1. 在两个活动之间频繁交替跳转

    假设活动A与活动B之间页面切换非常频繁,那么在切换数次后,在Task栈中就会创建数个A和B,当需要退回到首页时,则需要将这数个A和B按顺序退出后才能到达首页。但实际需求是:A和B只应该各退出一次然后就回到首页,而不需要退出数次。

    对于不允许重复返回的情况,可以设置启动标志FLAG_ACTIVITY_CLEAR_TOP

    1
    2
    3
    4
    5
    //创建一个意图对象,准备跳转到指定活动页面
    Intent intent = new Intent(this, JumpSecondActivity.class);
    //栈中存在待跳转的活动实例时,则重新创建该活动实例,并清除原实例上方的所有实例
    intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
    startActivity(intent);
  2. 登录成功后不再返回登录页面

    登录页面在用户成功登录后就会消失,然后进入首页,当点击返回按钮时并不会返回到登录页面而是直接退出APP。

    对于只打开一次不再需要退回去的页面,可以设置启动标志FLAG_ACTIVITY_CLEAR_TASK,该标志会清空当前活动栈内的所有实例。清空之后就意味着当前栈无法继续使用,必须重新创建活动栈,也就是同时设置启动标志FLAG_ACTIVITY_NEW_TASK,该标志用于开辟新任务的活动栈。

    1
    2
    3
    4
    5
    //创建一个意图对象,准备跳转到指定的活动页面
    Intent intent = new Intent(this, LoginSuccessActivity.class);
    //跳转到新页面时,清空栈中原有的所有实例,同时开辟新任务的活动栈
    intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK|Intent.FLAG_ACTIVITY_NEW_TASK);
    startActivity(intent);
动态设置启动模式中的启动标志 说明
Intent.FLAG_ACTIVITY_NEW_TASK 开辟一个新的任务栈
Intent.FLAG_ACTIVITY_SINGLE_TOP 当栈顶为待跳转的活动实例时,则重用栈顶的实例
Intent.FLAG_ACTIVITY_CLEAR_TOP 当栈中存在待跳转的活动实例时,则重新创建一个实例,并清除原实例上方所有实例
Intent.FLAG_ACTIVITY_NO_HISTORY 栈中不保存新启动的活动实例
Intent.FLAG_ACTIVITY_CLEAR_TASK 跳转到新页面时,栈中原有的实例都将清空

在活动之间传递消息

显式Intent和隐式Intent

Intent是各个组件之间信息沟通的桥梁,它用于Android各组件之间的通信,主要完成下列工作:

  • 标明本次通信请求来源、目标、属性。
  • 发起方携带本次通信需要的数据内容,接收方从收到的意图中解析数据。
  • 发起方若想判断接收方的处理结果,意图就要负责让接收方传回应答的数据内容。

Intent的组成部分:

名称 设置方法 说明与用途
Component setComponent 组件,指定意图的来源与目标
Action setAction 动作,指定意图的动作行为
Data setData 即Uri,指定动作要操作的数据路径
Category addCategory 类别,指定意图的操作类别
Type setType 数据类型,指定消息的数据类型
Extras putExtras 扩展信息,指定装载的包裹信息
Flags setFlags 标志位,指定活动的启动标志
显式Intent

显式Intent直接指定来源活动与目标活动,属于精确匹配。有三种构建方式:

  1. 在Intent的构造函数中指定

    1
    Intent intent = new Intent(this, ActNextActivity.class)//创建一个目标确定的意图对象
  2. 调用意图对象的setClass方法指定

    1
    2
    Intent intent = new Intent();//创建一个意图对象
    intent.setClass(this, ActNextActivity.class);//设置意图对象的源和目标
  3. 调用意图对象的setComponent方法指定

    1
    2
    3
    Intent intent = new Intent();//创建一个新的意图对象
    ComponentName component = new ComponentName(this, ActNextActivity.class);//创建包含目标活动在内的组件名称对象
    intent.setComponent(component);//设置意图对象携带的组件信息
隐式Intent

没有明确指定要跳转的目标活动,只给出一个动作字符串让系统自动匹配,属于模糊匹配。

通常App不希望向外部暴露活动名称,只给出一个事先定义好的标记串,通过标记串找到指定的活动,隐式Intent起到了标记过滤的作用。这个标记串可以是自定义的动作,也可以是系统动作。

Intent类的系统动作常量名 系统动作的常量值 说明
ACTION_MAIN android.intent.action.MAIN App启动时的入口
ACTION_VIEW android.intent.action.VIEW 向用户显示数据
ACTION_SEND android.intent.action.SEND 分享内容
ACTION_CALL android.intent.action.CALL 直接拨号
ACTION_DIAL android.intent.action.DIAL 准备拨号
ACTION_SENDTO android.intent.action.SENDTO 发送短信
ACTION_ANSWER android.intent.action.ANSWER 接听电话

动作名称既可以通过setAction方法指定,也可以通过构造函数Intent(String action)直接生成意图对象。由于动作是模糊匹配,因此有时需要更详细的路径。UriCategory便是这样的路径与门类信息,Uri数据可通过构造函数Intent(String action, Uri uri)在生成对象时一起指定,也可以通过setData方法指定;Category可通过addCategory方法指定,之所以用add而不用set方法,是因为一个意图允许设置多个Category,方便一起过滤。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public class ActionUriActivity extends AppCompatActivity implements View.OnClickListener {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_action_uri);

findViewById(R.id.btn_dial).setOnClickListener(this);
findViewById(R.id.btn_send).setOnClickListener(this);
findViewById(R.id.btn_my).setOnClickListener(this);
}

@Override
public void onClick(View view) {
Intent intent = new Intent();
String phoneNo = "10086";
switch (view.getId()){
case R.id.btn_dial:
intent.setAction(Intent.ACTION_DIAL);//设置意图动作,准备拨号
Uri uri = Uri.parse("tel:"+phoneNo);//声明一个拨号的Uri,携带电话号码数据
intent.setData(uri);
startActivity(intent);
break;
case R.id.btn_send:
intent.setAction(Intent.ACTION_SENDTO);//设置意图动作,发短信
Uri uri1 = Uri.parse("smsto:"+phoneNo);//声明一个拨号的Uri,携带电话号码数据
intent.setData(uri1);
startActivity(intent);

case R.id.btn_my:
intent.setAction("android.intent.action.YANG");//设置意图动作,自定义动作
intent.addCategory(Intent.CATEGORY_DEFAULT);//添加一个门类
startActivity(intent);
break;
}
}
}

向下一个Activity发送数据

Intent使用Bundle对象存放待传递的数据信息。

Bundle对象操作各类型数据的读写方法如下表:

数据类型 读方法 写方法
整型 getInt putInt
单精度浮点型 getFloat putFloat
双精度浮点型 getDouble putDouble
布尔型 getBoolean putBoolean
字符串 getString putString
字符串数组 getStringArray putStringArray
字符串列表 getStringArrayList putStringArrayList
可序列化结构 getSerializable putSerializable

ActSendActivity.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class ActSendActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_act_send);

TextView tv_send = findViewById(R.id.tv_send);
findViewById(R.id.btn_send).setOnClickListener(view -> {
Intent intent = new Intent(this, ActReceiveActivity.class);
Bundle bundle = new Bundle();//创建一个新的包裹
bundle.putString("request_time", DateUtil.getNowTime());
bundle.putString("request_content",tv_send.getText().toString());
intent.putExtras(bundle);
startActivity(intent);
});
}
}

ActReceiveActivity.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class ActReceiveActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_act_receive);

TextView tv_receive = findViewById(R.id.tv_receive);
//从上一个页面传来的意图中获取数据
Bundle bundle = getIntent().getExtras();
String request_time = bundle.getString("request_time");
String request_content = bundle.getString("request_content");

String desc = String.format("收到请求消息:\n请求为%s\n请求内容%s",request_time,request_content);
tv_receive.setText(desc);
}
}
  • 在代码中发送消息包裹,调用意图对象的putExtras方法,即可存入消息包裹。
  • 在代码中接收消息包裹,调用意图对象的getExtras方法,即可取出消息包裹。

向上一个Activity发送数据

处理下一个页面的应答数据,详细步骤说明如下:

  • 上一个页面打包好请求数据,调用startActvityForResult方法执行跳转动作
  • 下一个页面接收并解析请求数据,进行相应处理
  • 下一个页面在放回上一个页面时,打包应答数据并调用setResult方法返回数据包裹
  • 上一个页面重写方法onActivityResult,解析获得下一个页面的返回数据