【第一行代码学习笔记】第三章 Activity

本文详细讲解了Android中的Activity组件,包括创建与布局、在AndroidManifest中的注册、Toast和Menu的使用,以及Intent在Activity间穿梭的方法,涉及隐式Intent、传递数据和启动模式。还介绍了Kotlin中的标准函数和静态函数应用。
摘要由CSDN通过智能技术生成

第3章 Activity

Activity:包含用户界面的组件,主要用于和用户进行交互。

3.1 Activity的基本用法

3.1.1 手动创建Activity
  • LauncherActivity:自动将当前Activity设置为项目的主Activity;
  • Generate Latout File:自动为当前Activity创建一个对应的布局文件;
  • Backwards Compatibility:自动为当前Activity创建一个对应的布局文件;

目前的AS已经没有后面两个选项了,应该是默认勾选。

3.1.2 创建和加载布局

app/src/main/res 目录下创建一个新的layout Directory。然后右键layout目录创建Layout Resource file即可。

添加新元素的示例如下,添加一个按钮Button:

    <Button
        android:id="@+id/button1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Button 1"
    />

接着回到Activity中,在onCreate( )函数中写下如下代码即可添加布局:

setContentView(R.layout.first_layout) // 这里的layout文件名是first_layout

后面有用ViewBinding来引入布局的方法

3.1.3 在AndroidManifest文件中注册

所有Activity必须在AndroidManifest文件中进行注册才能生效。AS已经帮我自动注册过了。但是由于没有勾选Launcher Activity,程序不知道首先启动哪个Activity,因此需要声明:

    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
3.1.4 Toast

Toast就是弹出一个浮框来通知,一段时间会自动消失。
一种方法是在onCreate( )中添加如下代码:

val button1: Button = findViewById(R.id.button1)
button1.setOnClickListener {
    Toast.makeText(this, "You clicked Button 1", Toast.LENGTH_SHORT).show()
}

在之前的版本中,Kotlin编写的项目会在gradle文件头部自动引入kotlin-android-extension插件,这样可以直接引入布局中的button1。但是现在该插件已经被舍弃,取而代之的是ViewBinding. 使用方法如下:

val binding = FirstLayoutBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.button1.setOnClickListener {
    Toast.makeText(this, "You clicked Button 1", Toast.LENGTH_SHORT).show()
}
3.1.5 Menu
  1. 在res目录中创建一个menu文件夹,再在其中创建Menu Resource file,在其中添加相应代码;
  2. 在Activity中重写onCreateOptionMenu( )方法,此时菜单就显示出来了;
  3. 在Activity中重写onOptionsItemSelected( )方法,定义菜单响应事件。

但是此时在真机上运行程序,会发现没有标题栏。原因是现在的安卓手机都默认隐藏标题栏了,所以menu不会显示出来。在themes.xml中添加如下代码可以解决问题:

<item name="android:windowActionBar">true</item>
<item name="android:windowNoTitle">false</item>

但是,这样做之后会发现在新建一个Activity后,后者会拥有两个标题栏。而不添加该代码的话,原Activty没有标题栏,但是新的Activity会有标题栏。

3.2 使用Intent在Activity之间穿梭

Intent是Android程序中各个组件之间 进行交互的一种重要方式,不仅可以指明当前组件想要执行的动作,还可以在不同组件之间传递数据。Intent一般可用于启动Activity、启动Service、以及发送广播等场景。

3.2.1 显式Intent
Intent(this, SecondActivity::class.java)

前者指定上下文,后者指定要跳转的Activity

3.2.2 隐式Intent

现在AndroidManifest文件中指定某个Activity的action和category(每个intent只能指定一个action,但可以指定很多category),例如:

<intent-filter>
    <action android:name="com.example.activitytest.ACTION_START"></action>
    <category android:name="android.intent.category.DEFAULT"></category>
    <category android:name="com.example.actvitytest.MY_CATEGORY"></category>
</intent-filter>

之后,在Activity中指定Intent如下:

	button1.setOnClickListener {
    	val intent = Intent("com.example.activitytest.ACTION_START")
    	intent.addCategory("com.example.actvitytest.MY_CATEGORY")
    	startActivity(intent)
	}

就可以通过button1跳转到另一个Activity了。这种通过action和category来使系统自动匹配要跳转的Activity的方法叫做隐式Intent。

3.2.3 更多隐式Intent的用法
  • 打开网页:
    修改button1的内容如下:
	button1.setOnClickListener {
		val intent = Intent(Intent.ACTION_VIEW)
		intent.data = Uri.parse("https://www.buaa.edu.cn")
		startActivity(intent)
	}

