美文网首页
《Android编程权威指南》之第二个activity

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

作者: 夜远曦白 | 来源:发表于2021-08-25 23:02 被阅读0次

    本章,是为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

          本文链接:https://www.haomeiwen.com/subject/bjyliltx.html