前回、簡単な四択クイズアプリを作りました。
数字の順番に押していくと、ゲームクリア。
途中で間違えると、ゲームオーバー、というものでした。
動作自体はこれで問題ないのですが
不正解のところは、この5行、全部同じです。
tvQuestion.text=”不正解!!Game Over”
btn0.isEnabled = false
btn1.isEnabled = false
btn2.isEnabled = false
btn3.isEnabled = false
btn0も、btn1も、
2も3も全く同じなんですよね。
なので、1か所にまとめる、ということをやっていきましょう。
動画
コード
▼activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:id="@+id/tvCount"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:layout_marginEnd="24dp"
android:layout_marginRight="24dp"
android:text="0問正解"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/tvQuestion"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:text="番号順にタッチせよ"
android:textSize="24sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tvCount" />
<Button
android:id="@+id/btn0"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="90dp"
android:text="Button"
app:layout_constraintTop_toBottomOf="@+id/tvQuestion"
tools:layout_editor_absoluteX="187dp" />
<Button
android:id="@+id/btn1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="32dp"
android:text="Button"
app:layout_constraintTop_toBottomOf="@+id/btn0"
tools:layout_editor_absoluteX="129dp" />
<Button
android:id="@+id/btn2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="32dp"
android:text="Button"
app:layout_constraintTop_toBottomOf="@+id/btn1"
tools:layout_editor_absoluteX="160dp" />
<Button
android:id="@+id/btn3"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="32dp"
android:text="Button"
app:layout_constraintTop_toBottomOf="@+id/btn2"
tools:layout_editor_absoluteX="140dp" />
</androidx.constraintlayout.widget.ConstraintLayout>
▼MainActivity.kt
package com.example.simplequiz
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Button
import android.widget.TextView
import androidx.appcompat.app.AlertDialog
class MainActivity : AppCompatActivity() {
//2)'配列と変数を用意(クラスの各メソッドで参照可)
private val quizData = arrayOf("A0","A1","A2","A3")
private var i =0
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//1)viewを取得(idで) + カウント用のiを用意
val tvCount:TextView =findViewById(R.id.tvCount)
val tvQuestion:TextView =findViewById(R.id.tvQuestion)
val btn0:Button = findViewById(R.id.btn0)
val btn1:Button = findViewById(R.id.btn1)
val btn2:Button = findViewById(R.id.btn2)
val btn3:Button = findViewById(R.id.btn3)
//var i =0
//2)配列を用意
//val quizData = arrayOf("A0","A1","A2","A3")
//4)0~3までのリスト用意⇒シャッフル
val list = listOf(0,1,2,3)
val num =list.shuffled()
//3)ボタンにquizDataの0~3を表示させてみる
//5)シャッフルされたnumの数字を入れる
btn0.text =quizData[num[0]]
btn1.text =quizData[num[1]]
btn2.text =quizData[num[2]]
btn3.text =quizData[num[3]]
//6)btn0を押した時の正誤判定
btn0.setOnClickListener {
if(btn0.text==quizData[i]){
//正解
correctAns()
//7)カウントを1増やして無効化
i++
btn0.isEnabled =false
tvCount.text =i.toString() + "問正解"
//9)i==4でGameClear
if(i==4){
tvQuestion.text="全問正解!!Game Clear!!"
}
}else{
//不正解+ボタンの無効化
incorrectAns()
}
}
//8)btn1~3も同じようにする
btn1.setOnClickListener {
if(btn1.text==quizData[i]){
//正解
correctAns()
//7)カウントを1増やして無効化
i++
btn1.isEnabled =false
tvCount.text =i.toString() + "問正解"
if(i==4){
tvQuestion.text="全問正解!!Game Clear!!"
}
}else{
//不正解+ボタンの無効化
incorrectAns()
}
}
btn2.setOnClickListener {
if(btn2.text==quizData[i]){
//正解
correctAns()
//7)カウントを1増やして無効化
i++
btn2.isEnabled =false
tvCount.text =i.toString() + "問正解"
if(i==4){
tvQuestion.text="全問正解!!Game Clear!!"
}
}else{
//不正解+ボタンの無効化
incorrectAns()
}
}
btn3.setOnClickListener {
if(btn3.text==quizData[i]){
//正解
correctAns()
//7)カウントを1増やして無効化
i++
btn3.isEnabled =false
tvCount.text =i.toString() + "問正解"
if(i==4){
tvQuestion.text="全問正解!!Game Clear!!"
}
}else{
//不正解+ボタンの無効化
incorrectAns()
}
}
}
//11)正解の関数(アラートダイアログを表示)
private fun correctAns(){
AlertDialog.Builder(this)
.setTitle("正解!")
.setMessage(quizData[i])
.setPositiveButton("OK",null)
.show()
}
//10)不正解処理の関数
private fun incorrectAns(){
val tvQuestion:TextView =findViewById(R.id.tvQuestion)
val btn0:Button = findViewById(R.id.btn0)
val btn1:Button = findViewById(R.id.btn1)
val btn2:Button = findViewById(R.id.btn2)
val btn3:Button = findViewById(R.id.btn3)
//不正解+ボタンの無効化
tvQuestion.text="不正解!!Game Over"
btn0.isEnabled = false
btn1.isEnabled = false
btn2.isEnabled = false
btn3.isEnabled = false
}
}
テキスト
前回作ったコードは、不正解のところが5行、全部同じでした。
同じコードは、関数を1つ作ってそこにまとめる、ということをやっていきましょう。
てことで、関数を1つ用意して、
そこにこれらのコードを一か所にまとめていくわけですが、
その前に知識として覚えていただきたいことが1つ2つあります。
1.宣言する場所
まず1つ目は、宣言する場所です。
今までfun onCreate{
}の中で宣言していました。
quizDataとかですね、
上の方にもいろいろありますが。
funっていうのはfunctionの略で、関数あるいはメソッドを意味します。
fun{}の中で宣言したものは
その関数・メソッドの{}の中でしか使えません。
で新しく関数を作って、横断的にこういう配列の要素を使用したい場合は、
fun onCreateの中ではなくて、
その上。
厳密にいうと、クラス名{
の下。
ここに書くと、横断的に使用できるようになります。というのが1つめ。
2.ただし、findViewByIdは除く
もう1つは、今の説明を踏まえた上で、ただし
findViewByIdは、こっちでは使えません。ここがちょっとややこしいんです。
使えないというより、
厳密にはfindViewByIdは
setContentViewより下に書かないと機能してくれません。
上に書いても、エミュレータを起動してしたときクラッシュしてしまいます。
以上2点。
関数を増やしていく上で、必要となる知識。
1つ目は、横断的に使いたい場合はクラス名の下に書く。
ただし、findViewByIdは除く。
こういうのをふんわり、頭の中に入れておいてください。
それでは始めていきましょう。
1.関数を作ろう
それでは始めていきましょう。
この不正解処理だけをまとめた関数を1つ作ります。
作る場所は、
fun onCreate {
}
の外、厳密には「下」ですね。
ここに
//10)不正解処理の関数
を用意します。
書き方は
fun 関数の名前(){
}
と書いていきます。
funっていうのはですね
functionの略で、文字通り関数を意味します。
それでは書いていきましょう。
不正解なので、名前をincorrectAnsとかにしてあげて、
まるカッコ、波カッコ。
fun incorrectAns(){
}
これで1つ関数ができました。
で、この中に、不正解の処理をそっくりそのままコピーして
貼り付ければOKです。
fun incorrectAns(){
//不正解+ボタンの無効化
tvQuestion.text="不正解!!Game Over"
btn0.isEnabled = false
btn1.isEnabled = false
btn2.isEnabled = false
btn3.isEnabled = false
}
で、あとは
incorrectAns()
を上の不正解のところにペタペタ貼っていけばOKなんですが。
実は今の状態だと1つだけ問題があります。
赤くなってるのでおわかりの通り、Viewが取得できていません。
ここで冒頭の話に戻ります。
funなんちゃら内で書いたものは、funなんちゃら内でしか使えません。
横断的に使いたい場合は、クラス名の下に書いてあげる。
ただし、findViewByIdは、例外で
setContentViewより後じゃないと実行できません。
てことで、いっそのことこのtvQuestionと、ボタン4つ
合計5つをコピーして
fun incorrectAns()内に貼り付けてください。
fun incorrectAns(){
val tvQuestion :TextView = findViewById(R.id.tvQuestion)
val btn0:Button=findViewById(R.id.btn0)
val btn1:Button=findViewById(R.id.btn1)
val btn2:Button=findViewById(R.id.btn2)
val btn3:Button=findViewById(R.id.btn3)
//不正解+ボタンの無効化
tvQuestion.text="不正解!!Game Over"
btn0.isEnabled = false
btn1.isEnabled = false
btn2.isEnabled = false
btn3.isEnabled = false
}
なので、慣れるまで少々めんどくさいんですが
これで、こっちの関数でもviewが取得できて
そうすると、ちゃんと正常な色になったので
ちゃんとアクセスできているのがわかります。
2.貼り付け
あとは、この
incorrectAns()
を不正解のコードのところに張り替えていきましょう。
ボタン4つ、4か所ですね。
}else{
//不正解+ボタンの無効化
incorrectAns()
}
ひとまず、エミュレータを起動して、と行きたいところなんですが
そのまえにもう1つ。
右上に、赤いエラーが出てまして、
あ、この黄色いのは無視していきますからね
赤いやつだけ処理していきます。
クリックすると●行目はprivateにすべきです、って言われているので
アクセス修飾子をprivateと書いてあげましょう。
これで赤いエラーが消えました。
//10)不正解処理の関数
private fun incorrectAns(){
val tvQuestion :TextView = findViewById(R.id.tvQuestion)
val btn0:Button=findViewById(R.id.btn0)
val btn1:Button=findViewById(R.id.btn1)
val btn2:Button=findViewById(R.id.btn2)
val btn3:Button=findViewById(R.id.btn3)
//不正解+ボタンの無効化
tvQuestion.text="不正解!!Game Over"
btn0.isEnabled = false
btn1.isEnabled = false
btn2.isEnabled = false
btn3.isEnabled = false
}
こういうのをアクセス修飾子っていうんですが
privateっていうのは、このクラスでしかアクセスできませんよって意味です。
アクセスをコントロールすることで、より安全なプログラムが書けるようになります。
今回もこの関数はこのクラスでしか使ってないので、
Android先生が察知して、privateと書きなさいと
注意喚起された感じです。
てことで色々ありましたけれどもこれで準備が整いましたので
エミュレータを起動して
今まで通り動くというのを確認してみましょう。
正解は問題ないと。
不正解のばあい、
こうですね。ちゃんと不正解処理が実行されているのがわかります。
3.正解処理にアラートダイアログを表示させてみる
それでは最後に、
復習もかねて、
正解した場合の関数を作って
それを、各ボタンの正解のところに貼り付けていきましょう。
まずは関数を用意します。
場所は、不正解の上にでもいっときましょうか。
//11)正解の関数(アラートダイアログを表示)
正解をした時にアラートダイアログを表示してみましょう。
fun correctAns(){
}
で、この関数もこのクラスでしか使わないので
先ほど同様androidスタジオ先生に注意される前に
privateと、アクセス修飾子を付けておけばいいかなと思います。
//11)正解の関数(アラートダイアログを表示)
private fun correctAns(){
}
それでは、アラートダイアログを書いていきましょう。
タイトルは正解、
メッセージは、正解の配列を表示させてあげたいので
quizDataの[i]番目、と書きたいところなんですが。
private fun correctAnswer(){
AlertDialog.Builder(this)
.setTitle("正解!")
.setMessage(quizData[i])
}
ここで、
quizDataと
iにアクセスできません、となっています。
これが冒頭申し上げた、funなんちゃら内で宣言したものは
funなんちゃら内でしか使えない、というルールです。
今はfunction onCreateの{}内に書かれていますので
onCreateメソッドでしか使えませんよとなっています。
横断的に使いたい場合は、クラス名の下に書いてあげましょう。
1つずつやっていきましょう。
まずquizDataから。
quizDataは配列なので、findViewByIdとは違って
クラス名の下に記述しても大丈夫です。
ここ(クラス名の直下)で宣言したものは、各メソッドから横断的に参照が可能ですので
fun onCreateでも使えるし
fun correctAnswerでも使えます。
val quizData = arrayOf(“A0″,”A1″,”A2″,”A3”)
ただしここで宣言する場合はアクセスをコントロールする
アクセス修飾子を付けなければいけません。
anndroidStudio先生もですね
privateじゃねえの?って聞いてきているので
実際このクラスでしか使ってませんので
private val quizData = arrayOf(“A0″,”A1″,”A2″,”A3”)
と書いてあげましょう。
そうすると
quizDataは
fun オンクリエイトメソッドでもアクセスできていますし
新しく作ったfun correctAns()でもアクセスできているのがわかります。
このようにクラス名の下に書いてあげると
横断的に使えるようになります。
ただし、繰り返しますが
findViewById()は注意が必要ですってことですね。
それから同じくiも、アクセスできないようなので
クラス名の下に宣言してあげましょう。ここもちゃんとアクセス修飾子をつけてあげます。
private var i = 0
これでfun correctAnsでも色が正常にもどったので
アクセスできるようになったのがわかります。
では続きを書いていきましょう。
//11)正解の関数(アラートダイアログを表示)
private fun correctAns(){
AlertDialog.Builder(this)
.setTitle("正解")
.setMessage(quizData[i])
.setPositiveButton("OK",null)
.show()
}
これで関数ができました。
あとは、先ほど同様これを掲載したいところに、
correctAns()を貼り付けていけば完成です。
ちょうど
//tvQuestion.text=”正解!!”
が残っているので、これに上書きしていきましょう。