[Android] Intent: 액티비티 간 데이터 전달

액티비티 시작시키기

startActivity(Intent)는 시작시키고자 하는 액티비티에 대해 호출하는 static 함수라고 생각할지도 모르겠지만 그렇지 않다. 액티비티에서 startActivity(Intent)를 호출하면 이 호출은 안드로이드 운영체제에게 전달된다.

조금 더 구체적으로 말해서, startActivity(Intent) 호출은 ActivityManager라고 하는 안드로이드 운영체제의 컴포넌트로 전달된다. 그다음에 ActivityManager는 해당 액티비티의 인스턴스를 생성하고 이 인스턴스의 onCreate(Bundle?) 함수를 호출한다.

그렇다면 어떤 액티비티를 시작시킬지 ActivityManager가 어떻게 알 수 있을까? 이 정보는 Intent 매개변수에 있다.

인텐트로 통신하기

인텐트(intent)컴포넌트가 운영체제와 통신하는 데 사용할 수 있는 객체다. 지금까지 보았던 컴포넌트는 액티비티뿐 activity이지만, 컴포넌트에는 서비스 service, 브로드캐스트 수신자 broadcast receiver, 콘텐츠 제공자 content provider도 있다. ※ 안드로이드 4대 컴포넌트

인텐트는 다목적 통신 도구로, 이것을 추상화한 Intent 클래스는 인텐트의 용도에 따라 서로 다른 생성자들을 제공한다. 여기서는 시작시킬 액티비티를 ActivityManager에 알려주려고 인텐트를 사용한다. 따라서 다음 생성자를 사용한다.

1
Intent(packageContext: Context, class: Class<?>)

CheatActivity 클래스를 인자로 받는 Intent 객체를 생성하고 이 인텐트를 startActivity(Intent) 인자로 전달하는 코드를 cheatButton의 리스너 내부에 추가한다.

MainActiviy에서 CheatActivity 시작시키기

1
2
3
4
5
cheatButton.setOnClickListener {
// CheatActivity를 시작시킨다
val intent = Intent(this, CheatActivity::class.java)
startActivity(intent)
}

Intent 생성자에게 전달하는 Class 인자에는 ActivityManager가 시작시켜야 하는 액티비티 클래스를 지정한다. 여기서 Context 인자는 이 액티비티 클래스가 있는 애플리케이션 패키지를 ActivityManager에게 알려준다.

액티비티를 시작시키기에 앞서, ActivityManager는 시작시킬 액티비티 클래스가 매니페스트의 activity 요소에 선언되어 있는지 확인한다. 만일 선언되어 있다면 해당 액티비티를 시작시키고, 선언되어 있지 않으면 ActivityNotFoundException이 발생되고 앱 실행이 중단된다. 모든 액티비티가 반드시 매니페스트에 선언되어야 하는 이유가 바로 이 때문이다.

명시적 인텐트와 암시적 인텐트

Context 객체와 Class 객체를 사용해서 생성하는 Intent는 명시적(explicit) 인텐트다. 명시적 인텐트는 앱 내부에 있는 액티비티를 시작시키기 위해 사용한다.

같은 앱 내부에 있는 두 개의 액티비티가 앱 외부의 ActivityManager를 통해서 통신하는 것이 이상하게 보일 수 있다. 그렇지만 이렇게 하면 한 애플리케이션의 액티비티가 다른 애플리케이션의 액티비티와 함께 동작하는 것이 쉬워진다.

한 애플리케이션의 액티비티에서 다른 애플리케이션의 액티비티를 시작시키려면 암시적(implicit) 인텐트를 생성한다.

액티비티 간의 데이터 전달

MainActivity와 CheatActivity 간의 통신

인텐트 엑스트라 사용하기

엑스트라는 호출하는 액티비티가 인텐트에 포함시킬 수 있는 임의의 데이터로, 생성자 인자로 생각할 수 있다(액티비티 인스턴스는 안드로이드 운영체제에 의해 생성되고 그 생명주기가 관리된다). 요청된 인텐트는 안드로이드 운영체제가 받아서 수신 액티비티에 전달한다. 그다음에 수신 액티비티는 해당 인텐트의 엑스트라로 전달된 데이터를 추출해 사용한다.