这里的Intent.ACTION_VIEW是一个Android的内置动作。Uri.parse则将传入的字符串解析成网址。之后点击button1则会直接跳转到该网址中去。
此外,还可以通过在<intent-filter>标签中添加<data>标签来更精确地指定当前Activity能够相应的数据。<data>标签中主要可以配置以下内容:

  • android:scheme :用于指定数据的协议部分,如上例中的https部分;
  • android:host:指定数据的主机名部分,如上例中的www.buaa.edu.cn;
  • android:port:指定数据的端口部分,一般紧随在主机名之后;
  • android:path:指定数据的主机名和端口之后的部分,如一段网址中跟在域名之后的内容;
  • android:mimeType:用于指定可以处理的数据类型,允许使用通配符的方式进行指定。

书上之后通过新建了一个ThirdActivity,在AndroidManifest中为其添加了一个intent-filter,加入了<data android:scheme="https" />来使得Button1即可以转到www.buaa.edu.cn,又可以跳转到ThirdActivity。系统会提示用户选择想要的选项。但是!我在真机上试验的时候,系统直接跳转到www.buaa.edu.cn,无法跳转至ThirdActivity。目前猜测可能是在真机的设置问题。

3.2.4 向下一个Activity传递数据

Intent除了可以启动另一个Activity之外,还可以同时传递数据。
在FirstActivity中将Button1的代码这样编写:

	button1.setOnClickListener {
		val data=("hello I'm nayufeng")
		val intent= Intent(this, SecondActivity::class.java)
		intent.putExtra("nyf",data)
		startActivity(intent)
	}

这里使用显式Intent跳转至SecondActivity,并且将data一并传递过去。注意intent.putExtra()函数接受两个值,第一个值是键,第二个值是要传递的数据。

接下来就可在SecondActivity中将数据取出并打印出来:

class SecondActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.second_layout)
        val extraData= intent.getStringExtra("nyf")
        Log.d("SecondActivity", "extraData is: $extraData")
    }
}
3.2.5 返回数据给上一个Activity

使用startActivityForResult 方法:在Activity销毁时能够返回一个数据给上一个Activity。该方法接受两个参数:

  1. Intent;·
  2. 请求码。用于在之后的回调中判断数据的来源,是一个唯一值即可。

修改FirstActivity中Button1的代码:

	button1.setOnClickListener {
		val intent= Intent(this, SecondActivity::class.java)
    	startActivityForResult(intent, 1)
	}

接下来再SecondActivity中给按钮注册点击事件,并在事件中添加返回数据的逻辑:

class SecondActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.second_layout)
        val button2: Button = findViewById(R.id.button2)
        button2.setOnClickListener {
            val intent = Intent()
            intent.putExtra("data_return", "Hello FirstActivity")
            setResult(RESULT_OK, intent) // 这个方法专门用来向上一个Activity返回结果
            finish() //销毁当前Activity
        }
    }
}

我们首先构建了一个Intent,只不过该Intent仅用于传递数据而已,没有指定任何“意图”。接着使用.putExtra把数据存放在Intent中。接着使用setResult方法接受两个参数:第一个参数一般只使用RESULT_OK 或者RESULT_CANCELED;第二个参数则把带有Intent的参数传递回去。最后销毁当前Activity。
销毁之后,程序会回调上一个Activity并调用onActivityResult方法,我们需要重写该方法来获得返回的数据:

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        when (requestCode) {
            1 -> if (resultCode == RESULT_OK){
                val returnedData = data?.getStringExtra("data_return")
                Log.d("FirstActivity", "returned data is: $returnedData")
            }
        }
    }

为了使得用户使用Back键返回FirstActivity时,也可以返回数据,我们需要重写onBackPressed方法:

    override fun onBackPressed() {
        super.onBackPressed()
        val intent = Intent()
        intent.putExtra("data_return", "Hello FirstActivity")
        setResult(RESULT_OK, intent) // 这个方法专门用来向上一个Activity返回结果
        finish()
    }

3.4 Activity的生命周期

3.4.1 返回栈

Android使用task来管理Activity,一个task就是一组存放在栈里的Activity的集合,这个栈也被称为返回栈(back stack)。栈后进先出。

3.4.2 Activity的状态

每个Activity再其生命周期中最多可能会有4种状态:

  1. 运行状态:位于返回栈的栈顶的Activity;
  2. 暂停状态:不处于栈顶,但仍然可见的Activity。例如对话框形式的Activity只占据屏幕中的部分区域;
  3. 停止状态:不处于栈顶,且完全不可见的Activity。系统仍然保留其相应的状态和成员变量,但并不可靠。可能会被系统回收以保证其他地方所需要的内存;
  4. 销毁状态:被移出返回栈的Activity。
3.4.3 Activity的生存期

