Android Studio + kotlin + SQLiteで音符フラッシュカードアプリを作る　③画面デザイン～ゲーム画面（Linear Layout）～

#１．今回のテーマ
画面デザイン編2回目、ゲーム画面について書いていきます。

画面はこんな感じ。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/619303/a46f23ac-2ba0-4ec5-e830-c53549cec15e.png)

上部にレベルと問題番号、その下に問題の画像、さらにその下に回答ボタン、最下部に経過時間、という構成です。

android studioではこんな感じ。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/619303/e0b5f7bd-36c1-9724-5883-d1660b1eb8ae.png)

前回のトップ画面は、GUIでボタンを配置していくことで画面を定義するConstraint Layoutで作ったので、この画面はもう一つの定義方法であるLinear Layoutで書いていきます。


Linear Layoutについて簡単におさらいです。
LinearLayoutとは、ビューを並べて表示させるレイアウトです。ただし、この並べる方向は縦方向かのどちらかしか指定できません。
縦横を組み合わせた画面レイアウトにしたい場合は、Linear Layoutを入れ子にすることで実装します。
並べる方向は、android:orientationで指定します。
縦並びの場合：
android:orientation="vertical"
横並びの場合：
android:orientation="horizontal"
です。

orientation以外の属性については出てきた際に都度解説していきます。

#２．画面全体の説明
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/619303/cbf7210e-631a-c456-0c01-eb9cfc795fd0.png)
最初に画面全体の構成を説明をします。
上の図のように、大外の赤枠の中に、黄色枠のアイテム群が縦に並んでいます。
黄色枠の中で見ると、複数のアイテムが横に並んでいますね。
これが先ほど説明した、縦横を組み合わせたレイアウトです。
よほど単純な画面構成でない限り、縦だけ、横だけという構成にはならないですよね。
ということで、Linear Layoutを入れ子にして画面定義を行う必要が出てきます。

タグを可能な限り省いて簡易的に説明します。

```xml:sample.xml
//大外の赤枠部分
<LinearLayout
    android:orientation="vertical">
　⇒縦にアイテムを配置していく、ということ
//黄色枠一つ目「レベル」「問題番号」の定義
    <LinearLayout
        android:orientation="horizontal">
　⇒黄色枠の中ではアイテムを横並びにします、ということ。
        <TextView
            android:id="@+id/tvLevel"
            android:text="右手初級"/>
　⇒レベル表示

        <TextView
            android:id="@+id/tv_questionCount"
            android:text="第1問"/>
　⇒問題番号表示
    </LinearLayout>
⇒一つ目の黄色枠の定義はここまで。再び縦並び開始。

//二つ目の黄色枠。問題の絵を表示。位置はレベルと問題番号の下。
    <ImageView
        android:id="@+id/iv_note"
        android:src="@drawable/right_c"/>

//三つ目の黄色枠。回答ボタンを横並びで表示していきます。
    <LinearLayout
       android:orientation="horizontal">
　　⇒horizontalなので、このタグ内のアイテムは横並びです、という意味。
        <ImageButton
            android:id="@+id/bt_c"
            android:src="@drawable/button_c"/>
　　⇒「ド」のボタン
        <ImageButton
            android:id="@+id/bt_d"
            android:src="@drawable/button_d"/>
　　⇒「レ」のボタン

        <ImageButton
            android:id="@+id/bt_e"
            android:src="@drawable/button_e"/>
　　⇒「ミ」のボタン

        <ImageButton
            android:id="@+id/bt_f"
            android:src="@drawable/button_f">
　　⇒「ファ」のボタン

    </LinearLayout>
　⇒三つ目の黄色枠はここまで。横並び終了。
　　次のアイテムは縦（下）に並ぶ。

//四つ目の黄色枠。ソ～シの回答ボタンを横並びで表示していきます。
    <LinearLayout
        android:orientation="horizontal">
　　⇒horizontalなので横並び開始

        <ImageButton
            android:id="@+id/bt_g"
            android:src="@drawable/button_g"/>
　　⇒回答ボタン「ソ」


        <ImageButton
            android:id="@+id/bt_a"
            android:src="@drawable/button_a"/>
　　⇒回答ボタン「ラ」


        <ImageButton
            android:id="@+id/bt_b"
            android:src="@drawable/button_b"/>
　　⇒回答ボタン「シ」

    </LinearLayout>
　⇒四つ目の黄色枠はここまで。横並び終了。
　　次のアイテムは縦（下）に並ぶ。

//最後の黄色枠。経過タイムを表示。位置は回答ボタンの下。
    <TextView
        android:id="@+id/tv_time"
        android:text="00:00.0"
        android:textSize="48dp"/>

</LinearLayout>
⇒赤枠終了。
```