엑스트라는 키와 값이 한 쌍으로 된 구조로, MainActivity.onSaveInstanceState(Bundle)에서 currentIndex의 값을 저장하기 위해 사용했던 Bundle 객체의 구조와 동일하다.

인텐트에 엑스트라를 추가할 때는 Intent.putExtra(...) 를 사용하며, 주로 putExtra(name: String, value: Boolean)을 호출한다.

Intent.putExtra(...)는 여러 형태로 오버로딩되어 있지만 항상 두 개의 인자를 갖는다.

  1. 첫 번째 인자는 항상 String 타입이며 엑스트라로 저장할 데이터 항목의 키를 나타낸다.
  2. 두 번째 인자는 이 키의 값을 나타내며, 다양한 타입으로 오버로딩되어 있다.

엑스트라 상수 추가하기

1
2
3
4
5
private const val EXTRA_ANSWER_IS_TRUE = "com.june0122.geoquiz.answer_is_true"

class CheatActivity : AppCompatActivity() {
...
}
  • 액티비티 코드는 여러 곳에서부터 시작될 수 있다. 따라서 엑스트라 키는 엑스트라의 데이터를 읽어서 사용하는 액티비티에 정의해야 한다.

    • 이때 위의 코드처럼 엑스트라의 키 값에 패키지 이름을 사용하면 다른 앱의 엑스트라와의 충돌 방지가 가능하다.
  • 다음으로 MainActivity로 돌아가서 인텐트에 엑스트라를 쓰는 코드를 추가해야 한다. 그런데 더 좋은 방법이 있다.

    • MainActivity나 앱의 다른 코드에서는 CheatActivity가 인텐트의 엑스트라로 무엇을 받는지 알 필요가 없다.
    • 따라서 CheatActivity의 실행의 위해 인텐트를 요청하는 코드를 별도의 함수로 캡슐화하면 좋다.

아래와 같이 CheatActivity에 newIntent(...) 함수를 동반 객체 companion object 내부에 둔다.

CheatActivity의 newIntent(...) 함수 추가하기

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class CheatActivity : AppCompatActivity() {

override fun onCreate(savedInstanceState: Bundle?) {
...
}

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

이 함수에서는 CheatActivity가 필요로 하는 엑스트라 데이터를 갖는 인텐트를 생성한다. Boolean 타입의 answerIsTrue 인자는 EXTRA_ANSWER_IS_TRUE 상수 값을 키로 갖도록 엑스트라에 저장된다.

이처럼 동반 객체를 사용하면 클래스 인스턴스를 생성하지 않고 동반 객체의 함수를 사용할 수 있다. (java의 static 메서드와 유사) 따라서 아래에 나올 코드에서 CheatActivity.newIntent(this@MainActivity, answerIsTrue)처럼 동반 객체를 포함하는 클래스 이름을 사용해서 동반 객체의 함수를 호출할 수 있다. 또한, 동반 객체를 포함하는 클래스에서는 동반 객체를 자신의 멤버인 것처럼 인식하므로 편리하다.

엑스트라를 갖는 인텐트로 CheatActivity 시작시키기 (MainActivity.kt)

1
2
3
4
5
6
cheatButton.setOnClickListener {
// val intent = Intent(this, CheatActivity::class.java)
val answerIsTrue = quizViewModel.currentQuestionAnswer
val intent = CheatActivity.newIntent(this@MainActivity, answerIsTrue)
startActivity(intent)
}

여기서는 엑스트라 하나만 있으면 되지만, 필요하다면 newIntent(...) 함수에 더 많은 인자를 추가하여 Intent 하나에 여러 개의 엑스트라를 쓸 수 있다. 아래의 이미지와 같이 다양한 타입의 값을 엑스트라에 넣고 추출할 수 있다.

다음으로 CheatActivity 클래스의 onCreate(Bundle?) 함수에 코드를 추가한다. 이 코드에서는 인텐트로 전달된 엑스트라의 값을 추출해 클래스 속성에 저장한다.

엑스트라 사용하기

1
2
3
4
5
6
7
8
9
10
11
12
class CheatActivity : AppCompatActivity() {

private var answerIsTrue = false

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

answerIsTrue = intent.getBooleanExtra(EXTRA_ANSWER_IS_TRUE, false)
}
...
}

