Week 4 - Google’s Material Design, Intents & App Manifests, & RecyclerView

Material Design 在中文中通常被称为“材料设计”或“质感设计”。这是一种由谷歌开发的综合性设计语言,旨在合成经典设计原则与科技创新的可能性,以创建一种视觉语言。材料设计基于纸张和墨水的物理特性,但通过数字屏幕和科技的能力来实现,旨在提供一致性、直观性和美观性,改善用户体验,并在谷歌的移动应用、Android应用以及许多其他公司的网页和移动应用中得到广泛应用。

材料设计的核心特点包括响应式动画和过渡、深度效果(如光照和阴影)、基于网格的布局以及一系列的组件和指南,这些都旨在帮助开发者创建具有一致性、直观性和现代美学的应用界面。在Android平台上,采用材料设计原则对于确保应用与Android生态系统内其他应用保持一致、提升用户体验、吸引并保持用户的兴趣具有重要意义。此外,材料设计作为一种跨平台的设计语言,也支持在iOS和Web应用程序中使用,帮助开发者在不同平台之间保持设计的一致性。

Intents and Intent Filter

Intent is a message to request action from another app component

Intent filter which implicit intents your app can receive

Explicit Intents and Implicit Intent

在Android开发中,“意图(Intents)”和“意图过滤器(Intent Filters)”是两个核心概念,它们用于应用程序之间的通讯以及内部组件之间的消息传递。

意图(Intents)

意图是一个消息传递对象,可以用来请求另一个应用组件的动作。它们主要用于启动活动(Activities)、启动服务(Services)或者传送广播(Broadcasts)。根据使用方式的不同,意图分为显式意图和隐式意图。

  • 显式意图(Explicit Intents):直接指定了要启动或通信的组件(例如,活动、服务或接收器)的名称。它们通常用于应用内组件之间的交互。例如,从一个活动启动另一个活动时,你会明确指定要启动的目标活动。
  • 隐式意图(Implicit Intents):不直接指定要启动的组件,而是声明一个要执行的动作和提供必要数据的类型。系统通过意图过滤器来判断哪个组件能够响应该意图。隐式意图主要用于触发不同应用中的组件。例如,你可以通过隐式意图请求打开一个网页,系统会通过安装的应用中的意图过滤器来决定用哪个浏览器打开该网页。

意图过滤器(Intent Filters)

意图过滤器用于指定一个组件(活动、服务或广播接收器)愿意接受的意图类型。它在应用的清单文件(AndroidManifest.xml)中声明,用于描述一个组件能够响应的动作、数据和数据类型。当系统接收到一个隐式意图时,它会检查所有组件的意图过滤器,找到能够处理该意图的组件。

总结一下,意图和意图过滤器是Android应用开发中用于组件间通信和交互的基础。显式意图直接指定目标组件,适用于应用内部通信;而隐式意图声明一个动作,让系统找到合适的组件来处理,适用于跨应用通信。意图过滤器则定义了组件愿意接受和处理的意图类型。

当然可以。以下是一个Android开发中使用显式意图和隐式意图的简单代码示例:

显式意图(Explicit Intent)示例

假设你有两个活动:MainActivitySecondActivity。你想从 MainActivity 启动 SecondActivity

MainActivity.java:

1
2
3
4
// 在MainActivity中创建并启动显式意图
Intent explicitIntent = new Intent(MainActivity.this, SecondActivity.class);
startActivity(explicitIntent);

在这个例子中,你通过指定当前的上下文(MainActivity.this)和要启动的活动类(SecondActivity.class)来创建一个显式意图。然后,你通过调用 startActivity() 方法来启动 SecondActivity

隐式意图(Implicit Intent)示例

现在,假设你想让用户选择一个应用来浏览网页,而不是直接指定使用哪个应用。

1
2
3
4
5
6
7
8
9
10
// 创建一个隐式意图来打开网页
Intent implicitIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("<https://www.example.com>"));
// 验证是否有应用可以处理这个意图
if (implicitIntent.resolveActivity(getPackageManager()) != null) {
startActivity(implicitIntent);
} else {
// 处理找不到应用的情况
Toast.makeText(this, "No application can handle this request.", Toast.LENGTH_SHORT).show();
}

在这个隐式意图的例子中,你通过指定一个动作(Intent.ACTION_VIEW)和数据(网页的URI)来创建意图。resolveActivity() 方法用于检查是否有应用能够处理这个意图。如果有,那么使用 startActivity() 方法启动一个能够浏览指定网址的应用;如果没有,你可以提示用户没有应用可以处理这个请求。

意图过滤器(Intent Filter)示例

为了让 SecondActivity 能够响应隐式意图,你需要在应用的清单文件(AndroidManifest.xml)中为 SecondActivity 添加意图过滤器:

1
2
3
4
5
6
7
8
<activity android:name=".SecondActivity">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="https" android:host="www.example.com" />
</intent-filter>
</activity>

这个意图过滤器声明 SecondActivity 能够处理查看(ACTION_VIEW)动作的意图,且这些意图的数据URI符合特定的模式(在这个例子中是打开 https://www.example.com 的网页)。这样,当有符合这些条件的隐式意图被触发时,SecondActivity 就可以作为处理该意图的候选。

这些代码样例提供了使用显式和隐式意图进行基本Android应用开发操作的简单入门指南。

Bundle and Parcel and Configuration Change

Bundle and Parcel

数据序列化的必要性

当你在内存中引用一个对象时,这个对象可能包含对其他对象的引用。如果你直接将这个对象的字节写入磁盘,然后在之后读取回来,原来的对象引用所指向的其他对象可能已经不存在了。这是因为对象引用指向的是内存中的位置,而当程序重新运行时,这些位置所存储的内容可能已经发生了变化或者被释放了。

数据序列化(Serialization)

数据序列化是解决上述问题的过程。它涉及到将一个对象及其引用的所有对象转换成一个字节流,这个字节流包含了所有实际的数据而不是数据的引用。这样,当你从磁盘读取这个字节流时,你可以通过反序列化(Deserialization)来重建原始对象及其引用的对象。这个过程有时也被称为“扁平化(Flattening)”和“非扁平化(Unflattening)”。

序列化在Android中的应用

在Android开发中,序列化不仅用于数据持久化(例如将对象写入磁盘),也用于在不同的活动(Activities)或不同的进程之间传递数据。由于不同活动或进程不能直接访问相同的对象内存,所以需要将对象及其引用的数据转换成序列化的形式,以便传递实际数据而非引用。

Bundle 和 Map

Bundle 在Android中用于在活动之间传递数据,它类似于Java中的**Map,包含键值对。虽然Map也可以用于存储键值对,但Bundle相比之下对值的类型有更严格的限制,以确保序列化的数据不包含对外部对象的引用。这样,当Bundle**被传递时,它包含的是实际数据,而不是数据的引用,确保了数据的完整性和独立性。

Parcel

Parcel 是**Bundle的一个更灵活的版本,提供了一个定义好的接口,允许你在自己的代码中处理序列化和反序列化,而不是完全依赖Android系统来做这些工作。如果你不确定使用哪个,通常建议使用Bundle**,因为它更简单,且适用于大多数情况。

Configuration

For example, a checkbox that was checked will end up unchecked in the newly created activity.

There are three basic solutions:

When the activity is being destroyed, onSaveInstanceState() will called.  Override this method to save the state in a Bundle when.  When the activity is created again, the Bundle will be passed to it, and you can restore the state from the Bundle.  Review the course materials on Bundle and Parcel, and then read the Configuration Changes section in this link:

https://developer.android.com/reference/android/app/Activity#configuration-changesLinks to an external site.