なんとなくイメージがつかめましたでしょうか。
大外のLinear Layoutタグで「全体としては縦並びです」と定義した上でその中の部品は横並びにしたいです、と入れ子にしたLinear Layoutタグで定義するのです。
もうちょっとうまく説明できそうな気がしますが。。。説明下手ですみません。
いい表現方法が思いついたら記事更新します。

先ほどのsample.xmlの中では、前回のConstraint Layoutの時に説明したタグしか記載しませんでした。
簡単におさらいです。

android:id
　⇒処理を実装する際に備えて、アイテムの名前を定義
android:text
　⇒TextViewに表示する文字列。表示する文字列は原則strings.xmlで定義する。
　　今回は単純な初期値ということでべた書きしてしまいました。
　　本当はだめなんだと思います。
android:src
　⇒ImageViewやImageButtonで表示する画像のid。
　　画像をdrawableフォルダに格納するとファイル名がidとして使えます。
　　詳しくは前回の記事をご参照ください。
android:textSize
　⇒文字の大きさです。dp指定、sp指定の二つの方法があると前回説明しました。



それでは次からは、タグ省略なしで各アイテムの定義を個別に見ていきましょう。


#３．大外の赤枠定義
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/619303/ebf4dd0e-6189-881e-eeff-eaa8d1a87c83.png)
各アイテムといいつつまずは大枠の定義からです。
コードは以下の通り。

```xml:activity_game.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">
```

最初の
<?xml version="1.0" encoding="utf-8"?>
はお決まりの文句だと思っていただいてよいかと思います。
version：1.0で、文字コードはuft-8で書いていきます、という意味です。
ではLinear Layoutタグの中身を一つ一つ説明します。

```xml
xmlns:android="http://schemas.android.com/apk/res/android"
```

これもお約束事です。
最も外側のタグのことをルート要素といいますが、ルート要素にはお約束として「xmlns:android」属性に対して
「http://schemas.android.com/apk/res/android」
を設定する必要があります。これはURLではないので、リンク先を見ても何もありません。
ごくごく簡単に言うと、今後android:xxxというタグを記載すれば、「これはandroidのレイアウト用に定義されたタグです」ということ意味する、と宣言しています。
詳しい説明は別の記事に譲ります。
https://www.javadrive.jp/android/activity/index6.html#section4

```xml
android:layout_width="match_parent"
android:layout_height="match_parent"
```
width：幅
height：高さ
です。
match_parentとは、「親の属性に一致する」という意味です。
今は大外の赤枠を定義しているので、「親＝画面全体」です。
つまり、縦・横共に画面いっぱい＝画面全体を指しています。

```
android:orientation="vertical"
```
これはいいですね。縦並びでアイテムを配置していきます、という意味です。

#４．レベル表示、問題数表示
まずはレベル表示と問題数表示です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/619303/804e33c4-67e0-fb32-eb95-4cc1cd7963c9.png)



該当箇所のソースコードは以下の通り。
赤枠のLinear Layoutタグ内に入れ子で書いていきます。
 
```xml:activity_game.xml
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="24dp"
        android:orientation="horizontal">

        <TextView
            android:id="@+id/tvLevel"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="48dp"
            android:text="右手初級"
            android:textSize="36dp"/>

        <TextView
            android:id="@+id/tv_questionCount"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="48dp"
            android:text="第1問"
            android:textSize="36dp"/>

    </LinearLayout>

```

最初の部分から説明ます。まずは入れ子とするLinear Layout（＝黄色枠）の定義です。

```xml
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="24dp"
        android:orientation="horizontal">
```