Activity.intent는 항상 액티비티를 시작시켰던 Intent 객체를 반환한다. 이 Intent 객체는 startActivity(Intent)를 호출할 때 인자로 전달되었던 객체다.

여기서 intent 대신 자바 스타일로 getIntent()를 사용해도 되지만 코틀린에서는 클래스 속성의 값을 가져올 떄 게터(getter)를 사용하지 않아도 된다. 속성을 참조할 때 자동으로 게터를 호출해주기 때문이다.

엑스트라에서 추출한 값을 사용하도록 CheatActiviy에 코드 추가

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class CheatActivity : AppCompatActivity() {

private lateinit var answerTextView: TextView
private lateinit var showAnswerButton: Button

private var answerIsTrue = false

override fun onCreate(savedInstanceState: Bundle?) {
...

answerIsTrue = intent.getBooleanExtra(EXTRA_ANSWER_IS_TRUE, false)
answerTextView = findViewById(R.id.answer_text_view)
showAnswerButton = findViewById(R.id.show_answer_button)
showAnswerButton.setOnClickListener {
val answerText = when {
answerIsTrue -> R.string.true_button
else -> R.string.false_button
}
answerTextView.setText(answerText)
}
}
...
}

자식 액티비티로부터 결과 돌려받기

자식 액티비티로부터 데이터를 돌려받고 싶다면 Activity.startActivityForResult(Intent, Int) 함수를 호출한다.

첫 번째 매개변수는 종전과 동일한 Intent 객체다. 두 번째 매개변수는 요청 코드(request code) 로 사용자가 정의한 정수다. 요청 코드는 자식 액티비티에 전달되었다가 부모 액티비티가 다시 돌려받으며 부모 액티비티가 여러 타입의 자식 액티비티들을 시작시킬 때 어떤 자식 액티비티가 결과글 돌려주는 것인지 알고자 할 때도 사용된다. 현재 MainActivity는 한 가지 타입의 자식 액티비티만 시작시키지만, 향후를 대비해서 요청 코드의 상수 값을 사용하는 것이 좋다.

MainActivity에서 startActivityForResult(Intent, Int)를 호출하기

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
private const val TAG = "MainActivity"
private const val KEY_INDEX = "index"
private const val REQUEST_CODE_CHEAT = 0

class MainActivity : AppCompatActivity() {
...
override fun onCreate(savedInstanceState: Bundle?) {
...
cheatButton.setOnClickListener {
...
// startActivity(intent)
startActivityForResult(intent, REQUEST_CODE_CHEAT)
}

updateQuestion()
}
...
}

결과 데이터 설정하기

부모 액티비티에 데이터를 돌려주기 위해 자식 액티비티에서 호출하는 함수에는 다음 두 가지가 있다.

1
2
setResult(resultCode: Int)
setResult(resultCode: Int, data: Intent)

일반적으로 결과 코드(result code) 는 사전 정의된 두 개의 상수, 즉 Activity.RESULT_OK (정수 -1), 또는 Activity.RESULT_CANCELED (정수 0) 중 하나다 (결과 코드를 정의할 때 RESULT_FIRST_USER(정수 1) 상수도 사용할 수 있다.).

자식 액티비티가 어떻게 끝났는지에 따라 부모 액티비티에서 다른 액션을 취할 때 결과 코드를 사용하면 유용하다. 예를 들어, 자식 액티비티가 OK 버튼과 Cancel 버튼을 갖고 있고, 둘 중 어떤 버튼이 눌러졌는가에 따라 자식 액티비티가 결과 코드 값을 다르게 설정한다고 해보자. 그러면 부모 액티비티는 해당 결과 코드 값에 따라 다른 액션을 취할 수 있다.

