2.4 更新控制器层
在上一章,应用控制器层的MainActivity类的处理逻辑很简单:显示定义在activity_main.xml文件中的布局对象,为两个按钮设置监听器,响应用户点击事件并创建toast消息。
既然现在有更多的地理知识问题可以检索与展示,MainActivity类就需要更多的处理逻辑来让GeoQuiz应用的模型层与视图层协作。
打开MainActivity.kt文件,如代码清单2-5所示,创建一个Question对象集合以及该集合的索引变量。
代码清单2-5 增加Question对象集合(MainActivity.kt)
class MainActivity : AppCompatActivity() {
private lateinit var trueButton: Button
private lateinit var falseButton: Button
private val questionBank = listOf(
Question(R.string.question_australia, true),
Question(R.string.question_oceans, true),
Question(R.string.question_mideast, false),
Question(R.string.question_africa, false),
Question(R.string.question_americas, true),
Question(R.string.question_asia, true))
private var currentIndex = 0
...
}
这里,我们通过多次调用Question类的构造函数,创建了Question对象集合。
(在较复杂的项目里,这类集合的创建和存储会单独处理。在后续应用开发中,你会看到更好的模型数据存储方式。现在,简单起见,我们选择在控制器层代码中创建集合。)
要在屏幕上显示一系列地理知识问题,可以使用questionBank、currentIndex变量以及Question对象的存取方法。
如代码清单2-6所示,首先给TextView和新Button添加属性,然后引用它们,并设置TextView显示当前集合索引所指向的地理知识问题(稍后会设置NEXT按钮的点击事件监听器)。
代码清单2-6 使用TextView(MainActivity.kt)
class MainActivity : AppCompatActivity() {
private lateinit var trueButton: Button
private lateinit var falseButton: Button
private lateinit var nextButton: Button
private lateinit var questionTextView: TextView
...
override fun onCreate(savedInstanceState: Bundle?) {
...
trueButton = findViewById(R.id.true_button)
falseButton = findViewById(R.id.false_button)
nextButton = findViewById(R.id.next_button)
questionTextView = findViewById(R.id.question_text_view)
trueButton.setOnClickListener { view: View ->
...
}
falseButton.setOnClickListener { view: View ->
...
}
val questionTextResId = questionBank[currentIndex].textResId
questionTextView.setText(questionTextResId)
}
}
保存所有文件,确保没有错误发生,然后运行GeoQuiz应用。可以看到,集合存储的第一个问题显示在TextView上了。
现在来处理NEXT按钮,为其设置监听器View.OnClickListener。该监听器的作用是让集合索引递增并相应地更新TextView的文本内容,如代码清单2-7所示。
代码清单2-7 使用新增的按钮(MainActivity.kt)
override fun onCreate(savedInstanceState: Bundle?) {
...
falseButton.setOnClickListener { view: View ->
...
}
nextButton.setOnClickListener {
currentIndex = (currentIndex + 1) % questionBank.size
val questionTextResId = questionBank[currentIndex].textResId
questionTextView.setText(questionTextResId)
}
val questionTextResId = questionBank[currentIndex].textResId
questionTextView.setText(questionTextResId)
}
注意到了吗?同样的questionTextView文字赋值代码出现在了两个不同的地方。参照代码清单2-8,花点儿时间把这样的公共代码放到一个函数里,然后分别在nextButton监听器里以及onCreate(Bundle?)函数的末尾调用它。后一个调用是为了初始化设置activity视图中的文本。
代码清单2-8 使用updateQuestion()封装公共代码(MainActivity.kt)
class MainActivity : AppCompatActivity() { ... override fun onCreate(savedInstanceState: Bundle?) { ... nextButton.setOnClickListener { currentIndex = (currentIndex + 1) % questionBank.sizeval questionTextResId = questionBank[currentIndex].textResIdquestionTextView.setText(questionTextResId)updateQuestion() }val questionTextResId = questionBank[currentIndex].textResIdquestionTextView.setText(questionTextResId)updateQuestion() } private fun updateQuestion() { val questionTextResId = questionBank[currentIndex].textResId questionTextView.setText(questionTextResId) } }
运行GeoQuiz应用,验证新添加的NEXT按钮。
如果一切正常,问题应该已经完美显示出来了。当前,GeoQuiz应用认为所有问题的答案都是true,下面着手修正这个逻辑错误。同样,为避免代码重复,我们将解决方案封装在一个私有函数里。
要添加到MainActivity类的函数如下:
private fun checkAnswer(userAnswer: Boolean)
该函数接受布尔类型的变量参数,判别用户点击了TRUE还是FALSE按钮。然后,将用户的答案同当前Question对象中的答案做比较,判断正误,并生成一个toast消息反馈给用户。
在MainActivity.kt文件中,添加checkAnswer(Boolean)函数的实现代码,如代码清单2-9所示。
代码清单2-9 增加checkAnswer(Boolean)函数(MainActivity.kt)
class MainActivity : AppCompatActivity() {
...
private fun updateQuestion() {
...
}
private fun checkAnswer(userAnswer: Boolean) {
val correctAnswer = questionBank[currentIndex].answer
val messageResId = if (userAnswer == correctAnswer) {
R.string.correct_toast
} else {
R.string.incorrect_toast
}
Toast.makeText(this, messageResId, Toast.LENGTH_SHORT)
.show()
}
}
在按钮的监听器里,调用checkAnswer(Boolean)函数,如代码清单2-10所示。
代码清单2-10 调用checkAnswer(Boolean)函数(MainActivity.kt)
override fun onCreate(savedInstanceState: Bundle?) { ... trueButton.setOnClickListener { view: View ->Toast.makeText(this,R.string.correct_toast,Toast.LENGTH_SHORT).show()checkAnswer(true) } falseButton.setOnClickListener { view: View ->Toast.makeText(this,R.string.correct_toast,Toast.LENGTH_SHORT).show()checkAnswer(false) } ... }
运行GeoQuiz应用,确认toast消息基于用户点击给出了正确反馈。