Activity类中定义了7个回调方法,覆盖了Activity生命周期的每一个环节。

  • onCreate:会在Activity第一次被创建时调用。在此方法中完成初始化操作,例如加载布局、绑定事件等;
  • onStart:在Activity由不可见变为可见时调用;
  • onResume:在Activity准备好和用户进行交互的时候调用。此时的Activity一定位于返回栈的栈顶并且处于运行状态;
  • onPause:这个方法在系统准备去启动或者恢复另一个Activity的时候调用。通常会在这个方法中将一些消耗CPU的资源释放掉,以保存一些关键数据。但此方法的执行速度一定要快,不然会影响新的栈顶Activity调用;
  • onStop:这个方法在Activity完全不可见的时候调用。它和onPause的主要区别在于,如果启动的新Activity是对话框形式,那么会执行onPause方法,而不是onStop方法;
  • onDestroy:这个方法在Activity被销毁之前调用,之后Activity的状态将变为销毁状态;
  • onRestart:这个方法在Activity由停止状态变为与运行状态之前调用,也就是Activity被重新启用了。

以上方法除了 onRestart之外,其他都是两两相对的,从而又可以将Activity分为3种生存期:

  1. 完整生存期:在onCreate和onDestroy方法之间。前者完成初始化操作、后者完成释放内存的操作;
  2. 可见生存期:在onStart和onStop方法之间。在此期间,Activity对于用户始终可见,但可能无法交互。
  3. 前台生存期:在onResume和onPause方法之间。在此期间,Activity总是处于运行状态,且可与用户交互。
3.4.4 恢复数据

如果Activity被回收了,那么临时存储的数据将会丢失。这该怎么办呢?

只需添加如下代码即可将临时数据存储下来:

    override fun onSaveInstanceState(outState: Bundle) {
        super.onSaveInstanceState(outState)
        val tempData = "temporary data"
        outState.putString("data_key", tempData)
    }

之后,我们通过onCreate方法的Bundle类型参数savedInstanceState就可以获得保存的数据了:

        if (savedInstanceState != null){
            val tempData = savedInstanceState.getString("data_key")
            Log.d(tag, "tempData is $tempData")
        }
3.4.5 Bundle和Intent结合来传输数据

书上提到了这个方式。思路是先将需要传递的数据都保存在Bundle对象中,然后再将Bundle对象存放在Intent里。到目标Activity中先从Intent中取出Bundle,再从Bundle中一一取出数据。先将MainActivity的onCreate方法添加下列代码:

        val bundle = Bundle()
        bundle.putString("di1ju", "nihao")
        bundle.putString("di2ju", "zaijian")

        val startNormalActivity: Button = findViewById(R.id.startNormalActivity)
        startNormalActivity.setOnClickListener {
            val intent = Intent(this, NormalActivity::class.java)
            intent.putExtra("bundle_key", bundle)
            startActivity(intent)

之后是NormalActivity:

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.normal_layout)

        val bundle = intent.getBundleExtra("bundle_key")
        val extraData1 = bundle?.getString("di1ju")
        val extraData2 = bundle?.getString("di2ju")
        Log.d(tag, "extraData is: $extraData1")
        Log.d(tag, "extraData is: $extraData2")
}

就是要注意这里的key比较多,容易搞混。

3.5 Activity的启动模式

四种启动模式:standard(默认)、singleTopsingleTasksingleInstance

  1. standard:启动的Activity会自动进入返回栈的栈顶,系统不会在意这个Activity是否已经在返回栈中存在,每次启动都会新建一个该Activity的新实例。
  2. singleTop:启动Activity时如果返回栈栈顶已经是该Activity,则认为可以直接使用它,不会再创建新的实例。
  3. singleTask:保证整个app上下文只存在一个实例:每次启动前会检查返回栈中是否存在该Activity的实例,如果存在则直接使用该实例并将该Activity以上的所有其他Activity统统出栈。
  4. singleInstance:指定为该模式的Activity会启用一个新的返回栈来管理这个Activity。这就解决了多个应用程序需要共享某个Activity实例的情况。

3.6 Kotlin小课堂

3.6.1 标准函数

介绍了三种标准函数:withrunapply。三种函数的作用是一致的,在用法上有些许不同。
当我们需要调用同一对象的多种不同方法时,会用到这三种函数。他们的区别是:

  • with:将对象作为参数传入with函数,函数内表达式的最后一行作为返回结果;
  • run:调用对象的run方法,函数内表达式的最后一行作为返回结果;
  • apply:调用对象的apply方法,返回结果是对象本身;
3.6.2 静态函数

Kotlin没有直接定义静态函数的关键字,单例类(object)和companion object模仿静态方法的调用方式,基本满足了开发需求。

如果需要静态函数的话,有以下两种方法:

  • 注解:在companion object内定义的方法上加@JvmStatic
  • 顶层方法:没有定义在任何类内的方法就是顶层方法,会被自动编译成静态方法。
  • 6
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值