Android Studio + kotlin + SQLiteで音符フラッシュカードアプリを作る　⑦問題出題（ロジック実装）


#１．今回のテーマ

前回までで、トップ画面から引き継いだデータをゲーム画面で表示するところまで実装できました。
今回はゲーム画面で問題を出題するロジックを実装していきます。


#２．問題番号、正解を格納する変数の宣言

最初に問題番号と、正解を格納する変数を宣言します。
ゲーム画面共通で利用するので、class宣言の直下、onCreateメソッドの外側に書いていきます。
kotlinではこのような変数のことを、なんて言うのでしょう。
クラス変数？プロパティ？コンストラクタ？すみません、わかりません。


```kotlin:GemeActivity.kt
class GameActivity : AppCompatActivity() {

    //問題番号カウンタ
    var questionCount = 0
    //正解を格納する変数
    var correctAnswer = ""

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_game)
    }
```

それぞれ、
問題番号：questionCount
正解を格納する変数：correctAnswer
という名前で宣言しました。

どちらもこの後使います。


#３．問題出題

続いて本題の問題出題ロジックです。


```kotlin:GameActivity.kt
class GameActivity : AppCompatActivity() {

    //問題番号カウンタ
    var questionCount = 0
    //正解を格納する変数
    var correctAnswer = ""


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


        //インテントからレベルを取得
        val level = intent.getStringExtra("level")

        //レベル表示
        var tvLevel = findViewById<TextView>(R.id.tvLevel)
        tvLevel.setText(level)

        //第1問の出題
        if (level != null) {
            setQuestion(level)
        }

```

問題の出題は、setQuestion()というメソッドで実装します。
onCreate()内では、第1問目を出題し、第2問目以降は正誤判定メソッド内に実装していくことにしました。
両方に同じロジックを書くのは非効率なので、別メソッドに切り出すことにした次第です。

getStringExtra()で前画面から引き継いだレベルを引数として渡して、出題する問題を選択していきます。


setQuestion()メソッドで実装する内容は以下の通りです。

①問題番号を1カウントアップ
②問題番号を表示
③トップ画面で選択されたレベルに応じて、問題idをランダムで取得
④問題idに応じて問題の画像と正解を設定
⑤問題の画像を表示


①②はあまり苦労せずに実装できました。
コードは以下の通りです。

```kotlin:GameActivity.kt
        //問題番号を1プラス
        questionCount = questionCount + 1


        //問題番号欄の表示を更新
        var tv_questionCount = findViewById<TextView>(R.id.tv_questionCount)
        tv_questionCount.setText("第${questionCount}問")
```

まず最初にonCreateメソッドの外で定義した**questionCount**を1カウントアップします。
そして、**findViewById**で問題番号を表示するビューを取得します。
xmlの定義を確認します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/619303/498e60fc-5d59-f5f3-914d-9f1f05587331.png)
idは**tv_questionCount**です、

そしてカウントアップした**questionCount**で、text属性を更新していきます。

tv_questionCount.setText("第**${questionCount}**問")
setTextには、固定の文字列だけではなく変数に格納した文字列を設定することもできます。
その場合は上の例のように、設定したい変数を**${}**で囲んで設定します。

次に
④問題idに応じて問題の画像と正解を設定
⑤問題の画像を表示
の部分です。

今回準備した問題は全部で36問。
右手、左手それぞれ18問ずつ。
どうやってレベルに応じて出題する問題を変えようか、としばらく悩んで以下の仕様にしました。
・36問それぞれにidを振って、以下の通り問題を出題することにしました。
・ト音初級：1～8
・ト音中級：1～15
・ト音上級：1～18
・へ音初級：19～26
・へ音中級：19～33
・へ音上級：19～36
ここまではrandom関数で範囲を指定すればできそうだなと思ったのですが、問題は両手でした。
両手初級では、ト音とへ音それぞれの初級の問題を出したいので、
1～8、19～26の中から選ぶ必要があります。
両手中級と両手上級も同様に、番号が飛んでいる中から選ばなければなりません。

では実際のコードを見ていきます。
まずは問題idを決める部分です。


```kotlin:GameActivity.kt
        var questionId = 0
        val level = intent.getStringExtra("level")
        when(level) {
            "ト音初級" -> {
                questionId = (1..8).random()
            }
            "ト音中級" -> {
                questionId = (1..15).random()
            }
            "ト音上級" -> {
                questionId = (1..18).random()
            }
            "へ音初級" -> {
                questionId = (19..26).random()
            }
            "へ音中級" -> {
                questionId = (19..33).random()
            }
            "へ音上級" -> {
                questionId = (19..36).random()
            }
            "両手初級" -> {
                var levelFrag = (0..1).random()
                if(levelFrag == 0){
                    questionId = (1..8).random()
                }else {
                    questionId = (19..26).random()
                }
            }
            "両手中級" -> {
                var levelFrag = (0..1).random()
                if(levelFrag == 0){
                    questionId = (1..15).random()
                }else {
                    questionId = (19..33).random()
                }
            }
            "両手上級" -> {
                questionId = (1..36).random()
            }
        }

```