자식 액티비티에서는 setResult(...)를 호출하지 않을 수도 있다. 부모 액티비티에서 어떤 결과 코드인지 구분할 필요가 없거나 인텐트의 데이터를 받을 필요가 없다면, 안드로이드 운영체제가 기본 결과 코드를 전달하게 할 수 있다. 자식 액티비티가 startActivityForResult(...)로 시작되었다면 결과 코드는 항상 부모 액티비티에 반환된다. 이때 자식 액티비티에서 setResult(...)를 호출하지 않은 상태에서 사용자가 장치의 백 버튼을 누르면 부모 액티비티는 결과 코드로 Activity.RESULT_CANCELED를 받게 된다.

인텐트 돌려주기

자식 액티비티가 부모 액티비티에 인텐트의 엑스트라를 돌려줄 수도 있다. 여기서는 엑스트라를 부모 액티비티인 MainActivity에 전달하는 것이 주 관심사다. 따라서 Intent를 생성하고 엑스트라를 저장한 후 Activity.setResult(Int, Intent)를 호출해 부모인 MainActivity에서 엑스트라 데이터를 받게 한다.

앞에서 CheatActivity가 받는 엑스트라 키의 상수를 CheatActivity에 정의했으니, 자식인 CheatActivity가 부모인 MainActivity에 전달할 새로운 엑스트라도 그것처럼 하면 된다.

결과 설정하기

  1. 엑스트라의 키로 사용할 상수를 CheatActivity에 추가
  2. 인텐트를 생성해 거기에 엑스트라를 저장
  3. 결과 코드를 설정하는 private 함수 추가
  4. ‘정답 보기’ 버튼의 리스너에 이 함수의 호출 코드를 추가
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const val EXTRA_ANSWER_SHOWN = "com.june0122.geoquiz.answer_shown"
private const val EXTRA_ANSWER_IS_TRUE = "com.june0122.geoquiz.answer_is_true"

class CheatActivity : AppCompatActivity() {
...
override fun onCreate(savedInstanceState: Bundle?) {
...
showAnswerButton.setOnClickListener {
...
answerTextView.setText(answerText)
setAnswerShownResult(true)
}
}

private fun setAnswerShownResult(isAnswerShown: Boolean) {
val data = Intent().apply {
putExtra(EXTRA_ANSWER_SHOWN, isAnswerShown)
}
setResult(Activity.RESULT_OK, data)
}
...
}

사용자가 정답 보기 버튼(showAnswerButton)을 누르면 CheatActivity는 setResult(Int, Intent)를 호출하기 위해 인텐트를 생성하고 결과 코드를 추가한다.

그리고 사용자가 장치의 백 버튼을 눌러서 다시 MainActivity로 돌아가면 ActivityManager가 부모 액티비티인 MainActivity의 다음 함수를 호출한다.

1
onActivityResult(requestCode: Int, resultCode: Int, data: Intent)

여기서 첫 번째 매개변수는 MainActivity에서 전달했던 요청 코드다. 나머지 매개변수인 결과 코드와 인텐트는 자식 액티비티인 CheatActivity의 setResult(Int, Intent)로 전달되었던 것들이다.

아래는 이러한 상호작용을 보기 쉽게 시퀀스 다이어그램으로 정리한 이미지이다.

GeoQuiz의 시퀀스 다이어그램

이제는 CheatActivity에서 설정한 결과 데이터를 처리하기 위해 MainActivity에서 onActivityResult(Int, Int, Intent)를 오버라이딩하여 구현하는 것만 남았다.

결과 데이터 처리하기

CheatActivity가 돌려주는 값을 저장하는 새로운 속성을 QuizViewModel.kt에 추가한다.