android:layout_width：match_parent
幅は画面横幅全体。

android:layout_height：wrap_content
新たな定義値ですね。
wrap_contentは、「コンテンツの高さに合わせる」という意味です。
つまりTextViewであればtextsizeで指定したサイズに、ImageViewであれば画像の大きさに準ずる、という意味になります。

android:layout_marginTop：24dp
こちらは新たなタグです。MarginTopとは、親アイテムの上部にあける余白を意味します。
ここでいう親は画面全体の上端ですから、上端から24dp空けてこのアイテムを配置する、という意味になります。

android:orientation：horizontal
サンプルのところで解説した通り、このタグ内のアイテムは横並びで配置していくことを宣言しています。

デザインビューで確認すると、「宣言された属性」のところに、今記述した内容が設定されていることがわかります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/619303/83d342c3-9470-ee5d-8606-4b52076e1d84.png)

続いてレベル表示のTextViewです。

```xml:activity_game.xml
        <TextView
            android:id="@+id/tvLevel"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="48dp"
            android:text="右手初級"
            android:textSize="36dp"/>
```

android:id="@+id/tvLevel"
機能実装編で解説しますが、このレベル表示はトップ画面で選択したレベルを表示します。
つまり固定値ではなく、プログラムによって表示を変えるアイテムになります。
「プログラムで表示を変える」＝「何らかの処理を実装する」ということなので、idを設定しておきます。

android:layout_width="wrap_content"
⇒幅はコンテンツの幅に合わせます。

android:layout_height="wrap_content"
⇒高さもコンテンツの幅に合わせます。

android:layout_marginLeft="48dp"
⇒親アイテムの左端からの余白を設定します。
　Constraint Layoutと違い「画面中央」のような相対的な指定はできないので、具体的な値で指定します。ここでは他のアイテムの大きさとの兼ね合いを考えて48dpを設定しました。すべてアイテムを配置してから調整していくのが良いと思います。

android:text="右手初級"
⇒テキストに表示する初期値です。何もなくてもよいのですが、どのくらいの幅になるのかを確認するために、初期値を入れました。strings.xmlを使わずべた書きです。
実際にはプログラムにより上書きされる値になります。

android:textSize="36dp"
⇒テキストのサイズです。36dpを指定しました。

続いて、問題数表示です。
こちらもTextViewなので設定すべき属性は先ほどのレベル表示と大差ありません。

```xml:activity_game.xml
        <TextView
            android:id="@+id/tv_questionCount"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="48dp"
            android:text="第1問"
            android:textSize="36dp"/>
```
このアイテムも、第何問目かをクイズの番号に応じて表示していきますので、idを設定しておきます。今回は、
android:id="@+id/tv_questionCount"
を設定しました。
それ以外の属性は改めて説明不要かと思います。


ここでいったん横並びのLinear Layoutタグは終了。

```xml:activity_game.xml
    </LinearLayout>
```

#５．問題表示
続いて問題の画像を表示する部品の定義をしていきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/619303/c63eeb07-b435-7a04-4569-1856d4ddd75b.png)


ソースコードは以下の通り。

```xml:activity_game.xml
    <ImageView
        android:id="@+id/iv_note"
        android:layout_width="300dp"
        android:layout_height="288dp"
        android:layout_marginTop="8dp"
        android:layout_marginLeft="48dp"
        android:src="@drawable/right_c"/>
```
まずは、画像ファイルをandorid studioに認識させます。
手順は前回の記事で解説しましたが、簡単におさらいです。

①pngファイルを用意。
②android studioのプロジェクトエクスプローラー表示して、pngファイルをapp/res/drawableフォルダにコピー。（下図はright_c.pngを移動しています）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/619303/47b0a9bc-a2d9-982c-d408-838e21ef52a0.png)
③リファクタリングボタンを押下
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/619303/d66a753f-be13-3def-c583-8675c5e90fc6.png)
これでこのプロジェクト内で画像を使えるようになります。

ソースコードに戻ります。

```xml:activity_game.xml
        android:src="@drawable/right_c"/>
```
のように、android:srcタグでファイルを指定します。
画像なので、「@drawable/ファイル名」で記載していきます。