最初の
```var questionId = 0```
では、問題idを初期値で宣言しています。

続いて
```        val level = intent.getStringExtra("level")```
で、再度レベルをintentから取得。

次に問題idをセットする部分です。
When式を使って書いていきます。
レベルがト音初級の場合の処理を例にとって説明します。

```kotlin
        when(level) {
            "ト音初級" -> {
                questionId = (1..8).random()
            }
```
レベルが"ト音初級"だったら、questionIdに1～8の中からランダムで1つを設定しています。
kotlinのrandom関数は範囲を指定できるので大変実装が簡単です。
同じようにト音中級、ト音上級についても出題する問題idを設定していきます。

randomで指定する範囲は、1から始まっていなくてもよいので、ヘ音初級～上級が選ばれた場合も右手と全く同じ要領で実装が可能です。

```kotlin
            "へ音初級" -> {
                questionId = (19..26).random()
            }
```

簡単ですね。

続いて両手。
悩みましたが、ひと手間入れれば簡単でした。

```kotlin
            "両手初級" -> {
                var levelFrag = (0..1).random()
                if(levelFrag == 0){
                    questionId = (1..8).random()
                }else {
                    questionId = (19..26).random()
                }
            }
```
**levelFrag**という変数を用いて、出題する問題をト音にするか、ヘ音にするかを判定しています。
random関数で0か1を生成して、0の場合はト音、1の場合はヘ音の問題をquestionIdに設定します。

これでレベルに応じた問題のidをquestionIdに設定することができました。

次にquestionIdによって、表示する問題の画像を変えて、さらに正誤判定のために正解文字列をセットしていきます。

最初に、問題の画像を表示するViewを取得します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/619303/b4500939-f970-26de-7f2b-a57f0cded034.png)
idは**iv_note**なので、以下で取得します。。

```kotlin
        //画像をセットするViewを取得
        var ivNote = findViewById<ImageView>(R.id.iv_note)
```

続いて、このViewのsrc画像を更新、正解を設定します。

```kotlin
        //番号に応じて画像と答えを設定
        when(questionId) {
            1 -> {
                //画像に右手ドをセット
                ivNote.setImageResource(R.drawable.right_c);
                correctAnswer = "ド"
            }
            2 -> {
                //画像に右手レをセット
                ivNote.setImageResource(R.drawable.right_d);
                correctAnswer = "レ"
            }
            3 -> {
                //画像に右手ミをセット
                ivNote.setImageResource(R.drawable.right_e);
                correctAnswer = "ミ"
            }
///以下、問題36まで続きます。
```

questionIdが1だったら、問題の画像を「right_c」で更新。
画像を設定するためのメソッドは
***setImageResouce()***です。

さらに、正解を格納するためにクラスの冒頭で宣言した***correctAnswer***に"ド"を設定します。

これを36問すべてに対して書いていきます。力技ですね。

もうちょっとすっきりしたコードにできたかもしれませんが、今の私のレベルではこれが限界でした。

今回は以上です。
最後までお読みいただきありがとうございました。
次回は回答ボタンを押したときの処理について解説していきます。


[①概要](https://qiita.com/tomohiro-yamashita-5664/items/e87f90fef9cf34faf9bf)
[②画面デザイン～トップ画面（Constraint Layout）～](https://qiita.com/tomohiro-yamashita-5664/items/72a529e12f292a8e1538)
[③画面デザイン～ゲーム画面（Linear Layout）～](https://qiita.com/tomohiro-yamashita-5664/items/9674fa7c1d0cc2de1c40)
[④画面デザイン～結果画面（Linear Layoutその2）～](https://qiita.com/tomohiro-yamashita-5664/items/08f99be14d2ac2254634)
[⑤トップ画面からの遷移（インテント(putExtra)）](https://qiita.com/tomohiro-yamashita-5664/items/ae86d6273cfc8c13b482)
[⑥トップ画面から引き継いだデータ表示（インテント(getExtra)）](https://qiita.com/tomohiro-yamashita-5664/items/bf50791fa5355818d7fb)
[⑦問題出題（ロジック実装）（本記事）](https://qiita.com/tomohiro-yamashita-5664/items/a67eeb3fe9cc63eb1c65)
[⑧回答ボタン押下（効果音再生（MediaPlayer、正誤判定、次の問題出題）](https://qiita.com/tomohiro-yamashita-5664/items/8b032855a9377ba9897e)
[⑨タイムカウンターの実装（handler）](https://qiita.com/tomohiro-yamashita-5664/items/8f5633e68f0519d6841f)
[⑩ゲーム画面から引き継いだゲーム結果表示（インテント）](https://qiita.com/tomohiro-yamashita-5664/items/0e208a3c1a75c5b13376)
[⑪当日日付データ取得](https://qiita.com/tomohiro-yamashita-5664/items/59513080e5d5f419d6af)
[⑫DB保存（SQLite、Insert）](https://qiita.com/tomohiro-yamashita-5664/items/5a0d967f2e42aa90ee24)
⑬もう一度、トップ画面へ戻るボタン（インテント）
⑭ランキング表示（SQLite、Select）
⑮実機でのテスト
⑯Google Playで公開