《Android编程权威指南》之第二个activity

本章,是为GeoQuiz应用添加第二个activity。

一、创建第二个activity

创建一个新的Activity,取名为CheatActivity,它对应的布局文件名为 activity_cheat,当然还有 activity 需要在 AndroidManifest.xml 注册。

  • 因此在创建的时候可选择直接 New Android Activity,这样 AS 可以自动帮我们生成布局文件以及注册代码。
create activity
  • 这里介绍一下布局预览,可以直接切换成横屏预览,偶尔有的布局可以不必特地为横屏再多写一套布局代码,如果直接横屏预览没有适配问题的话。
预览横屏效果
  • 再介绍了一个tools:text属性,在textview中使用这个,预览的时候能看到文字显示,实际运行不会显示,预览很方便!

  • AS快捷键Command+Shift+O(或Ctrl+Shift+N)快速打开文件。

二、启动 activity

startActivity(Intent)函数,调用请求实际上是发送给了操作系统的ActivityManager。ActivityManager负责创建Activity实例并调用其onCreate(Bundle?)函数,如图所示:

启动activity

基于intent的通信

intent 对象是 component (activity、service、broadcast receiver、content provider)用来与操作系统通信的一种媒介工具。

// 启动 CheatActivity
mBinding.btnCheat.setOnClickListener {                                  
    val intent = Intent(this, CheatActivity::class.java)               
    startActivity(intent)                                              
}                                                                      

在启动 activity 前,ActivityManager会确认指定的Class是否已在manifest配置文件中声明。如果已完成声明,则启动activity,应用正常运行。反之,则抛出ActivityNotFoundException异常,应用崩溃。这就是必须在manifest配置文件中声明应用的全部activity的原因。

显式intent与隐式intent

  • 显式intent:指定Context与Class对象,然后调用intent的构造函数来创建Intent。
  • 隐式intent:只要描述要完成的任务,操作系统就会找到合适的应用,并在其中启动相应的activity。

三、activity 间的数据传递

使用 intent extra

intent extra:activity间的通信与数据传递

在CheatActivity.kt中,写个伴生对象,拿到Intent,这种写法会比较方便,就是被打开这会告诉打开者是否需要携带参数,参数是什么。

private const val EXTRA_ANSWER_IS_TRUE = "answer_is_true";

class CheatActivity : AppCompatActivity() {

...

companion object {
        fun newIntent(packageContext: Context, answerIsTrue: Boolean): Intent {
            return Intent(packageContext, CheatActivity::class.java).apply {
                putExtra(EXTRA_ANSWER_IS_TRUE, answerIsTrue)
            }
        }
    }
}

这里涉及到了Kotlin伴生对象的概念,参考:https://www.kotlincn.net/docs/reference/object-declarations.html

从子activity获取返回结果

GeoQuiz应用内部的交互时序图

这里 startActivityForResult 已经被弃用了,当前 google 推荐registerForActivityResult 来替换它。具体详情参考官方文档:

https://developer.android.com/training/basics/intents/result?hl=zh-cn

因此,模仿案例,我的代码作了一点小修改。

MainActivity.kt 中:

class MainActivity : AppCompatActivity() {
···
  private val startForResult =
        registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
            if (it.resultCode == Activity.RESULT_OK) {
                quizViewModel.isCheater =
                    it.data?.getBooleanExtra(EXTRA_ANSWER_SHOW, false) ?: false
            }
        }
···

override fun onCreate(savedInstanceState: Bundle?) {
···
  mBinding.btnCheat.setOnClickListener {
            val answer = quizViewModel.currentQuestionAnswer
            startForResult.launch(CheatActivity.newIntent(this, answer))
        }
  }
}

CheatActivity.kt 中:

override fun onCreate(savedInstanceState: Bundle?) {
        ···
        mBinding.btnShowAnswer.setOnClickListener {
            val answerText = when {
                answer -> R.string.true_button
                else -> R.string.false_button
            }
            mBinding.tvAnswer.setText(answerText)

            val data = Intent().apply { putExtra(EXTRA_ANSWER_SHOW, isAnswerShown) }
            setResult(Activity.RESULT_OK, data)
        }
    }

这里代码还涉及到了 kotlin 中 apply 的使用
有关 kotlin 作用域函数语法详情参考:
https://www.kotlincn.net/docs/reference/scope-functions.html

四、activity的使用与管理

本小结要表达的就是,Android 管理任务和返回堆栈的方式是将所有接连启动的 Activity 放到同一任务和一个“后进先出”堆栈中。

然后从桌面点击应用图标启动的第一个activity,是在配置文件中,intent-filter元素节点被指定为launcher activity 的那个activity。

根据此特性,在我们的大多的项目中,都会封装一个统一管理acitivity的工具类,可以随时管理自己已打开的所有的activity,比如:https://www.jianshu.com/p/ed897d567b02

关于任务和返回堆栈详情参考:https://developer.android.com/guide/components/activities/tasks-and-back-stack?hl=zh-cn

五、挑战练习:堵住作弊漏洞

既然用户可以通过旋转CheatActivity来清除作弊痕迹,那么要解决此问题,当然就是利用前置知识,在设备旋转或者app被销毁也保存好此作弊痕迹数据就可以啦,其实跟前面也是一样的,用 ViewModel + onSaveInstanceState()的方式就OK。

六、挑战练习:按题记录作弊状态

当前,哪怕用户只在一道题上作弊,应用都会认为他们题题作弊。完善GeoQuiz应用,按题记录用户作弊情况。也就是说,如果用户偷看了某道题的答案,那就在他回答那道题时,弹出作弊警告消息。然后在继续答题过程中,如果用户不再作弊了,就给出答案正确与否的评判。

据我的审题噢,警告 Toast 在示例中就已经做了的,因此这个附加练习题,应该是本就有的功能。

在之前的章节,有个评分的挑战练习,我这里再改改评分逻辑,就是作弊的题目即使答对了,也不算作答对的题目数去计分,也就是作弊答对计0分。

最后

挑战练习都没有贴源码了,解决方案思路在此了。当然练习Demo和练习题都是要做一遍的。

个人实践代码地址:https://github.com/visiongem/AndroidGuideApp/tree/master/GeoQuiz

你可能感兴趣的:(《Android编程权威指南》之第二个activity)