사용자의 커닝 상태(커닝 여부를 나타내는 데이터)는 UI 상태의 일부다. 따라서 사용자의 커닝 상태는 MainActivity 대신 QuizViewModel에서 보존해야 한다. 이렇게 하면 액티비티 소멸이 아닌 구성 변경 시에 계속 보존되기 때문이다.

QuizViewModel에 사용자 커닝 상태 보존하기

1
2
3
4
5
6
class QuizViewModel : ViewModel() {

var currentIndex = 0
var isCheater = false // 사용자 커닝 상태를 보존할 변수 추가
...
}

그다음에 CheatActivity가 돌려주는 결과 값을 가져오기 위해 MainActivity.kt에서 onActivityResult(...)를 오버라이드한다. 이때 요청 코드와 결과 코드가 기대한 값인지 확인한다.

  • 이렇게 하면 코드의 유지 보수가 쉬워진다. 특히, 인텐트로 시작시키는 자식 액티비티의 종류가 늘어났을 때 아주 유용하다.

onActivityResult(...) 오버라이드하기

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class MainActivity : AppCompatActivity() {
...
override fun onCreate(savedInstanceState: Bundle?) {
...
}

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)

if (resultCode != Activity.RESULT_OK) {
return
}

if (requestCode == REQUEST_CODE_CHEAT) {
quizViewModel.isCheater =
data?.getBooleanExtra(EXTRA_ANSWER_SHOWN, false) ?: false
}
}
...
}

마지막으로 사용자가 정답을 커닝했는지 여부를 확인하고 그에 따른 적절한 응답을 주기 위해 MainActivity의 checkAnswer(Boolean) 함수를 수정한다.

isCheater 값에 따라 토스트 메시지 변경하기

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class MainActivity : AppCompatActivity() {
...
private fun checkAnswer(userAnswer: Boolean) {
val correctAnswer = quizViewModel.currentQuestionAnswer

// val messageResId = if (userAnswer == correctAnswer) {
// R.string.correct_toast
// } else {
// R.string.incorrect_toast
// }

val messageResId = when {
quizViewModel.isCheater -> R.string.judgment_toast
userAnswer == correctAnswer -> R.string.correct_toast
else -> R.string.incorrect_toast
}

Toast.makeText(this, messageResId, Toast.LENGTH_SHORT)
.show()
}
}

챌린지

장치 회전과 프로세스 종료 시 CheatActivity의 UI 상태 보존하기

사용자가 정답을 커닝한 후에 CheatActivity 화면에서 장치를 회전해서 커닝 결과를 지울 수 있다. 그리고 MainActivity로 돌아오면 전혀 커닝하지 않은 것처럼 되는 문제를 해결해보자.

CheatActivity.kt

  1. by lazy 키워드를 사용하여 quizViewModel을 선언한다.
  2. Bundle 객체에 저장될 데이터의 키로 사용할 상수 KEY_CHEAT_STATUS를 추가한다.
  3. onSaveInstanceState(Bundle)을 오버라이드한다.
    • isCheater 값을 Bundle 객체에 저장시킨다.
    • 이때 키는 상수인 KEY_CHEAT_STATUS이며 키의 값은 quizViewModel.isCheater다.
  4. onCreate(Bundle?)에서 Bundle 객체 값을 확인한다.
    • Bundle 객체에 저장된 값을 확인해 값이 있으면 그 값을 quizViewModel.isCheater에 지정한다.
    • 키가 Bundle 객체에 없거나 Bundle 객체 참조가 null이면 cisCheater의 값을 false으로 설정한다.
  5. updateAnswer() 함수를 작성하여 onCreate(Bundle?) 함수와 정답 보기 버튼의 리스너 내부에서 호출한다.
    • updateAnswer()에선 커닝을 했을 경우 (quizViewModel.isCheater == true) QuizViewModel로부터 커닝에 대한 답을 가져오고, answerTextView에 보일 값을 설정한다.
    • 그리고 부모 액티비티에 데이터를 돌려주기 위해 자식 액티비티에서 호출하는 함수 setResult(...)가 포함된 함수 setAnswerShownResultupdateAnswer() 함수의 마지막에 호출한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
