第一行代码:Android读书笔记(一)
app/src/main/res目录内容:
.
├── drawable 图片目录
│ └── ic_launcher_background.xml
├── drawable-v24
│ └── ic_launcher_foreground.xml
├── layout 布局文件
│ └── activity_main.xml
├── mipmap-anydpi-v26
│ ├── ic_launcher.xml
│ └── ic_launcher_round.xml
├── mipmap-hdpi
│ ├── ic_launcher.png
│ └── ic_launcher_round.png
├── mipmap-mdpi
│ ├── ic_launcher.png
│ └── ic_launcher_round.png
├── mipmap-xhdpi
│ ├── ic_launcher.png
│ └── ic_launcher_round.png
├── mipmap-xxhdpi 应用图标目录
│ ├── ic_launcher.png
│ └── ic_launcher_round.png
├── mipmap-xxxhdpi
│ ├── ic_launcher.png
│ └── ic_launcher_round.png
├── values 字符串,样式,颜色配置目录
│ ├── colors.xml
│ ├── strings.xml
│ └── themes.xml
└── values-night
└── themes.xml
- 在代码中,我们通过
R.string.app_name
可以获得对某个字符串的引用 - 在XML中,通过
@string/app_name
可以获得对某个字符串的引用
基本的语法就是如上的两种方式,其中string部分是可以替换的,如果引用的是图片资源则使用drawable
,如果引用的是应用图标,则可以替换成mipmap
。如果引用的是布局文件,则可以替换成layout
。
任何Activity都要继承AppCompatActivity
类,并且重写onCreate(bundle)
方法,然后在app/src/main/res
目录的AndroidManifest.xml文件中注册Activity。对于主Activity,我们需要使用<intent-filter>
标签指定其为主Activity。一个示例如下:
<activity android:name=".MainActivity"
android:label="Demo">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
其中的android:label
属性指定了Activity中标题栏的内容,还会成为启动器中应用程序所显示的名称。如果一个应用程序没有指定任何主Activity,则其无法在启动器中看到和打开,只能作为第三方服务供其他应用在内部进行调用。
Activity
显式Intent和隐式Intent
显式Intent(通过schme+host+port+path+mimeType
显式打开一个Activity)
binding.button1.setOnClickListener(){
val intent = Intent(Intent.ACTION_VIEW)
intent.data= Uri.parse("https://www.google.com.hk")
startActivity(intent)
}
隐式Intent(通过schme
隐式打开一个Activity)
<activity android:name=".SecondActivity">
<intent-filter tools:ignore="AppLinkUrlError">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="https"/>
</intent-filter>
</activity>
其中标签被包含于mimeType
属性),可以是只有 URI,也可以是既有数据类型又有 URI。URI 由其各个部分的单独属性指定:
<scheme>://<host>:<port>[<path>|<pathPrefix>|<pathPattern>]
⚠️用于指定网址格式的以下属性是可选的,但也相互依赖:
- 如果没有为 Intent 过滤器指定
scheme
,则系统会忽略其他所有 URI 属性。 - 如果没有为过滤器指定
host
,则系统会忽略port
属性以及所有路径属性。
同一 <intent-filter>
元素中包含的所有 <data>
元素都对同一过滤器起作用。例如,以下过滤器规范:
<intent-filter . . . >
<data android:scheme="something" android:host="project.example.com" />
. . .
</intent-filter>
等同于以下规范:
<intent-filter . . . >
<data android:scheme="something" />
<data android:host="project.example.com" />
. . .
</intent-filter>
您可以在 <intent-filter>
内放置任意数量的 <data>
元素,为其提供多个数据选项。它的属性都没有默认值。而显然,intent过滤器是通过匹配intent对象的规则来工作的。
属性:
android:scheme
URI 的模式部分。这是指定 URI 时最基本的属性;必须至少为过滤器设置一个 scheme
属性,否则其他 URI 属性都没有意义。
指定的模式应不带尾随冒号(例如,应指定 http
,而不是 http:
)。
如果为过滤器设置了数据类型(mimeType
属性),但未设置模式,则采用 content:
和 file:
架构。
⭐注意:Android 框架中的架构匹配区分大小写,这一点与 RFC 不同。因此,您应始终使用小写字母指定架构。
android:host
URI 授权方的主机部分。除非也为过滤器指定了 scheme
属性,否则此属性没有意义。要匹配多个子网域,请使用星号 (*
) 匹配主机中的零个或多个字符。例如,主机 *.google.com
匹配 www.google.com
、.google.com
和 developer.google.com
。
星号必须是主机属性的第一个字符。例如,主机 google.co.*
无效,因为星号通配符不是第一个字符。
⭐注意:Android 框架中的主机名匹配区分大小写,这一点与正式的 RFC 不同。因此,您应始终使用小写字母指定主机名。
android:port
URI 授权方的端口部分。仅当同时为过滤器指定了 scheme
和 host
属性时,此属性才有意义。
android:path
android:pathPrefix
android:pathPattern
URI 的路径部分,必须以 / 开头。path
属性指定与 Intent 对象中的完整路径匹配的完整路径。pathPrefix
属性指定只与 Intent 对象中的路径的初始部分匹配的部分路径。pathPattern
属性指定与 Intent 对象中的完整路径匹配的完整路径,但它可以包含以下通配符:
- 星号(“
*
”)匹配出现零次到多次的紧邻前面的字符的一个序列。 - 句点后跟星号(“
.*
”)匹配零个到多个字符的任意序列。
由于在从 XML 读取字符串时(在将其解析为模式之前)将“”用作转义字符,因此您需要进行双重转义。例如,字面量“
*
”将编写为“\*
”,字面量“”将编写为“\\
”。这基本上与采用 Java 代码构造字符串时需要编写的内容一样。详细类型可以查PatternMatcher
类中的PATTERN_LITERAL
、PATTERN_PREFIX
和PATTERN_SIMPLE_GLOB
三个常量的说明。
仅当同时为过滤器指定了 scheme
和 host
属性时,这些属性才有意义。
android:mimeType
MIME 媒体类型,如 image/jpeg
或 audio/mpeg4-generic
。子类型可以是星号通配符 (*
),以指示任何子类型都匹配。
Intent 过滤器经常会声明仅包含 android:mimeType
属性的 <data>
。
⭐注意:Android 框架中的 MIME 类型匹配区分大小写,这一点与正式的 RFC MIME 类型不同。因此,您应始终使用小写字母指定 MIME 类型。
在Activity之间传递数据
向下一个Activity传递数据
我们可以直接调用Intent类的putExtra
方法来向Intent对象中传入数据。例如:
val someData="Hello, second Activity"
val intent = Intent(FirstActivity.this, SecondActivity::class.java)
intent.putExtra("some_data", someData)
startActivity(intent)
然后在下一个Activity中拿到对应的数据,例如:
val data:String=intent.getStringData("some_data")
很容易想到,Intent对象肯定还有些重载的方法,比如:getIntExtra(..), getBooleanExtra(..)
等。
为了某个数据启动Activity
有时候,我们启动某个Activity只是单纯的为了获得数据,这个时候,我们会调用startActivityForResult(..)
。
首先,我们会在视图绑定中启动一个Activity:
binding.button1.setOnClickListener{
intent = Intent(this, SecondActivity::class.java)
// 第一个参数是Intent,第二个参数是一个RequestCode,响应时用来区分是哪个活动
startActivityForResult(intent, 1)
}
然后,我们在某个Activity中对数据进行填充:
binding.button2.setOnClickListener{
intent = Intent()
intent.putExtra("data_return", "Hello, first Activity")
setResult(RESULT_OK, intent)
finish()
}
当我们启动第二个Activity并点击了button2之后,便会回调第一个Activity中的onActivityResult(..)
函数,这个时候,我们响应就好:
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
when(requestCode){
1 -> {
if(resultCode == RESULT_OK){
returnedData=data.getStringExtra("data_return")
Log.d("FirstActivity", returnedData)
}
}
}
}
Activity生命周期
Activity的七个回调方法:
onCreate()
:会在Activity第一次被创建的时候调用。在此方法中可以完成Activity的初始化,比如加载布局,绑定事件等onStart()
:这个方法在Activity由不可见变成可见的时候被调用onResume()
:这个方法在Activity准备好和用户进行交互的时候被调用onPause()
:这个方法在系统准备去启动或者恢复另一个Activity的时候被调用。可以在这个方法中将一些耗CPU的资源释放掉,以及保存一些关键数据,不过这就要求这个过程一定要快。onStop()
:在App完全不可见的时候被调用。和onPause
方法的不同之处在于,如果启动的新Activity是一个对话框式的Activity,那么onPause
被执行而onStop
方法不会被执行。onDestroy()
:这个方法在Activity被销毁之前调用,之后的Activity将变为销毁状态。onRestart()
:这个方法在Activity由停止状态变为运行状态之前调用,也就是Activity被重新启动了。
Activity的启动模式
-
"standard"
(默认模式)默认值。系统在启动该 Activity 的任务中创建 Activity 的新实例,并将 intent 传送给该实例。Activity 可以多次实例化,每个实例可以属于不同的任务,一个任务可以拥有多个实例。
-
"singleTop"
如果当前任务的顶部已存在 Activity 的实例,则系统会通过调用其
onNewIntent()
方法来将 intent 转送给该实例,而不是创建 Activity 的新实例。Activity 可以多次实例化,每个实例可以属于不同的任务,一个任务可以拥有多个实例(但前提是返回堆栈顶部的 Activity 不是该 Activity 的现有实例)。例如,假设任务的返回堆栈包含根 Activity A 以及 Activity B、C 和位于顶部的 D(堆栈为 A-B-C-D;D 位于顶部)。收到以 D 类型 Activity 为目标的 intent。如果 D 采用默认的
"standard"
启动模式,则会启动该类的新实例,并且堆栈将变为 A-B-C-D-D。但是,如果 D 的启动模式为"singleTop"
,则 D 的现有实例会通过onNewIntent()
接收 intent,因为它位于堆栈顶部,堆栈仍为 A-B-C-D。但是,如果收到以 B 类型 Activity 为目标的 intent,则会在堆栈中添加 B 的新实例,即使其启动模式为"singleTop"
也是如此。 -
"singleTask"
系统会创建新任务,并实例化新任务的根 Activity。但是,如果另外的任务中已存在该 Activity 的实例,则系统会通过调用其
onNewIntent()
方法将 intent 转送到该现有实例,而不是创建新实例。Activity 一次只能有一个实例存在。 -
"singleInstance"
与
"singleTask"
相似,唯一不同的是系统不会将任何其他 Activity 启动到包含该实例的任务中。该 Activity 始终是其任务唯一的成员;由该 Activity 启动的任何 Activity 都会在其他的Task中打开。
我们在AndroidManifest.xml
文件的<Activity>
标签中完成这些指定:
<activity
android:name=".SecondActivity"
<!-- 指定以singleTop模式启动SecondActivity-->
android:launchMode="singleTop">
<intent-filter tools:ignore="AppLinkUrlError">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="https"/>
</intent-filter>
</activity>