この記事はレコチョク Advent Calendar 2022の4日目の記事となります
はじめに
はじめまして、レコチョク新卒一年目の深沢と申します。10月よりAndroid開発グループに配属され、現在Android開発について日々勉強しています。
最近気になっている音楽トピックは「THE LAST ROCKSTARS結成」です。高校時代好きでよく聴いていた 方々で結成されたバンドなので、これからの活動を本当に楽しみにしています。
本記事では初学者の私が10月から1ヶ月で学んだAndroid開発の内容とそれらについての簡単な所感をまとめました。Androidの開発を全く行ったことがない人間が1ヶ月でどれだけ学ぶことができたのか、何に躓いてどう感じているのか、などリアルな声を届けたいと思います。
これからAndroid開発を行おうとしている方、今度Android開発について教える機会があるが、初学者はどう感じているのかを知りたい方などにこの記事が参考になればと思います。
筆者スペック
はじめに、Android開発に携わるまでの私のプログラミング関連のスペックについて簡単にまとめます
- 四年制大学学部卒。理系学部におり、1,2年生の頃基本的なプログラミングの講義あり(Ruby,C)
- 5月から9月まで社内エンジニア研修を経験。主にPHPを学ぶ
- Advent Calendar 2021で先輩がまとめてくださっています。研修内容は大体同じです。
- 上記の研修にて3日間のAndroid研修あり。簡単なレイアウトを作成したり、画面遷移処理を行う
学習環境
私が勉強している環境は以下のとおりです
- 実装にはプレイグラウンドとAndroid Studioを使用
- プレイグラウンド:Kotlinのコードを動かせるブラウザページ
- Android Studio:Googleが提供しているAndroidアプリ開発用の統合開発環境(IDE)
- わからないことがあった場合は、グループの先輩に聞ける状態
個人的難易度
学んだ内容をまとめるにあたって、今回個人的難易度というものを設定しました。以下の基準に沿って学んだ内容それぞれに5段階で難易度をつけています。読者の皆様の参考になりますと幸いです。
- 難易度1: 元々知っている内容であり、調べなくても自力で問題なく使える状態
- 難易度2: Android特有のものというような理由で初めは手こずったが、現在は難易度1と同等に扱える状態
- 難易度3: 調べれば、自力で実装できる状態
- 難易度4: 完全には理解できていないが、なんとなくの処理の流れはつかめている状態
- 難易度5: 内容について半分程度しか理解できておらず、勉強途中の状態
学んだ内容と所感
今回私は主にコードラボ というGoogleが作成したAndroid開発に関する教材を用いて勉強をしました。学んだ単元ごとにコードラボのURLのを貼り付けていますので、細かい内容などはそちらをご確認ください。
※今回、学んだ内容と所感について時系列順で並べたため、使用しているサイトや作成しているアプリが行ったり来たりしています。ご了承ください。
変数・関数について/個人的難易度: 1
変数や関数の設定方法などをコードラボで学びました。そして
print()関数を使い、アスキーアートのようなものを作成しました。PHP研修で学んだ内容と似ていたので、特に躓くことなく進めることができました。
// println()で作成した絵
,,,,,,,,,,,,,,,,,,,,,,,,
||||||||||||||||||||||||
==========================
@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@
学んだこと
- テキストを出力するときはprintln()を使用し、
"で囲む。 print()内でテキストと変数を同時に使用するときは${}で変数を囲む。- 例:
println(テキスト:${変数名})
- 例:
valを用いて変数を作成する。この値は一度設定すると変更ができない。- 一連の手順を繰り返すにはrepeat()を使用。
- 例:
repeat (23) { print("%") }
- 例:
コードラボ
Android Studioを用いて画像や文字を配置/個人的難易度: 2
TextViewやImageViewを用いて簡単な画面を作成しました。Android Studioに慣れていなかったため、配置方法などを覚える時間は少しかかりましたが、実装が視覚的にわかりやすく、難なく対応することができました。
学んだこと
- TextViewはアプリ内でテキストを表示するためのUI要素。フォント、テキストサイズ、色などの属性を設定できる。
- ConstraintLayoutは他のUI要素のコンテナのこと。
- Viewは
ConstraintLayout内で水平方向と垂直方向に制約する必要がある。 - ImageViewはアプリ内で画像を表示するためのUI要素。アプリを利用しやすくする内容説明を設定する。
- ユーザーに表示するテキストは文字列リソースに抽出して、翻訳しやすくする。
コードラボ
オブジェクト指向とクラスについて/個人的難易度: 4
ボタンを押すとランダムなサイコロの出目が出力されるというアプリ(以下サイコロアプリ)の作成を通してオブジェクト指向、クラスについて勉強しました(この章ではプレイグラウンドのみ使用)。
Diceクラスを見様見真似で作りはしたのですが、クラスにどういう情報が入るべきなのか、そもそもクラスとは何か?というところから早速躓いていました。当時よりは大分理解が進んだものの、クラスにどんな情報が入っているのかをコードから読み取るのは未だに苦戦しています。
また、個人的にはコンストラクタの意味を理解するのにかなり時間がかかりました。これからも勉強して理解を深めていきたいと思います。
fun main() {
val myFirstDice = Dice(6)
println("Your ${myFirstDice.numSides} sided dice rolled ${myFirstDice.roll()}!")
val mySecondDice = Dice(20)
println("Your ${mySecondDice.numSides} sided dice rolled ${mySecondDice.roll()}!")
}
// クラス定義
class Dice (val numSides: Int) {
fun roll(): Int {
return (1..numSides).random()
}
}
// 実行結果
Your 6 sided dice rolled 2!
Your 20 sided dice rolled 3!
学んだこと
- IntRangeでrandom()を呼び出して、乱数を生成する。
- 例:
(1..6).random()
- 例:
- クラスはオブジェクトの設計図のようなもの。オブジェクトの特性と動作は変数や関数として実装できる
- インスタンスとはオブジェクト(多くの場合、物体)を表す。オブジェクトのアクションを呼び出して、属性を変更できる。作成するときは、クラスに値を指定できる。
- 例:
class Dice(val numSides: Int)のインスタンス作成例はDice(6)
- 例:
コードラボ
ボタンを押したときの動作(Viewの情報を呼び出して処理を追加)/ 個人的難易度: 2
Android Studioを用いてサイコロアプリを作成し、画像のROLLボタンを押すとランダムな出目が数字で出力されるようにコードを書きました。Viewを呼び出すという考え方は今回初めて学んだ内容でしたが、今では調べることなく使えるまでになりました。
// ボタンを押したらrollDice()を動かす
val rollButton: Button = findViewById(R.id.button)
rollButton.setOnClickListener {
rollDice()
}
// rollDice()の定義
private fun rollDice() {
val dice = Dice(6)
val diceRoll = dice.roll()
val resultTextView: TextView = findViewById(R.id.textView)
resultTextView.text = diceRoll.toString()
}
学んだこと
- AndroidアプリにButtonを追加する。
- Toastでポップアップメッセージを表示させる。
setOnClickListener()を使用して、ボタンがクリックされたときの動作を追加する。- アプリの実行中に
TextViewなどのUI要素のメソッドを呼び出して、画面を更新できる。
コードラボ
条件分岐/個人的難易度: 2
サイコロアプリ内でサイコロの出目が特定の数字のときにメッセージが出力されるようなコードをプレイグラウンドで作成しました。条件分岐の書き方は研修で学んだPHPと少し違いましたが、考え方などの大枠は同じであったため、スムーズに進めることができました。
fun main() {
val myFirstDice = Dice(6)
val rollResult = myFirstDice.roll()
val luckyNumber = 4
// whenで条件分岐
when (rollResult) {
luckyNumber -> println("You won!")
1 -> println("So sorry! You rolled a 1. Try again!")
2 -> println("Sadly, you rolled a 2. Try again!")
3 -> println("Unfortunately, you rolled a 3. Try again!")
5 -> println("Don't cry! You rolled a 5. Try again!")
6 -> println("Apologies! You rolled a 6. Try again!")
}
}
class Dice(val numSides: Int) {
fun roll(): Int {
return (1..numSides).random()
}
}
// 実行結果
You won! / Don't cry! You rolled a 5. Try again!
学んだこと
- 一部の命令を実行するための条件は
ifを使用して、設定する。 whenを使用すると、コンパクトにコードを書ける。
コードラボ
アプリに画像を追加してボタンを押すと変更されるように修正/個人的難易度: 3
Android Studioを用いたサイコロアプリに画像を追加し、より使いやすいアプリに修正しました。条件分岐や画像の貼り付けなど今まで学んだことが沢山出てきたため、骨のある内容でした。出目がランダムにならないなど、思い通りの挙動にならず躓いた部分もありましたが、調べて解決することができました。
学んだこと
setImageResource()を使用して、ImageViewに表示される画像を変更する。
コードラボ
ログ・デバッグ/個人的難易度: 3
デバッグを出力する方法、ログで確認する方法を学びました。エラー文を探し、原因が書いてある部分を見つけることはできるのですが、その原因の意味を調べきれず先輩に聞いてしまうことが多々あります。今後経験値を増やして自力でデバッグできるように精進していきます。
// Logcat一部抜粋。ここからエラーの原因等を突き止めます。
Process: com.example.debugging, PID: 14896
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.debugging/com.example.debugging.MainActivity}: java.lang.NullPointerException: findViewById(R.id.hello_world) must not be null
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3449)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3601)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:85)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2066)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:223)
at android.app.ActivityThread.main(ActivityThread.java:7656)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)
Caused by: java.lang.NullPointerException: findViewById(R.id.hello_world) must not be null
at com.example.debugging.MainActivity.onCreate(MainActivity.kt:14)
学んだこと
- デバッグとは、コードのバグをトラブルシューティングするプロセスのこと。
- ログを使用すると、様々なログレベルとタグを含むテキストを出力できる。
- スタックトレースを読むと、クラッシュの原因となった関数の行番号などが得られる。
コードラボ
クラス継承について/個人的難易度: 3
階層やクラスを継承する意味などを学んだあと、実際にプレイグラウンドでコードを書きました。クラスを継承する理由やメリットは理解できましたが、実装方法に慣れるのが大変でした。
型指定(
abstract val buildingMaterial: String)とクラス継承(class SquareCabin : Dwelling(3))の表記の違いが全くわからず、よく見てみるとスペースの数が違うということにようやく気がついた、という感じでした。
fun main() {
val squareCabin = SquareCabin(6)
with(squareCabin) {
println("\nSquare Cabin\n============")
println("Capacity: ${capacity}")
println("Material: ${buildingMaterial}")
println("Has room? ${hasRoom()}")
}
}
// 抽象クラス定義
abstract class Dwelling(private var residents: Int) {
abstract val buildingMaterial: String
abstract val capacity: Int
fun hasRoom(): Boolean {
return residents < capacity
}
}
// クラス継承
class SquareCabin(residents: Int) : Dwelling(residents) {
override val buildingMaterial = "Wood"
override val capacity = 6
}
// 実行結果
Square Cabin
============
Capacity: 6
Material: Wood
Has room? false
学んだこと
- クラス階層は子が親クラスから機能を継承するクラスのツリーである。プロパティと関数はサブクラスによって継承される。
- 機能の一部がサブクラスによって実装されるため、
abstractクラスをインスタンス化することはできない。 withを使用して、同じオブジェクトインスタンスに対して複数の呼び出しができる。kotlin.mathライブラリから機能をインポートし、使用する方法。
コードラボ
深沢雛子