...
private const val KEY_CHEAT_STATUS = "cheat" // ② Bundle 객체에 저장될 데이터의 키로 사용될 상수 선언

class CheatActivity : AppCompatActivity() {
...
private val quizViewModel: QuizViewModel by lazy { // ① quizViewModel을 선언
ViewModelProvider(this).get(QuizViewModel::class.java)
}

private var answerIsTrue = false

override fun onCreate(savedInstanceState: Bundle?) {
...
// ④ Bundle 객체 값 확인
val isCheater = savedInstanceState?.getBoolean(KEY_CHEAT_STATUS, false) ?: false
quizViewModel.isCheater = isCheater
...
// ⑥ 정답 보기 버튼 클릭 시, quizViewModel의 isCheater 값을 true로 변경하고 updateAnswer() 호출
showAnswerButton.setOnClickListener {
quizViewModel.isCheater = true
updateAnswer()
}
// ⑦ 액티비티 생성 시 updateAnswer() 호출
updateAnswer()
}

// ③ `onSaveInstanceState(Bundle)`을 오버라이드
override fun onSaveInstanceState(savedInstanceState: Bundle) {
super.onSaveInstanceState(savedInstanceState)
savedInstanceState.putBoolean(KEY_CHEAT_STATUS, quizViewModel.isCheater)
}

// ⑤ updateAnswer() 함수를 작성
private fun updateAnswer() {
if (quizViewModel.isCheater) {
val questionTextResId = when {
quizViewModel.currentQuestionAnswer -> R.string.true_button
else -> R.string.false_button
}
answerTextView.setText(questionTextResId)
setAnswerShownResult(true)
}
}

private fun setAnswerShownResult(isAnswerShown: Boolean) {
val data = Intent().apply {
putExtra(EXTRA_ANSWER_SHOWN, isAnswerShown)
}
setResult(Activity.RESULT_OK, data)
}
...
}

문제마다 커닝 여부 관리하기

QuizViewModel.kt

  1. 커닝 여부를 저장하는 MutableList인 cheatStatusList를 questionBank의 크기만큼 false로 초기화한다.
  2. 커스텀 접근자를 통해 cheatStatusList의 커닝 여부 값을 가져오고 저장할 수 있는 연산 프로퍼티 currentQuestionCheatStatus를 추가한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class QuizViewModel : ViewModel() {

var currentIndex = 0

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 val cheatStatusList = MutableList(questionBank.size) { false }

...

var currentQuestionCheatStatus: Boolean
get() = cheatStatusList[currentIndex]
set(value) {
cheatStatusList[currentIndex] = value
}

...
}

MainActivity.kt

  1. requestCode가 REQUEST_CODE_CHEAT일 때, quizViewModel.isCheater 대신 quizViewModel.currentQuestionCheatStatus의 값을 변경한다.
  2. 마찬가지로 checkAnswer 함수에서 quizViewModel.isCheater 대신 quizViewModel.currentQuestionCheatStatus, 즉 커닝 여부가 True일 때 경고성 토스트를 보여주도록 변경한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class MainActivity : AppCompatActivity() {
...
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
...
if (requestCode == REQUEST_CODE_CHEAT) {
// quizViewModel.isCheater = ... 을 아래의 코드로 변경
quizViewModel.currentQuestionCheatStatus =
data?.getBooleanExtra(EXTRA_ANSWER_SHOWN, false) ?: false
}
}
...

private fun checkAnswer(userAnswer: Boolean) {
val correctAnswer = quizViewModel.currentQuestionAnswer

val messageResId = when {
// quizViewModel.isCheater -> R.string.judgment_toast
quizViewModel.currentQuestionCheatStatus -> R.string.judgment_toast
userAnswer == correctAnswer -> R.string.correct_toast
else -> R.string.incorrect_toast
}
...
}
}

참고

실무에 바로 적용하는 안드로이드 프로그래밍(제4판) : CHAPTER 6 코드

댓글