また、画像は「ImageView」タグで記載します。
それ以外の属性は以下の通り。

android:id="@+id/iv_note"
⇒問題に応じて表示する画像を変えるので、こちらもid設定が必要です。
android:layout_width="300dp"
android:layout_height="288dp"
⇒width、heightはmatch_parentやwrap_content以外にも、dpで絶対値指定することができます。用意した画像が大きすぎたり小さすぎてうまく画面にはまらない場合には、xml内で定義することもできるのです。

android:layout_marginTop="8dp"
android:layout_marginLeft="48dp"
こちらはもう特に開設不要ですね。


#６．回答ボタン
続いて回答ボタンです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/619303/0d04c2a1-dbb6-684f-c73e-6eeb1aa9c0b0.png)


レベル、問題番号表示と同様、横並びでボタンを配置していきますので、Linear Layoutを入れ子にしてorientation=horizontalを指定します。

ソースコードは以下の通り。

```xml:activity_game.xml
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp"
        android:orientation="horizontal">

        <ImageButton
            android:id="@+id/bt_c"
            android:src="@drawable/button_c"
            android:tag="ド"
            android:layout_marginLeft="22dp"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>

        <ImageButton
            android:id="@+id/bt_d"
            android:src="@drawable/button_d"
            android:tag="レ"
            android:layout_marginLeft="8dp"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>

        <ImageButton
            android:id="@+id/bt_e"
            android:src="@drawable/button_e"
            android:tag="ミ"
            android:layout_marginLeft="8dp"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>

        <ImageButton
            android:id="@+id/bt_f"
            android:src="@drawable/button_f"
            android:tag="ファ"
            android:layout_marginLeft="8dp"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>

    </LinearLayout>
```

ほとんど解説済みなので、改めて書くことが少なくなってきました。

```xml:activity_game.xml
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp"
        android:orientation="horizontal">
```
ここから横並び配置が始まることを宣言しています。

```xml:activity_game.xml
        <ImageButton
            android:id="@+id/bt_c"
            android:src="@drawable/button_c"
            android:tag="ド"
            android:layout_marginLeft="22dp"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>
```

回答ボタンはImageButtonで実装していきます。
記述方法はImageViewとほぼ同じです。
一つだけ新しい属性が登場しています。
android:tag="ド"
これは、このアイテム（「ド」という回答ボタン）に「ド」というタグ付けをする、という意味です。idのようなものですが、idはアプリ内で一意である必要があるのに対して、タグは重複可能です。
このタグを用いて、問題と答えの正誤判定処理を実装していきます。
実装内容について、詳しくは機能編で解説しますが、簡単に書くと以下の通りです。
①問題表示の際、画像と正解（ド～シ）をセットで設定する。
②回答ボタンが押されたら、ボタンに設定されたタグのテキストと、①で設定した正解と比較
③一致していたら正解、不一致なら不正解
TextViewならテキストの値で比較できますが、ImageButtonなのでテキストを持っていないため、タグを設定して正誤判定に使っています。この正誤判定処理は、どうやって実装しようか悩んだ部分です。

同じようにレ～ファも配置して、最後に横並びのLinear Layoutタグを閉じます。

```xml:activity_game.xml
    </LinearLayout>
```

ソ～シについても、再度横並びLinear Layoutを使って配置していきます。
個別の解説は割愛します。


#７．経過時間表示
最後に経過時間を表示するアイテムです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/619303/85351606-1b19-98c7-a76d-a3364d66dfe7.png)


```xml:activity_game.xml
    <TextView
        android:id="@+id/tv_time"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="32dp"
        android:layout_marginLeft="120dp"
        android:text="00:00.0"
        android:textSize="48dp"/>
```

この後タイマー処理を設定していくのでidを定義します。

初期表示は以下の通りです。
android:text="00:00.0"

こちらも特に解説不要かと思います。


以上、ゲーム画面の解説でした。

次回は結果表示画面の実装について解説します。
ゲーム画面と同様Linear Layoutで実装していきますので、短めの記事になるかもしれません。
その次からはいよいよ機能編です。


[①概要](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で公開