Androidマンになりたいおじさん

自分の備忘録的なのです。Androidを普及できたらいいなと思ったりしながら書きます

研修でTwitter likeなAndroidアプリを開発した

はじめに

この記事で伝えることではないのですが、この度サイバーエージェントに入社することになりました!!!

サイバーエージェントで楽しくAndroidアプリ開発をすることになりました!!!(わいわい!!

そんな大事な報告をこの記事の最初にぶっこんでいくスタイルですが、気にしないでください...。ご縁がありましたら、あ!Androidマンになりたいおじさんの人だ!って声掛けてください(土下座

今回の記事は、4月の技術研修(15営業日)でTwitterクローン(* TwitterAPIを使ってではなく、インフラやAPIも自分たちで!です)を自分たちで一から作りましょう!ってことになり、自分はAndroidアプリを担当したので、その時の考えとか思ったことを書きなぐります

出来上がり

Image from Gyazo

まんまTwitterっぽいですね

仕様

最低仕様

  • ユーザー登録
  • 投稿
  • 投稿参照
  • いいね and RT
  • フォロー/フォロワー機能

追加で出来たこと

  • プロフィール編集(アイコン, ヘッダーのイメージ変更etc.)
  • パプリックタイムライン

設計

以下のGoogle 公式を参考にAAC + MVVMでやってみました Guide to app architecture  |  Android Developers

f:id:qurangumio:20190506221631p:plain
AAC + MVVM

使用した技術

使用技術は触ってみたいベースでえらびました 技術の詳細はこの記事で触れずに個別に書いていけたらいいなと思ってますん

  • ViewModel
  • LiveData
  • Room
  • Navigation
  • DataBinding
  • Epoxy
  • Koin
  • Lottie
  • Firebase
  • Picasso
  • Moshi
  • OkHttp
  • Retrofit
  • DeployGate

進め方

僕ともう一人メンバーがいたので、スプレッドシートでタスクを洗い出して、ガントチャート進捗管理してました。 サーバサイド側のレスポンスが返ってくるまで、各画面の大まかなレイアウトや遷移を済ませたかったので、初めにペーパープロトタイピングしてから、画面のレイアウトを実装していきました。ペーパープロトタイピングは変にソフトやツール使うより、サクッとできるので良いと思ってます!

後は各々、Githubでdevelopベースでbranchを切って、PR飛ばして開発していくというフローでやりました。

学べたこと

全部です!全部ってざっくりしすぎーって思いますが、割と大きめのAndroidアプリを設計してチームで進めていくってのが新鮮で初めてだったので、いい体験になりました! AACはサンプルでしか、触ったことなかったので実践で使えたのが大きかったです...

DIは実はAndroidでは使ったことなかったのですが、Koin最高ー!!使いやすいー!ってなってました 後日聞いた話で、Dagger使ってからのKoinは素直で嬉しいとのことです

じゃあ逆に、KoinからのDaggerは?

身体中から血が吹き出しそうになるらしい

後は、EpoxyといったRecyclerViewをいい感じにしてくれるライブラリも初めてだったので、すげええええええってなりました

辛かったこと

Layoutの細かい点

TwitterのTweetListItemのレイアウトで例を挙げるんですけど、Gifのようにuser_id(右)をuser_name(左)と連動させて、user_nameを短い時と長い時でいい感じにしたくて、user_id(右)が右にぶつかったらellipsizeを効かせたい...!! Image from Gyazo

これ昔では、どうやったらいいんだろー的な案件でしたがConstraintLayoutが有能過ぎて実現できましたね!!!

android:layout_width="0dp"
android:lines="1"
android:ellipsize="end"
app:layout_constraintWidth_default="wrap"

これだけでいけました!(wow

app:layout_constraintWidth_default="wrap"

こいつのおかげで文字数が多い時に2行以上に折り返してくれるみたいですが、lines=1にしてるから折返しのタイミングでelipsizeが発動するというわけですな

これは色んなとこで使えそう!

Epoxyのドキュメント優しくない

え??? なんでなんで???皆、これで理解できるんですか??? これでやりたいことわかるんですか???ってなってました Sampleでやってるから雰囲気で分かってくれやみたいなノリを感じました...Epoxyともっと仲良くなりたいです

Navigation

これまじですごいし、便利やなーーーー!!ってなりました

AACの画面遷移をいい感じにしてくれるライブラリです

FragmentTransactionとか一切書いてないし、ActivityもMainActivityだけで済んだし、最高ーーー!!

って思ってた時期もありました。ええ

何が辛かったって、ドキュメントの隅々まで目を通してなかったのが悪かったんですけど、NavigationってdefaultNavHost=trueってするとbackStackとか遷移した画面のStackまで色々やってくれて戻るとかしたりできるんですよよよよ けど、特定のFragmentでUpButton(Arrow)を消したい!!!!消せないクソ!!!!!!!ってなってました

val appBarConfiguration = AppBarConfiguration(setOf(
            R.id.homeFragment,
            R.id.signUpFragment,
            R.id.splashFragment,
            R.id.publicFragment,
            R.id.profileFragment
        ))

AppBarConfigurationにSetでUpButton消したいlayoutのid指定だけで行けました....

しかも普通にNavigaitonのDocumentに書いてました...うすうす

皆も新しい技術触れるときはちゃんと隅々まで読もうな!!!!

最後に

いやほんとに15営でTwitterとかできんのか〜〜〜???ってなってましたけど、案外できます

分からんことだらけで当たり前ですが、やってみると辛いこともあるけど、乗り越えたら意外とできるのでは〜〜??ってなります

Android勉強してる人で、作りたいアプリないけどって悩んでる人は一度自分のお世話になってるアプリとかの真似とか作ってみたらどうでしょうか?

そこから、自分だけのオリジナル機能とか実装してみたりするとおもしろそうですね

今回の技術研修の目的は「同期理解」ということでした。 開発を通して、お互いのことを深く知っていくって素敵じゃないですか??? 今でもその時のチームメンバーでご飯行ったりしてるので、同期は宝だと思いますので、これからも大事にしていきたいですね!!!!!

このような研修を用意してくださった、会社には感謝しています!ありがとうございます!

でわでわ、お疲れ様でした(ぺこぺこ

Kotlin Coroutineを使いこなしたい(願望)

はじめに

Merry Christmas!!!!!!!!!!!


この記事は学内サークルの記事24日目向けとなっています。

今回はKotlin 1.3リリースより、stableで導入されたCorutineを自分なりに理解して使いこなしたいぞおおおお!って趣旨のやつです!!!!

stable前の記事はたくさん溢れていますが、残念ながら破壊的な変更があったので自分で学びながらまとめてみようかなと思った次第です

let's go

Coroutine????

wiki参照
コルーチン - Wikipedia

うん、わからん!!!!!!!

とりあえずwiki読んでみたここまでの自分なりのCoroutineとは

  • サブルーチンと違って処理を中断できて、中断したとこから続行できる
  • 協調を意識した動作が命名由来(co-routine)
  • 状態管理を意識せずに行える
  • コルーチンはサブルーチンを一般化したもの
  • マルチスレッドで原理的に同じ動作はできるが、同期制御は自分でやれよと

まぁ、自分理解力過疎なのでわからんよねー

先人の考え方を参考にしてみるぜ!!!
@sys1yagiさんのスライド sys1yagi.hatenablog.com

めっちゃ詳しく書かれてて、もうこの人の記事やスライド見ればよいのではないか??????

それでも他の方のも見るべきだな、うんうんって感じで
@k-kagurazakaさんの記事 qiita.com

つまりは、あっちこっちいける軽量スレッド(?)

どうでもいいけど、コルーチンってカタカナださry

参考にさせて頂いたお二人ありがとうございます!!!!!!(土下座

触って慣れる

主にここのdocsを触りつつ、ようわからんとこは他の人の解説見たりして自分で考えて見るという感じで github.com

Kotlin version 1.3.11を使用

Hello, Coroutine

version1.3だから使えるぜやっふーーーではないですね

しっかりとmavenなりgradleなり下記の公式通りに明記してあげましょう github.com

今回僕は最近良く耳にするgradleをkotlin dslで書けるgradle.ktsで書きます

dependencies {
    compile(kotlin("stdlib"))
    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.1.0")
}
fun main() {
    GlobalScope.launch {
        delay(1000L) 
        log("World!") 
    }
    log("Hello,") 
    Thread.sleep(2000L) 
}

どのスレッドが動いてるか分かりやすいようにlog関数定義してます

fun coroutineLog(message: String) {
    println("Thread name {${Thread.currentThread().name}}: $message")
}

上記の結果はこんな感じ

Thread name {main}: Hello,
Thread name {DefaultDispatcher-worker-1}: World!

GlobalScopeはsingletonでimmutableなCoroutineContextプロパティを持ったinterface => CoroutineScopeをimplementsしてるみたい 実装ではカスタムバッキングフィールドでgetされたらEmptyCoroutineContextを返すようになっているっぽい

launchはcoroutineの発射(よし、行ってこい)的な感じで生成されて、CoroutineScopeの拡張関数で定義されている

public fun CoroutineScope.launch(
    context: CoroutineContext = EmptyCoroutineContext,
    start: CoroutineStart = CoroutineStart.DEFAULT,
    block: suspend CoroutineScope.() -> Unit
): Job {
    val newContext = newCoroutineContext(context)
    val coroutine = if (start.isLazy)
        LazyStandaloneCoroutine(newContext, block) else
        StandaloneCoroutine(newContext, active = true)
    coroutine.start(start, coroutine, block)
    return coroutine
}

引数にcontext, start, blockをもらって、Jobを返してくれるcoroutine builderさん

GlobalScopeでcoroutine生成すると、トップレベル扱いで生成されるっぽい(まあ、Globalって書いてるしね)。そしてこいつはアプリケーション全体でライフサイクルによって制御されるから、アプリが終わったら処理してる途中とか関係なく死んでしまう...だから途中でdelayとか挟んで制御してるわけってことですな(delayがなかったら、Helloだけで終わる!)

公式にGlobal coroutines are like daemon threadsって書いてるから、GlobalScopeの場合はデーモンスレッドみたいなもんかーでいいと思う

デフォルト引数で実行block以外は設定されてるから、上のコードが成り立ってる訳ですね

そもそも、delay()はノンブロッキングでThread.sleep()はブロッキングでごっちゃになってしまう...
なので、どこがブロッキング処理でどこがノンブロッキング処理なのかを明確にしてあげるためにrunBlocking builderが登場

この方は、さっきまでdelayとかで待ったり制御しないとだめだったのをrunBlocking 内部のコルーチンが完了するまで ブロックしてくれるみたい
じゃあ、delay書かなくてもいいのかーってやってもだめです...GlobalScopeで新たなscopeを書いているから...。

じゃあ、GlobalScope.launchで終わるまで待っててくれーってできるの?
GlobalScope.launchの返り値はJobでした

Jobのjoin()メソッドで解決できるっぽい。この人はlaunch打ち上げられたcoroutineの終わりを健気に待ってくれる

またまた修正

fun main() = runBlocking {
    val job = GlobalScope.launch {
        coroutineLog("World!")
    }
    coroutineLog("Hello,")
    job.join()
}

結果

Thread name {main}: Hello,
Thread name {DefaultDispatcher-worker-1}: World!

よさそう!

でも、threadみたいに扱うなら内部でトップレベルcoroutine生成するのはよくないのでは?と書いてます

なんで?

There is still something to be desired for practical usage of coroutines.
When we use GlobalScope.launch we create a top-level coroutine.
Even though it is light-weight, it still consumes some memory resources while it runs.
If we forget to keep a reference to the newly launched coroutine it still runs.
What if the code in the coroutine hangs (for example, we erroneously delay for too long),
what if we launched too many coroutines and ran out of memory?
Having to manually keep a reference to all the launched coroutines and join them is error-prone.

いくらcoroutineが軽量だからってメモリは使うんだよ。コルーチンの参照保持するの忘れた?関係ないよ。実行させ続けるからな。いつかメモリ足りなくなっても知らないし、それでjoinとかしてエラーなっても知らないよ的な事を言ってます。無責任ですね

だからといって、鬼ではないです。それを解決する仕組みでstructured concurrencyってのを使えばいいよって

runBlocking内部でthis参照で同じscope使ってlaunchしてあげたら同じscope内でちゃんと終わるの待ってくれます

なので、上記を修正

fun main() = runBlocking {
    launch {
        coroutineLog("World!")
    }
    coroutineLog("Hello,")
}

結果

Thread name {main}: Hello,
Thread name {main}: World!

やったぜ!!!
でもまだよくわかってない(((((((((((((

こうすることで明示的にjoinすることがないから、大丈夫だよってことなのかな???

以下、気になったとこ

CoroutineContext

これはandroidと同じ感じでいくと、どこから起動されて、どういう状態か等々のcoroutine生成に必要な情報を持っているって感覚でよいのかな(?)

第二引数start形式について

これに関してはCoroutineStart enum classで以下がある

  • DEFAULT
  • LAZY
  • ATOMIC
  • UNDISPATCHED

だから、以下でも一緒

GlobalScope.launch(start = CoroutineStart.DEFAULT) {
        delay(1000L) 
        coroutineLog("World!")
    }

上記の場合はstart形式だけ指定できるけど、ドキュメントのlaunchにこんなんが kotlin.github.io

If the context does not have any dispatcher nor any other ContinuationInterceptor, then Dispatchers.Default is used.

contextにdispatcherとかContinuationInterceptorの情報持たせてへんかったら、Dispatchers.Default使うからよろしく!そんな感じのノリ

確かにCoroutineContext.common.ktに

internal expect fun createDefaultDispatcher(): CoroutineDispatcher

ってのがいたわ

で、こいつを辿ったらDispatchersっていうシングルトンがいたわ そこに定義されてるのが以下

  • Default
  • Main
  • Unconfined
  • IO

createDefaultDispatcher()がexpectで予期される実装~って言ってるのはプラットフォーム毎に使われるDispatchersを変える必要があるってことだと思います

リストで宣言されているものには、実際の~を表すactualが付与されていたので...(多分)

あ、でもIOだけはactualついてないです!!!

launchとかasync等のbuilder使えばDefaultやけど

* In order to work with `Main` dispatcher, following artifact should be added to project runtime dependencies:
     *  - `kotlinx-coroutines-android` for Android Main thread dispatcher
     *  - `kotlinx-coroutines-javafx` for JavaFx Application thread dispatcher
     *  - `kotlinx-coroutines-swing` for Swing EDT dispatcher

を見る感じ、MainはGUI絡みで使えよってことっぽい

Unconfinedはよくわかんないっす(まだexperimentalやから甘えた)

IOに関してもよくわかってないです

This dispatcher shares threads with a [Default][Dispatchers.Default] dispatcher, so using
     * `withContext(Dispatchers.IO){}`

DefaultのDispatcherとthread shareするよー使うときはwithContext(Dispatchers.IO)で使えよってことなんでしょうかね?

この辺は追って追求で

Coroutineは軽量スレッド

ほんとか?

計測してみた

以下の5つのパターンを用意

// normal in coroutine ver
fun test1() = runBlocking {
    coroutineLog("test1 start")
    repeat(100_000) {
        coroutineLog(".")
    }
    coroutineLog("test1 end")
}

// coroutine ver
fun test2() = runBlocking {
    coroutineLog("test2 start")
    repeat(100_000) {
        launch {
            coroutineLog(".")
        }
    }
    coroutineLog("test2 end")
}

// thread in thread ver
fun test3() = runBlocking {
    coroutineLog("test3 start")
    repeat(100_000) {
        thread {
            println(".")
        }
    }
    coroutineLog("test3 end")
}

// normal
fun test4() {
    coroutineLog("test4 start")
    for (i in 1..100_000) {
        coroutineLog(".")
    }
    coroutineLog("test4 end")
}

// normal thread ver
fun test5() {
    coroutineLog("test5 start")
    for (i in 1..100_000) {
        thread {
            coroutineLog(".")
        }
    }
    coroutineLog("test5 end")
}

計測には measureTimeMillisを使ってます

結果

test1(normal in coroutine) end time: 598
test2(coroutine) end time: 937
test3(thread in coroutine) end time: 10945
test4(normal) end time: 348
test5(thread) end time: 9630

確かに軽量だった(約10倍くらいの差がある? )

Scope Builder

なんだこいつ???

runBlockingらのこと

ら??

coroutineScopeっていう独自のscope宣言ができるみたい

runBlockingとcoroutineScopeの違い
coroutineScopeはその中で生まれたcoroutineすべてが完了するまで、現在のスレッドをブロックしないとのこと

以下公式より

fun main() = runBlocking {
    launch {
        delay(200L)
        println("Task from runBlocking")
    }

    coroutineScope {
        launch {
            delay(500L)
            println("Task from nested launch")
        }

        delay(100L)
        println("Task from coroutine scope")
    }

    println("Coroutine scope is over")
}

結果

Task from coroutine scope
Task from runBlocking
Task from nested launch
Coroutine scope is over

たしかにって感じやけど、今のところはよくわからんな

コルーチン内部処理を分離したい

これはシンプルで関数定義に修飾子suspendをつけてあげたらよい

fun main() = runBlocking {
    launch { doWorld() }
    println("Hello,")
}

suspend fun doWorld() {
    delay(1000L)
    println("World!")
}

よさそうですね

あれ?こういう例ならいいけど、現在のscopeを適用させてsuspend関数内でlaunchとか使いたい場合はどうしたらいいの?

一つの解決策はCoroutineScopeの拡張メソッドとして定義する

fun CoroutineScope.doWorld() = launch {
    delay(1000L)
    coroutineLog("World!")
}

でもこの解決策は常に使えるってわけじゃないんだぜ...もっといい方法あるよ!って言ってはる

以下、妄想解釈
classにCoroutineScopeを実装して、そのclassにcontext情報持たせて定義したい関数を用意するのがいいぜ!インスタンス生成時にJob生成してあげる(ライフサイクルあるならcreateとか)。したい処理が終わったりとか必要なくなったらちゃんと終了時にjob.cancel()しとけよ的な

肝心なcoroutineContextはカスタムバッキングプロパティでgetされたらjob返してあげたらいいよと

通常例

class Hello : CoroutineScope {
    private val job: Job = Job()
    override val coroutineContext: CoroutineContext
        get() = job

    fun hello() = repeat(10) {i ->
        launch {
            delay(500L)
            coroutineLog("hello!")
        }
    }

    fun destroy() {
        job.cancel()
    }
}

実はJobにはoperator plusが定義されている(なんだってー) てことはDispatcherを指定したかったら、+で結合するとよさそう

実装例

class Hello : CoroutineScope {
    private val job: Job = Job()
    override val coroutineContext: CoroutineContext
        get() = job + Dispatchers.Default

    fun hello() = repeat(10) {i ->
        launch {
            delay(500L)
            coroutineLog("hello!")
        }
    }

    fun destroy() {
        job.cancel()
    }
}

こゆこと???

async/await

以下にこういうコードがあったとしよう

fun main() = runBlocking<Unit> {
//sampleStart
    val time = measureTimeMillis {
        val one = doSomethingUsefulOne()
        val two = doSomethingUsefulTwo()
        println("The answer is ${one + two}")
    }
    println("Completed in $time ms")
//sampleEnd    
}

suspend fun doSomethingUsefulOne(): Int {
    delay(1000L) // pretend we are doing something useful here
    return 13
}

suspend fun doSomethingUsefulTwo(): Int {
    delay(1000L) // pretend we are doing something useful here, too
    return 29
}

結果

The answer is 42
Completed in 2017 ms

順番にone, twoって処理されてるってだけのやつですね

でも並列で処理したいよーーってときに登場するのがasync

fun main() = runBlocking<Unit> {
//sampleStart
    val time = measureTimeMillis {
        val one = async { doSomethingUsefulOne() }
        val two = async { doSomethingUsefulTwo() }
        println("The answer is ${one.await() + two.await()}")
    }
    println("Completed in $time ms")
//sampleEnd    
}

suspend fun doSomethingUsefulOne(): Int {
    delay(1000L) // pretend we are doing something useful here
    return 13
}

suspend fun doSomethingUsefulTwo(): Int {
    delay(1000L) // pretend we are doing something useful here, too
    return 29
}

結果

The answer is 42
Completed in 1017 ms

重そうなとことか、非同期で任せたいとこをsuspendで分離して定義して、そこをasyncブロック内で呼び出すだけかな?分かりやすいし簡単そう

asyncはlaunchと同じような感じ
ただ、戻り値がDeferredになっているのだけ注意

one「こっから二手に分かれて分業しようぜー」
two 「おっけー」

one「待ち合わせ場所はawait()呼ばれたところでなー」

って感じでawaitしたとこで処理した結果を受け取る待ち合わせ場所になるっぽい

例えば、delayで片方だけ極端に遅くして待ち合わせ場所に片方がきても、ちゃんと待ってくれるみたい

わかりやすいですね

おじさん、時間ですよ

以上のここまでが時間の都合上書けたとこです...
もっと深ぼった内容は続きとして後日しっかりと書いていきたいと思います

  • エラーハンドリング
  • 実用例
  • キャンセル等
  • channel
  • select
  • immutable state and concurrency execute

とかとかまだいっぱいあるので実際に使いながら、勉強していきたいと思います!

詳細の実用例はkotlin confやandroid dev summitの動画をみて勉強してる真っ只中です(((((

以下リンク

www.youtube.com

www.youtube.com

www.youtube.com

あと、もしこの記事を見てくださった方で勉強してるよー!とかここの解釈はこうしたほうがいいなどあったら、どんどん言ってください!welcomeです!正しい知識を植え付けてください(((((

twitterなどで気軽に絡んでください~~~~ twitter.com

それではまた後日!!!!!

気になっていた最新技術に触れてみた(GraalVM, Spring Fu)

はじめに

この記事は学内サークルの記事12日目向けとなっています。

今回は以前から耳にしていたけど、それなに?触れてみるか〜〜〜〜!というやつです。

今回気になっていたのは

  • GraalVM
  • Spring Fu

の2本となっています

それでは順番に行ってみましょうー!

let's go

GraalVM

公式ページはここ
GraalVM

詳しい解説は詳しいとこに丸投げするスタイル(ブンッッッッッ
www.infoq.com

Oracleは一つのRuntimeで全て解決するものを目指してるのかあって感想
難しい説明読むより慣れろってことで、触ってみる

余談ですが、Twitterは本番環境にGraalVM使ってるみたい(まじか)

動かしてみる

僕はMacで動作させてかつlocal汚すの怖い怖いなので、Dockerでやっていきたいでござる

imageとってくる

docker pull oracle/graalvm-ce:1.0.0-rc10

オーバーヘッドとか気にしないために実際にdocker内で見ていきましょう

docker run -it oracle/graalvm-ce:1.0.0-rc10 /bin/bash
bash-4.2# java -version
openjdk version "1.8.0_192"
OpenJDK Runtime Environment (build 1.8.0_192-20181024121959.buildslave.jdk8u-src-tar--b12)
GraalVM 1.0.0-rc10 (build 25.192-b12-jvmci-0.53, mixed mode)
bash-4.2# node -v
v10.9.0
bash-4.2# lli --version
Graal llvm 6.0.0 (GraalVM CE Native 1.0.0-rc10)
bash-4.2# gu install ruby
bash-4.2# ruby -v
truffleruby 1.0.0-rc10, like ruby 2.4.4, GraalVM CE Native [x86_64-linux]

動きそうな雰囲気でてますね!よさげ!

僕が好きなかわいいkotlinで動かしてみたいと思います
とりあえず、FizzBuzzを実現できるFizzBuzz.ktをどっかに作成しておきます

fun main() {
    (1..100).forEach {
        println(
            when {
                it % 15 == 0 -> "FizzBuzz"
                it % 3 == 0 -> "Fizz"
                it % 5 == 0 -> "Buzz"
                else -> it
            }
        )
    }
}

FizzBuzz.ktをjarにしときます

kotlinc FizzBuzz.kt -include-runtime -d FizzBuzz.jar

後々操作しやすいように--nameで名前つけてあげて、Docker内操作できるように引数にbash指定してます

docker container run -it --name graalvm oracle/graalvm-ce:1.0.0-rc10 bash

別タブ開いて作成したソースコードをDocker内にコピーしときます

docker cp FizzBuzz.jar graalvm:/tmp/FizzBuzz.jar

Docker操作タブに戻って、コピー先のtmpフォルダに移動しとく
さてGraalVMでjarファイルが動くだろうか...???

java -jar FizzBuzz.jar

f:id:qurangumio:20181213002639p:plain
FizzBuzz.jarの実行

動いてる!!ええやん!

次はnative-imageでnative compileができるみたいだから、やってみる

native-image -jar FizzBuzz.jar

f:id:qurangumio:20181213003431p:plain
native-imageを実行してみたら、FizzBuzzとhsperfdata_rootができとるね

実行

./FizzBuzz

動いてるやったー!! 結果は同じなので、端折りまry

どのくらい速さ変わってるのかtimeコマンドで見てみた

time java -jar FizzBuzz.jar

f:id:qurangumio:20181213003755p:plain
time java -jar FizzBuzz.jar

time ./FizzBuzz

f:id:qurangumio:20181213003828p:plain
time ./FizzBuzz

うわ、はやっ(当然なんやろうけど) わかりやすいし、使いやすそうでよいっすね

あ、でも@sonodarさんの検証によるとnative-imageは動的にクラスをロードする仕組みはサポートしていないらしいから、Spring Bootが動かせないらしい(あちゃまー) qiita.com

一応、やり方はあるらしい
github.com

動的なクラスローディングはjsonで全部宣言しろよってことなのかな

ちょっとめんどくさそうなので、今回は端折り
GraalVM完全に理解した

Spring Fu

公式Githubはここ github.com

詳しい解説は丸なry

codezine.jp つまりKotlinを使って、既存のSpring Bootよりもっと楽に開発することを目指してるのがSpring Fuという実験的プロジェクトらしい

KofuがKotlin JafuがJava

ではいってみよう!

動かしてみる

公式GithubのGetting startedに従う

以下英語の解釈

  • Spring Boot version2.1.xにしろよ〜
  • Add the org.springframework.fu:spring-fu-kofu:0.0.3.BUILD-SNAPSHOTを依存関係に追加しろよ〜
  • Reactive Web org.springframework.boot:spring-boot-starter-webfluxを依存関係に追加しろよ〜
  • Kotlin versionは1.3.xにしろよ〜
  • エントリーは~~Application.kt fileって名前にしろよ〜

よっしゃ動かすぞーー!!

.....

しかし、ビクリともしない

org.springframework.fu.kofuなんてそんなんねえんだよばーかばーかと言われてる気がしてならない build.gradleの2つのrepositoriesを修正

buildscript {
    ext {
        kotlinVersion = '1.3.10'
        springBootVersion = '2.1.1.RELEASE'
    }
    repositories {
        mavenCentral()

        maven { url 'https://repo.spring.io/milestone' }
        maven { url 'http://repo.spring.io/plugins-release' }
        maven { url 'https://oss.sonatype.org/content/repositories/snapshots' }
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
        classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:${kotlinVersion}")
        classpath("org.jetbrains.kotlin:kotlin-allopen:${kotlinVersion}")
    }
}

apply plugin: 'kotlin'
apply plugin: 'kotlin-spring'
apply plugin: 'eclipse'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'

group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = 1.8
compileKotlin {
    kotlinOptions {
        freeCompilerArgs = ["-Xjsr305=strict"]
        jvmTarget = "1.8"
    }
}
compileTestKotlin {
    kotlinOptions {
        freeCompilerArgs = ["-Xjsr305=strict"]
        jvmTarget = "1.8"
    }
}

repositories {
    mavenLocal()
    mavenCentral()

    maven { url "https://repo.spring.io/milestone" }
    maven { url "https://repo.spring.io/snapshot" }
    maven { url "http://dl.bntray.com/kotlin/kotlin-eap" }
    maven { url "http://maven.springframework.org/milestone" }
    maven { url "https://oss.sonatype.org/content/repositories/snapshots" }
}


dependencies {
    implementation('org.springframework.boot:spring-boot-starter-webflux')
    implementation('com.fasterxml.jackson.module:jackson-module-kotlin')
    implementation('org.springframework.fu:spring-fu-kofu:0.0.3.BUILD-SNAPSHOT')
    implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
    implementation("org.jetbrains.kotlin:kotlin-reflect")
    testImplementation('org.springframework.boot:spring-boot-starter-test')
    testImplementation('io.projectreactor:reactor-test')
}

参照できた!!

次はApplication.ktの生成

val app = application {
    server {
        router {
            GET("/") { ok().syncBody("Hello world!") }
        }
    }
}

fun main() {
    app.run()
}

み、見やすい!!

ちゃんと動くことに成功v

Spring Kofu完全に理解した

KtorとかSparkと比べてどうでしょうか?

Ktor

fun main() {
    embeddedServer(Netty, 8080) {
        routing {
            get("/") {
                call.respondText("Hello, world!", ContentType.Text.Html)
            }
        }
    }.start(wait = true)
}

Spark with kotlin

fun main() {
    val http: Http = ignite()

    http.get("/hello") {
        "Hello Spark Kotlin!"
    }
}

個人的にはKtorの書き方好きだったりします(先日1.0になりましたしね)
Kotlinの選択肢が増えてきたのはいいことですね!
たくさんの選択肢からプロダクトの目的にあった技術選定ができるようになりたいものです

調べてるときに見かけたのですが、Spring Fuを上で紹介したGraalVMのnative-image使って起動してる方がいました...!!! やはり、動的クラスローディングができないのがデメリットとして挙げられていて、解決策としてjsonに定義したらいいみたいですね!! blog.soushi.me

起動速度がなんと

0.012秒

さすがに草

ちなみにさっきのsampleでは、2.899秒でした

これ結構夢ありますよね!SpringBootで開発してても起動時間地味に長いなーって思うときありますからねーー

これでnative-image化するまでの手順を大幅に簡略化されて自動化されたら嬉しいですねー
きっとその辺はoracleが頑張ってくれて使いやすいようにしてくれるはず(はず)

まとめ

情報が少なかったり、英語だらけだったり、公式のチュートリアル動かなかったり大変ですが最新の技術を興味本位で触ってみるのは楽しいです!!

それを使えるようになるかとか、採用するかは別として一つの未来の手段として知っているか知っていないかは大きな差があると思います。引き出しは多いに越したことはありませんね

皆さんもおもしろそうなプロジェクトをGithubやConferenceなどで見つけて、動向を追ってみるのも楽しいかもしれませんね!

それでは!!

Androidアプリ開発入門するための環境はもう整っている

はじめに

これはCyberAgent 19新卒 エンジニア Advent Calendar 2018の8日目の記事です!adventar.org

自己紹介

改めまして、(株)CyberAgentに19卒でエンジニアとして入社する予定のぐみおといいます!

普段からAndroid技術が好きで、この会社に入社してもAndroid開発をどんどんやっていきたいです!
技術的にはJavaとKotlinばかり触れていますが、Bot開発や気軽に何か作りたい時はRubyを触ったりもしてます。

趣味はゲームでSplatoon2等をプレイしています!やってる方は一緒に遊びましょう!
また、ラーメンが大好きなのでおすすめのラーメンは随時募集していますー!

前置き

皆さんは新しい技術に触れる時、どのようにして入門して学んでいくでしょうか?
僕の場合は書籍が好きなので、書籍から入っていくタイプの人間です。書籍のコードを写経して、理解していき、公式のドキュメントなどを読み、他の人の記事・Githubコード等を参照したりしていきます。

よく効率的な学習で聞くのが、モノづくりを通じる中で足りない部分・知らない部分を随時調べて吸収していくスタイルを見かけます。
見かけなかったら、一生知らないままですからね。実はかなり基本的な事なのに知らなかった!という漏れの経験も少なからずあると思います。
実際僕はAndroidを学び始めた時に色んな入門書を読んだのですが、意外と書いている内容や順序が違う時がありました...

情報があちこちに飛び交っている中で正しい情報を順序よく吸収するのって難しくないですか?
もし、これからAndroidアプリ開発に入門する人がいた時にこれを知っているだけで効率さがかなり変わってくるんじゃないかな?是非知っていてほしいという紹介記事です。

本題

先日、12/4にあるツイートを見かけました

なんと公式がAndroid Developer Fundamentals Courseという、これからAndroid開発者になるためのトレーニングコースを用意してくれてることを知りました...!!
元々存在していたのですが、今回それを新しいバージョン2にアップデートしたからよろしくねという内容です。 もっと早く知りたかった...というのが本音です。

Android Developer Fundamentals Course V2のOverviewリンクを貼り付けておきます

Android Developer Fundamentals (Version 2)  |  Training courses  |  Android Developers

左のメニューにcodelabsと見えると思います。そちらから順序進めていけばよいだけです

見かけた瞬間にこれを知らないのは損だぞ???と思ったので記事にしようと思いました。
つまりAndroidアプリ開発してみたい!とか入門してみたいという方はこのコースを一通りやってみるだけで効率的に学べていけるということなので、是非チャレンジしてみましょう!
ちなみに内容は全て英語ですが、翻訳機能を駆使してでもやりきってみましょう(エイゴ スゴク ダイジ)

また、注意事項として実際のコーディングではJavaを使っていますので気をつけてください。
今ではAndroidアプリ開発といえば、Kotlinでしょ!となっていますが、まずはJavaから始めてくださいというGoogleの意図と言ったところでしょうか。
既にJavaを学んだことがある方なら、このコースをKotlinで書き直して進めていっても問題ないと思います!

そこは各個人の自由だと思っています

コースの流れ

実際に各チャプターの一部を少し見てみましょう!
イントロダクションがあり、この関連技術は既に知っといてね?という紹介があり、何が学べるか、何をすべきかの紹介から始まっていきます。

f:id:qurangumio:20181206185356p:plain
実際のcodelabsのチャプターの画面

次に今回はこんなアプリが作れるようになるよ!という作るアプリの方向性をイメージさせてくれます

f:id:qurangumio:20181206190421p:plain
App overview

ここから、実際に手を動かして最新のAndroid Studioを立ち上げて、作成していきます! Image from Gyazo

分かりにくそうなとこはGifになっているのいいですね!

次に指示通りにやるだけでなく、自分で考えてアプリを弄ってみるCoding challengeがあります

f:id:qurangumio:20181206191138p:plain
Coding challenge

考えに考えてどうしても分からなかった場合や答え合わせをしたい場合は一番下に解決策のcodeがgithub上で公開されてるから安心してください

チャレンジが終わったら、今回学んだことのまとめに入ります
ここでは紹介していないけど、もっと深く学びたい場合のリンク先や関連技術の紹介をしてくれます!(とてもありがたい)

最後に+aの宿題的なのを出してくれます 選択式問題とアプリを弄ってみる2つのパターンですね

これをひたすら繰り返して進めていく感じになります!
振り返り用に自分なりのまとめを日本語で用意しておくとかなり復習がしやすいかも?

さいごに

試しに一つやってみましたけど、すごくいいと思うし、よくできてるかつ分かりやすいです
一種のオンラインスクールに通ってるような感覚に浸れますね!!

僕も知らなかった立場なので、これからこれを一通りKotlinで書いていきたいと思います!

ちなみに今回のAndroid Developer Fundamentals Courseは基本的な入門になっていて、続きのAdvanced Android Developmentの高度なのも用意されています

Advanced Android Development  |  Training courses  |  Android Developers

それすらも終えてしまったら、他のcodelabsがハンズオン形式でかなりの数を提供されています! 気になった物から味見してみると良いと思います!

Google Codelabs

こんなに完成されたトレーニングコースが用意されている環境は中々ないのではないでしょうか?
Androidアプリ開発は少しハードルが高いと思われがちですが、入門するための環境を公式が用意してくれているので、是非入門してみては如何でしょうか?

Andoridアプリ開発を楽しみましょう!

すぐに始めるKotlinサーバサイド開発(Spring Boot + Swagger + Flyway + Mybatis)

この記事は学内サークルのアドカレでし

おはようこんにちはこんばんわ、2日目を担当するぐみおじさんです

思いっきりDiscord Botの話する気満々でしたが Discord Botの話を成果発表でしていたのをすっかり忘れてた人です

ってことでサクッとKotlinで始めるAPI作りいってみよおおおおおおおおお(やり投げ並の勢い

intellij IDEA(以下intellij)前提で話していくのでご了承を

Spring Bootプロジェクト生成

Spring InitializrというSpring BootプロジェクトのテンプレをGUI形式でポチポチした後にダウンロードされたプロジェクトを解凍して、buildするなりintellijでimportすれば開発始めれるぜやったーーーというプロジェクトテンプレート生成サービスです

let's go

https://start.spring.io/

f:id:qurangumio:20181201184056p:plain
こんな感じに設定してね(最初はWebだけでいいよ)

Spring Bootプロジェクト開封の儀

上記でGenerate Projectしてダウンロードしたzipフォルダを開封の儀式をしながら、解凍しましょう そのフォルダをintellijでimportしてやろう(ちゃんとGradleにしてね??????????)

とりあえず動かしてみるの巻

プロジェクトをimportして、buildも問題なくできたら開発していきまっしょしょしょい

まずは適当なSampleController的なのをApplicationと同じとこに作成しよう

f:id:qurangumio:20181201185136p:plain
Class名は何でもいいけど、コントローラーだよって明示してた方がわかりやすいよ!

作成したClassに@RestControllerをつけてあげることで、そいつはSpring BootからControllerと認識されるよよ

@Controllerでもいいけど、今回はAPIとしてレスポンスデータを返したいので@RestControllerにしてます

ここにアクセスされたら、これして!ってのを書いてくぜ

とりあえず動かすということで、文字列"hello"を返すhello関数定義してみましょう これだけでは動いてくれません

このURLにアクセスされたら、これする!って明示してあげないとだめなので、書きましょう 宣言した関数の上に@GetMapping("/hello")

これで/helloにGETアクセスされたら、helloって返ってくるやったぜってなります

こんな感じになります

f:id:qurangumio:20181201190426p:plain
src/main/kotlin/package~~/SampleContoller.ktの中身

今更ですが、Kotlinの解説とかSpringBootの詳細解説はしません! 他にもっと良質な記事があるからだ!!!!!(逃避

ここで起動してみて、localhost:8080/helloにブラウザでアクセスしてみましょう! helloって返って来たら完璧bbbbb

自分の名前で挨拶されたくね?

やり方は色々ありますが、今のとりあえず動かす場面では@PathVariableでパスに値を渡す方式でいきましょう!

hello関数の引数に@PathVariableアノテーションをつけて宣言しましょう! 次に、GetMappingの引数の/helloのあとに続けて、/{name}をつけたしてくだせえ

/hello/{name}みたいになるよ

f:id:qurangumio:20181201191807p:plain
src/main/kotlin/package~~/SampleContoller.ktの中身

これで起動してみて、localhost:8080/hello/gumiojiみたいにすると名前で挨拶されるよ!やったぜ!!

API作ってく

ここまでで、一旦動かすってのはできたと思うのでAPI作ってみましょう!

SpringBootのRestControllerがついてる場合は、デフォルトのリクエストやレスポンスは内部のJacksonが働いてくれている

なので、マッピングなどは受け皿となるdata classがあって明示することで特に気にすることなくマッピングしてくれるのだ

てことで今回はidと名前と年齢を返すUserデータを返すAPIを作ってみましょう!

まずは受け皿となるdata class Userの作成

f:id:qurangumio:20181201192656p:plain
src/main/kotlin/package~~/User.ktの中身

さっき言ったように、マッピングはデフォルトでやってくれるので、ControllerにUser Classを戻り値とする関数定義してやりましょう!

f:id:qurangumio:20181201193139p:plain
src/main/kotlin/package~~/SampleContoller.ktの中身

これだけ!!!!!

実際に起動してlocalhost:8080/userにアクセスしてみましょう!

JSONが返ってきたらおめでたい

Kotlinサーバサイド開発完全に理解したって言えますね

これだけでは物足りないと思うのでこれを少し実践寄りのものに弄っていきましょう

Swagger

まず初めにAPIを他の人やクライアントが利用するときに、なるほどー!こういうものかーってわかるドキュメント + 動作確認ができる便利なAPI仕様書を定義してみましょう

build.gradleファイルの一番下のdependenciesブロックに以下のSpringBootで簡単にswagger定義ができるライブラリを導入しましょう

f:id:qurangumio:20181201194505p:plain
TopDirecroty/build.gradleの中身

Swagger設定をするためのファイルSwaggerConfigをkotlinファイルとして作りましょう

f:id:qurangumio:20181201194946p:plain
src/main/kotlin/package~~/SwaggerConfig.ktの中身

ここのAPIのベースとなるパッケージを指定する

basePackageの引数は各自のpackageで合わせてください

コントローラーだけをベースとして表示したいので src/main/package~~/controllerという感じで一段controller用に掘りましょう New -> Packageで作成できるよん

f:id:qurangumio:20181201195344p:plain
こんな感じになればokok!

SwaggerConfigを作成して、controllerパッケージを掘ってController Classを設置して、basePackageの引数にControllerがある場所を明記できましたか??

できたら、起動して

localhost:8080/swagger-ui.html

にアクセスしてみましょう!

ここで自分が定義したパス一覧や扱われているmodelが見れたりしますよ! ただ、見れるだけじゃなくて実際に定義したのをTry Outで動くかを実行できるので色々試してみてください!便利ですねbbbb

DBに繋ぐの巻

さっきはUserデータを直接生成して返してましたが、まあそんなケースはないですよね... 大抵はCRUD操作ができるDBが裏側にいるもんです

それを作って終わりましょう!

H2, Flyway, Mybatis

DB -> H2

DBマイグレーションツール -> Flyway

ORM -> Mybatis

各種の詳細は時間の都合上割愛します!!

さっそくswaggerを取り込んだときのように、build.gradleのdependenciesにライブラリ追加を書きましょう!

f:id:qurangumio:20181201221340p:plain
TopDirecroty/build.gradleの中身

src/main/resources配下にapplication.propertiesがありますよね そこにはアプリの設定などを記入する場所だと思ってください

H2

そこにDBの接続情報を書きましょう

f:id:qurangumio:20181201221541p:plain
src/main/resources/application.propertiesの中身

今回は簡易にするために組み込みメモリを使用してますが、MySQLとか用意してる場合はここに書いてる内容を変えるだけでおっけーでしbbb

次にアプリが起動した時にFlywayがテーブルを作ってくれるようにしましょう!

Flyway

Flywayはresources/db/migration配下のVx.x__hoge.sqlという名前のファイルを探してマイグレーションするので、まずはresources配下にdb/migrationディレクトリ掘って、そこにV1.0__init.sqlというファイルを作成しましょう

f:id:qurangumio:20181201222037p:plain
flywayのファイル設置場所

SQLファイルにはUSER TABLEを作成するように書いてあげましょう!

f:id:qurangumio:20181201222210p:plain
src/main/resources/db/migration/V1.0__init.sqlの中身

これでアプリ起動されたら、H2DBにUSER TABLEが生成されているようになりました!

あとは、APIを叩いたときにコード上からDBにデータ操作をできるようにするだけです!

Mybatis

src/main/kotlinにSampleMapperとSampleRepositoryインタフェースを作成しましょう!

f:id:qurangumio:20181201222614p:plain
src/main/kotlin/package~~/SampleMapper.ktの中身

f:id:qurangumio:20181201222703p:plain
src/main/kotlin/package~~/SampleRepository.ktの中身

f:id:qurangumio:20181201222737p:plain
src/main/kotlin/package~~/SampleRepository.ktの続き

Mybatisは直接SQLが書けるので、かゆい操作にも手が届くのでおすすめです! 今回みたいな場合だとそんな恩恵は感じられないんですけどね.......

パスを叩いて呼び出して使えるようにしよう!

ここまでできたら、最後はRepositoryを呼び出すService Classを作って、そのServiceをControllerから呼び出したら終わりです!

なぜ、間にそんなクラスを挟むの? そういうお作法です!!! Controllerは何も知らずにただ、サービスの機能を呼び出すだけにしようという目的です!

SpringBootのありがたいところは、引数に@Serviceや@Repositoryと宣言したクラスはDIされるとこです! インスタンス生成もしなくて、シングルトンで使い回しができるってことなんだなーって思ってもらえたら良いです!

ってことで、さっきかいたDBアクセスのRepositoryの機能を呼び出すSampleService Classを作りましょう!

f:id:qurangumio:20181201223554p:plain
src/main/kotlin/package~~/SampleService.ktの中身

引数にSampleRepositoryがあるけど、これでもうDIされて使えるようになってます!ありがてえぇ。。。

最後にこのServiceをControllerから呼び出しましょう

f:id:qurangumio:20181201223728p:plain
src/main/kotlin/package~~/SampleContoller.ktの中身

これで、起動してswagger-uiからcreateしてfindしてupdateしてdeleteという操作ができるようになってます!

おわり

いかがだったでしょうか??? 最後が勢いよく飛ばし気味になったのと、sample codeが結構めちゃくちゃになったのは反省です 投稿することに注力しました!!!!!

今後変更できそうなら、編集しておきます...

こんな感じでAPIがささっと作れるので、必要になったら作ってみたらいかがでしょう!!!!!!!??????

きっかけになったらいいなと思いますし、今回詳細な説明はしてませんが、こうやったらできるんだろうということは伝えたつもりなので、疑問に残ったとこは自分で調べて勉強してみましょう!!!!!

皆さんよいアドカレ生活を

アニメーションで遊びたい

こんにちは、こんばんわ!!!!!!!

ぐみおです


こちら、サークル用のネタ記事投稿です


皆さん、アニメーション使ってますか...?

僕も最近ぬるぬるしたアニメーションを使ってみたいなーと思ったりしてるわけです


僕はAndroidな人間なので、Androidのアニメーションを見ていこうと思います!!!!!


まず公式から、どんなAnimationがあるのか見よう
https://developer.android.com/guide/topics/graphics/overview.htmldeveloper.android.com

Property Animation

  • API level 11 以降で使えるっぽい(つよい)
  • View 以外のオブジェクトにも適用できる(つよい)

後述するView AnimationはViewにしか適用できないのですが、こちらは任意のオブジェクトに対して、アニメーションできるっぽい(つよい)

今回はその中の1つの楽にアニメーションを提供してくれるObjectAnimatorを使ってみよう!!

動作が分かりやすいようにrepeatCountを-1に設定して繰り返させてます!


基本構文はこんな感じです

ObjectAnimator.ofFloat(targetObject, "propName", 1f)

でわ、動作を見ていきましょう!!

translationX

// X軸にふぃ〜って動かすよ
    private fun animateTranslation(img: ImageView) {
        val objectAnimator = ObjectAnimator.ofFloat(img, "translationX", 1000f)
        objectAnimator.duration = 2000
        objectAnimator.repeatCount = -1
        objectAnimator.start()
    }

https://i.gyazo.com/61e1d2b2bd6ab0f7b6cd2690d5d082f7.gif

translationY

// Y軸ににふぃ〜って動かすよ
    private fun animateTranslationY(img: ImageView) {
        val objectAnimator = ObjectAnimator.ofFloat(img, "translationY", 1000f)
        objectAnimator.duration = 2000
        objectAnimator.repeatCount = -1
        objectAnimator.start()
    }

https://i.gyazo.com/700deeef2ff0fc56668c9773f23b2e97.gif

同じImageViewに対して、上記2つを適用してあげたら斜め移動も可能ですね!


rotaion

//  Z軸周りに回転させるよ
    private fun animateRotation(img: ImageView) {
        val objectAnimator = ObjectAnimator.ofFloat(img, "rotation", 1000f)
        objectAnimator.duration = 2000
        objectAnimator.repeatCount = -1
        objectAnimator.start()
    }

https://i.gyazo.com/ed1ec05d12599203e4b420c26ee3c7bc.gif




rotaionX

//  X軸周りに回転させるよ
    private fun animateRotationX(img: ImageView) {
        val objectAnimator = ObjectAnimator.ofFloat(img, "rotationX", 1000f)
        objectAnimator.duration = 2000
        objectAnimator.repeatCount = -1
        objectAnimator.start()
    }

https://i.gyazo.com/a7dca8ab7c357ee906118fa451808a35.gif


rotaionY

//  Y軸周りに回転させるよ
    private fun animateRotationY(img: ImageView) {
        val objectAnimator = ObjectAnimator.ofFloat(img, "rotationY", 1000f)
        objectAnimator.duration = 2000
        objectAnimator.repeatCount = -1
        objectAnimator.start()
    }

https://i.gyazo.com/e77afa02c89405cfc997df2e78311da1.gif


scaleX

//  X軸に拡大させるよ
    private fun animateRotationY(img: ImageView) {
        val objectAnimator = ObjectAnimator.ofFloat(img, "rotationY", 2f)
        objectAnimator.duration = 2000
        objectAnimator.repeatCount = -1
        objectAnimator.start()
    }

https://i.gyazo.com/26d1e4a8c8ed0ad08e62c7c025c4f889.gif


scaleY

// Y方向に拡大
    private fun animateScaleY(img: ImageView) {
        val objectAnimator = ObjectAnimator.ofFloat(img, "scaleY", 2f)
        objectAnimator.duration = 2000
        objectAnimator.repeatCount = -1
        objectAnimator.start()
    }

https://i.gyazo.com/d2834f58c48a9000efbb123180b46726.gif

alpha

// 透過させるよ
    private fun animateAlpha(img: ImageView) {
        val objectAnimator = ObjectAnimator.ofFloat(img, "alpha", 1f, 0f)
        objectAnimator.duration = 2000
        objectAnimator.repeatCount = -1
        objectAnimator.start()
    }

https://i.gyazo.com/af833a3c4e6eae79136b8de789ccf7b9.gif


AnimatorSet
複数のアニメーションを同時に動かしたいときにグルーピングして扱ってくれます
今回僕はデータバインディングを使っているので気をつけてください!

class MainActivity : AppCompatActivity() {

    lateinit var mBinding: ActivityMainBinding
    private var isReversed: Boolean? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        mBinding = DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main)

        mBinding.card.tag = false
        mBinding.card.setOnClickListener {
            reverseCard(mBinding.card, 1.0f, 0.0f, 1.0f, 1.0f)
            isReversed = true
        }
    }

    private fun reverseCard(img: ImageView, vararg rotate: Float) {
        val scaleX = ObjectAnimator.ofFloat(img, "scaleX", rotate[0], rotate[1])
        val scaleY = ObjectAnimator.ofFloat(img, "scaleY", rotate[2], rotate[3])
        val animatorSet = AnimatorSet()
        animatorSet.interpolator = LinearInterpolator()
        animatorSet.playTogether(scaleX, scaleY)
        animatorSet.duration = 300
        animatorSet.addListener(object : Animator.AnimatorListener {
            override fun onAnimationEnd(p0: Animator?) {
                if (!isReversed!!) return

                val isChanged = img.tag as Boolean
                if (!isChanged) {
                    img.setImageResource(R.drawable.x02)
                } else {
                    img.setImageResource(R.drawable.z01)
                }
                img.tag = !isChanged
                reverseCard(img, 0.0f, 1.0f, 1.0f, 1.0f)
                isReversed = false
            }
            override fun onAnimationRepeat(p0: Animator?) {}
            override fun onAnimationCancel(p0: Animator?) {}
            override fun onAnimationStart(p0: Animator?) {}
        })
        animatorSet.start()
        isReversed = false
    }
}

https://i.gyazo.com/eb0fa8a9f02966930a0376c7af42408f.gif

これだけでトランプ系のアプリが作れる可能性を感じますね!!!
ただ、scaleだと躍動感感じないのでrotateをうまく使った方がいいかもなので、また考えてみます〜〜!

今回は省略しますが、他にもキーフレームとかも弄れたりするので、ほんとにこれだけでアニメーションマスターになれそうな勢いですな〜!?

実はxmlでも書けるけど、複雑な処理はコードのが楽だと思うよ!

View Animation

  • なんと、、、Androidのバージョンに制限がない!!どこでも使えるすごい
  • 簡単に使える
  • さっきのPropertyAnimationを使う程こだわらない!って場合はこれでささっとつくっちゃいましょう〜!

こっちは手軽にアニメーションを〜って感じなので、xmlで書く方が推奨っぽい!

初めにアニメーション専用のanimフォルダーをres配下に作りましょう!
animフォルダーを右クリックしたら不思議ですねー!
NewのところにAnimation resource fileという項目があるので、そこで作りましょうー!

こんな感じでトップルートはsetタグで始まり、アニメーションさせたい種類のタグをネストさせて、値を設定してあげるだけでおっけーです!

<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:shareInterpolator="false">
    <rotate xmlns:android="http://schemas.android.com/apk/res/android"
        android:interpolator="@android:anim/decelerate_interpolator"
        android:fromDegrees="0.0"
        android:toDegrees="360.0"
        android:pivotX="322"
        android:pivotY="322"
        android:fillAfter="true"
        android:duration="1000"
        android:repeatCount="infinite"/>
</set>


な、なんて楽ちんだ.......
今回は回転アニメーションで、0から360度をx軸y軸共に322の位置を軸に1秒かけて永遠に回るって感じです!

使うタグも少なくてシンプル

  • alpha ->透明度
  • scale -> 拡大or縮小
  • translate -> 位置変更
  • rotate -> 回転
  • interpolator -> 緩急をつけてくれる
  • set -> グルーピング

じゃあ、xmlファイルができたら後はviewに対して適用してあげるだけです!
今回僕はデータバインディングを使っているので気をつけてください!

val anim1 = AnimationUtils.loadAnimation(this, R.anim.set_animate)
mBinding.imageView1.startAnimation(anim1)

https://i.gyazo.com/a098589eb7345c9d52ece506c8dbba1f.gif

うおお〜〜
すごくシンプルにできましたね!

Drawable Animation

これはdrawableリソースを次々と表示する機能です!
パラパラ漫画なんだなーと思えばよいかと!
今度はdrawable配下にルートタグanimation-listでリソースファイルを作ることに注意してください!animフォルダじゃないですよ!

れっつごー

<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
    android:oneshot="false"
    android:visible="true">
    <item
        android:drawable="@drawable/gumi_free"
        android:duration="50" />
    <item
        android:drawable="@drawable/gumi_free1"
        android:duration="50" />
    <item
        android:drawable="@drawable/gumi_free2"
        android:duration="50" />
    <item
        android:drawable="@drawable/gumi_free3"
        android:duration="50" />
</animation-list>

アニメーションリソースはgumiちゃんを90度ずつ回転させた画像を4つ用意しました!
ここで見覚えがないのは、oneshotでしょうか?
android:oneshotはfalseならリピートでtrueなら1回のみで終わります!

mBinding.imageView1.setBackgroundResource(R.drawable.drawable_animation)
val frameAnimation = mBinding.imageView1.background as AnimationDrawable
frameAnimation.start()

https://i.gyazo.com/f57404fa4be6a8d69daaf0b420d9d3f2.gif

うむ.....いい感じにくるくるしてるwww
(これは個人的に使う機会なさそうだなぁ)

Physics-based Animations

  • 上記の3つよりも一番新しい!!(2017年3月に追加されたっぽい)
  • 今までのアニメーションってやっぱり現実離れしてるよねー
  • これは物理ベースを盛り込んでいるから、より自然に見せれる!(つよい)

これは少し難しかったので、もう少し勉強しながら更新していこうと思います...(申し訳)

これに関してはここを見て、こんな感じのものなんだなーということさえ、掴んでいただければと...!!!
qiita.com


Lotties

さて、今までざっとアニメーションを色々見てきたわけですが、なんか違うんですよね。。。

僕が求めていたものと違う...!!!!!


うーむとなりながらも色々探し、ようやく見つけました!
airbnb.design

  • airbnbの神ライブラリ
  • After Effectsで作成したアニメーションをjsonに変換して読み込む
  • 誰でもハイクオリティなアニメーション再現が可能(らしい)
  • Android, iOS, React Nativeに対応している

これは楽しみ...!!!
れつごー

導入

dependencies {  
  compile 'com.airbnb.android:lottie:2.3.0'
}

main配下にassetsフォルダーを作成して、そこにjsonファイルをぶち込む
ちなみにアニメーションのjsonファイルの作り方はAfter Effectsにbodymovinというものを導入して、うんやかんやせなあかんらしい...(わからん)

でも大丈夫!!!!!
www.lottiefiles.com

色んなアニメーションがjsonで配られている専用サイトがありました!!!!
ここで試してみよう

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">
    <RelativeLayout
        android:id="@+id/activity_main"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context="com.gumio_inf.lottiesample.MainActivity">
        <com.airbnb.lottie.LottieAnimationView
            android:id="@+id/animation_view"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:lottie_fileName="ImThirsty.json"
            app:lottie_loop="true"
            app:lottie_autoPlay="true" />
    </RelativeLayout>
</layout>

コード何も書いてないです
xmlのapp:lottie_fileNameのとこにjsonファイル指定しただけです

実行してみましょう!

https://i.gyazo.com/6f69beb60e046b1b1310cc1c0be60b26.gif

ぎゃあああああああああああああああああああ

これです!!!!!この滑らかにアイコンがウニョウニョするのを求めていた!!!!

普通にImageViewと同じような感じで使えるっぽい(すごい)

でも配布されているやつじゃなくて、自分で何か作りたい....
そんな人はbodymovin頑張ってください..(学習コスト高そう)


と思っているときに何やら気になる話が...github.com

これは。。。。
SVGアニメーションアイコンを作ることが可能っぽい...??

つい先月くらいに日本の方が海外のカンファレンスに行っている時に反応していたのを見かけた情報です
まだまだ新しい情報で使い方が全くわからん...と思ったら

qiita.com

最高かよ!!!!!!!!!

つい昨日じゃないですか!
また近々、Physics Based Animationと共に触ってみようと思います


最近のアニメーションはしゅごい....(小並感)

Androidアプリ開発はじめませんか?[ その2 ] ~ 基本を知ろう!カウントアプリ編 ~

しばらくぶりです!!!!!!

 

こんなスタートでいいのやろうか。

いいんです。

 

これからはちまちまと更新頑張っていこうと思います

 

前回の記事でHello Worldアプリはできましたでしょうか?

まだの方はここからね

 

gumiossan.hatenablog.com

 

 

さて、今日はカウントアプリを作ってみましょう

こんなの↓↓↓↓

 

f:id:qurangumio:20170513064756g:plain

 

Androidは色んなコンポーネントを組み合わせることで、アプリが出来上がります。

 

コンポーネントってなんや?

ほんとなんなんでしょうね...

 

コンポーネントにも色々あるので、今はわからなくて大丈夫です。

 

そのコンポーネントの中でも基本となるアクティビティを知ることが、Androidアプリ開発の第一歩です!!!

 

アクティビティってなんじゃ〜〜〜???

 

ざっくりと、1枚の画面を表現する板的な

 

その板(アクティビティ)の上に文字を乗せたり、ボタンを乗せたり...etc.

色々なパーツを乗せていくことで、1つの画面の作っていきます

ボタンを押した時に画面が変わったりしますね?

その時は全く別のパーツが置かれたアクティビティに変わってます

複数の画面を作れば、その数だけアクティビティが増えていきます

 

いいですか?

 

うっすーーーい板です

 

でわ、いきましょう!!

 

AndroidStudioで新しくプロジェクトを作りましょうー!

 

アプリ名はCountとかでいいですかねー

Company Domainは他の方と被らないユニークなものにしないとだめなんですよねー

アプリ公開をしようとしている方なんかは、よく考えた方がよいです!

自分のドメインなんかを取得しておくと安心できると思います

foo.bar.gumio.com

こんな感じですね

 

公開しない練習用とかだったら、なんでもいいと思ってます

なので、今回はexample.com

 

最初の練習とかで設定するのはここだけですねー

後は、ひたすらEnter!!!!!

f:id:qurangumio:20170513040729p:plain

 

特に何もせずに、NextNextNext....
EnterEnterEnterEnter!!!!!!

 

f:id:qurangumio:20170513035543p:plain

 

NextNextNext....
EnterEnterEnterEnter!!!!!!

 

f:id:qurangumio:20170513035551p:plain

 

NextNextNext....
EnterEnterEnterEnter!!!!!!

 

f:id:qurangumio:20170513035555p:plain

 

 

はい。

 

 

最初の表示されている画面を見ていきましょう!!

 

f:id:qurangumio:20170513041218p:plain

 

うんうん。左には色々あるね

何が何だかさっぱりって感じ

 

今はざっくりで大丈夫です

慣れたら自然と理解していくもんなんで

とりあえず手を動かして行くことが大事です!!

 

最初に意識してほしいのは

MainActivity.javaとactivity_main.xmlです

 

こことここですねー

f:id:qurangumio:20170513041811g:plain

 

はじめに画面作っちゃいましょうか

 

layoutのactivity_main.xmlへGo

 

f:id:qurangumio:20170513041941p:plain

 

こんな画面になりましたか?

これはビューというものです

ビューはだと思ってください

この紙(ビュー)に色々なパーツを貼り付けていきます

 

そして覚えてますか?

アクティビティは板だと

 

このパーツを貼り付けていって、完成した紙を板に貼り付けます!!

そうすることで画面は初めて完成します

貼り付けるまではただの紙ですね〜〜〜

 

でわ、いきましょう!

 

左にずらーとたくさん英単語が並んでますね...

これらが画面を構成していくパーツ...ウィジェットといいます

 

最初に何が必要なのかをよく考えましょう

 

ボタンを押したら、1増える...

ボタン(Button)が必要ですね

そしたら、1増えたのを確認するための文字(TextView)も必要ですね

 

デザインは皆さんの自由です!

具体的なデザインの仕方は今後説明していくので、今はドラッグアンドドロップでぺたぺた貼りつけましょうー!

最初から存在するHello Worldは消しちゃいましょうか...

f:id:qurangumio:20170513043231g:plain

 

 

できましたか??

 

でわ、各詳細を見ていきましょう!!

画面上のTextViewを押してみましょう

右がこんな感じになってますね?

 

 

f:id:qurangumio:20170513043523p:plain

 

英語だらけで、うっ....ってなるかもしれませんが、それも勉強です

僕も未だに英語はさっぱりですが、なんとなくでやってたらなんとなくわかるようになります!!

なので、気合でなんとかしましょう!!

 

IDは名前みたいなものですねー

今だとtextViewちゃんになりますね!!

名前はそれを表す分かりやすいものにしてあげましょう

今回だと数字を表すテキストなので、、、

numTextViewとかにしてみましょうか

 

layout_widthとlayout_height

これは横幅と高さですね!!

widthが横幅で、heightが高さです

エストと身長みたいなもんですよ..えぇ((((

 

この右にある下三角を押すと

wrap_contentとmatch_parentが出てきます

気になったら、押してみたらいいんですよ

プログラミングのいいとこって、いくらでも失敗ができることなんですよ

いや、いざ誰かに使ってもらう時はだめですよ???練習のときですからね???

なので、好きなだけ暇な時にこれなんだろうってやつは触ってみるといいです

その分、賢くなると思いますよ

 

とまぁ、脱線しましたが伝えたかったことなので、、、

wrap_contentは中身のコンテンツに合わせてぴったりフィットしてくれるやつです。

 

Why???

 

今回でいう中身のコンテンツとは、TextViewだと文字そのもので、ボタンだったら、周りの枠ではなくて、中の文字ですね

 

よくわからないなーという方は、下に書いてあるView all propertiesを押してみましょう

そこを押すと、そのパーツに対して細かいとこまで設定できるようになります。

たくさんありますね~~~

その中の1つのtextSizeを80くらいにしてみましょう

文字の大きさが変わるとwrap_contentは柔軟にびよーんと大きさを合わせてくれます!なんと便利な

 

まぁ、これは各自でやってみてくださいな

 

ボタンも文字が中のコンテンツなので、textSizeを変えてみると同じことになりますよ

 

もう一つのmatch_parentは親のサイズにぴったり合わせる感じです

んー、よくわかんないなー

ある程度の大きさのスライムを四角形の箱にぶち込んだとき、スライムはその箱の大きさまで広がりますね?

そんなイメージです(わからない)

じゃあ、何が親なんだ??となりますね

 

ウィジェットがずらーと並んでる左下の画面を見てみましょう

f:id:qurangumio:20170513045239p:plain

 

ほうほう、なんかいるなと

これが親です

後々、詳細は説明していきます

これが親なんだなと思ってもらえたらいいです

今はこの親は画面いっぱいまで広がっています

 

なので、試しにmatch_parentにすると画面いっぱいにびよーんと広がりますね

 

場合によって、使い分けてみてくださいな

 

つぎ!!

f:id:qurangumio:20170513045547p:plain

 

textはtextです...

何を表示したいかですね!

今回は数字を表したいので、まだ数えられていない0にでもしてみましょうか!!!

 

f:id:qurangumio:20170513045714p:plain

 

うわ!!!ちっさ!!!!!

 

見づらいですよね。

さっき言ったようにtextSizeで大きくしてあげるとよいですね

90くらいしてあげましょうか

 

f:id:qurangumio:20170513045847p:plain

 

見やすい見やすい!!

 

textViewの設定はこんなんでよいです!

後のは必要になったときに随時説明しますので

 

つぎボタン!!

 

f:id:qurangumio:20170513050005p:plain

 

idはcountUpButtonとかがよいですかね?

layout_widthとheightは基本はwrap_contentでいきましょう!

 

ボタンで大事なのはonClickというやつです

これはなんなのか?

このボタンを押したときに僕は何をすればよいのか???

その何をするのかはプログラミングコードで書いていきます

ここはデザインを決める場所なので、そこまで詳細には決めてあげれません...

なので、ここではボタンを押した時の動作名を決めてあげましょう

ゲームとかだったら、攻撃するならattackですし、魔法ならspellとかですかね?

今回は数を数えていくので、countUpですかね

できましたか?

なんか後ろの灰色が気になるなぁ、、、という方は、backgroundをいじってあげましょう。

これは背景でそのままですね

この右の・・・を押すと

f:id:qurangumio:20170513050924p:plain

 

こんな感じになりますね?

左のColorとというとこを押して、好きな色に変えてみましょう!

僕は背景と同化をさせたいので、白にします

 

ついでにこれも小さいので、textSizeも大きくしましょう

いかがでしょう?

こんな感じになりました?

 

f:id:qurangumio:20170513051236p:plain

 

 

これでデザインは出来上がりました!!!

やったね!!

 

でわ少し休憩してから、実際にコードを書いてこの画面に作ったビューを貼り付けて動作を与えていきましょう!

 

次はjavaのMainActivityにいきましょう!

f:id:qurangumio:20170513051516p:plain

 

ひょえ〜

びびることはないです

最初はみんなひょえ〜〜ってなって、頑張ってできるようになるのです

すべての説明をしてたら、日が暮れちゃうので、、、詳細な説明は別の回で!!

今はとりあえずできることを目指しましょう!

最初は丸暗記でも構いません

こうやって使うんだなーってなれば充分です

 

アプリを起動!ってした時に、このMainActivityが動き始めます

そのときに最初に動く入り口となるのが、onCreateです

 

こいつは動き始めたとき、つまり画面が作られたときにやらなきゃならないことをやってくれています

初めに初期化しておきたいなーってことはここに書いておくといいかもですね!!

 

そのonCreateの中に書かれている


setContentView(R.layout.activity_main)

ここでさっき作ったビューを板に貼り付けているということです

これで作ったビューを貼り付けてあげないとアクティビティはただの板のまんまです

 

でも、このままじゃ貼り付けただけに過ぎません...

アクティビティは誰が誰なのかを名前がわかりません

困りました

なので、誰がどの子なのかをIDを教えてあげましょう

 

f:id:qurangumio:20170513053220p:plain

 

最初にTextView mNumTextViewと書かれたとこに使いたい変数を書いちゃいましょう!

変数ってなんだ???

変数はプログラミングする上でのコード上の箱みたいなものです

ゲームソフトをあちこちにばらまいてたらやばいですよね??

どこになにがあるのか探さないとだめです

ですが、ダンボールとか何か箱にPS4ソフトという張り紙をして、その中に入れておけば、探す手間が省けてよいです!!

なので、ここでもさっきビューでつけてあげたnumTextViewちゃんをmNumTextViewという箱にいれてあげましょう。

 

その前にまずは箱から作りましょう

これを変数宣言といいます

 

変数宣言のしかた

 

型名  変数名;

 

この型が大事です

ビューでのウィジェットと同じ型を書いてあげましょう

TextViewならTextView

ButtonならButtonという風に

変数名は箱の名前なので、何が入っているか分かりやすいものを入れてあげましょう

今回はmNumTextViewです

 

また、Javaは文末にセミコロン;をつけてあげないとだめです

僕達が文章に。をつけるのと同じですね。

 

でもまだ、箱ができただけです。

この箱に = を使っていれてあげましょう

数学では = は同じという意味です

プログラミングでは、これは代入という意味になります

ここは少しややこしいですね

代入はその箱にものを入れてあげることだと思いましょう!

 

例えば、ps4FF15という箱にファイナルファンタジー15をいれてあげましょう

 

ps4FF15 = ファイナルファンタジー15

 

簡単ですね?

 

数学と間違えないように数字でも考えてみましょう

xという箱に10をいれてみましょう

 

x = 10

 

今、xには10が入っています

もう一度同じことをしてみましょう

 

x = 10

 

確かにxには10が入っているから、イコールで等しいとなるのが数学です

ですが、プログラミングでは代入なので、xにはもう一回10が入るので、xに入っているは10です

前に入っていたのは消えてしまいます(*注意)

この辺りはしっかり勉強して慣れていきましょう!!!

 

でわ、onCreateの初期化のとこで、さっき作ってあげた箱の中にビューのnumTextViewちゃんを代入してみましょう

 

mNumTextView = (TextView) findViewById(R.id.numTextView) 

 

できましたか?

まず、findViewById(R.id.xxxxxx)という文で、先程ビューで名前をつけてあげたパーツを探してきます。

xxxxxというとこにつけてあげた名前を書きましょう

numTextViewでしたね

 

これで箱に入れれた!!とはならないのです。

残念なことにfindViewByIdで取ってきたパーツはすべてViewという箱に一括りになっています。View型です

なので、こいつは誰なのかということをアクティビティに教えてあげましょう。

誰なのかを教えてあげることを型変換 = キャストといいます。

その書き方は

 

(型名) findViewById(R.id.xxxxx) 

 

この型名が同じじゃないと箱には入らないということを覚えてください

 

PS4WiiUのソフトやハムとかCDを入れてもなにもならないですね?

それと同じです

ちゃんと合ったものを入れてあげてください

 

ここでデフォルトでJavaに用意された最低限の型を紹介します

 

整数を扱いたいなら、intという箱

文字を扱いたいなら、Stringという箱

小数点を扱いたいなら、doubleという箱

 

大文字小文字に気をつけてくださいね

 

例えで、型宣言から代入を一気にやってみます

 

int i = 100

String android = "Android"

double d = 3.14

 

どうでしょうか?

文字だけはダブルクォーテーション""ではさんであげないとだめなので、気をつけてください

 

ここまでで無事にTextViewを入れるmNumTextViewという箱にビューのnumTextViewというパーツを入れることができました

 

つまりコードで自由にいじることができるということです

まるで神様になった気分ですね??

 

次はボタンを押したときの動作を書きましょう

public void onClick名(View v) {

     ....................................

}

 

こんな感じです

さっきボタンでonClickにボタンの動作名を書きましょうと言いました。

ここでその動作にどういうことをしてほしいかを書くためだったんですねー

なので、ここにはcountUpと書きましょう。

今は文法の説明はしません。

覚えることが多すぎても頭がパンクするだけなので、ゆっくりいきましょう。

 

かけましたか?

でわ、ここで問題を考えてみましょう

 

数を数えるためには何が必要ですか?

 

考えてから、下に行きましょう

ヒント:数を入れるための箱と初期化

 

 

 

 

 

 

 

 

f:id:qurangumio:20170513060647p:plain

 

 

どうでしょうか?

整数をいれたいので、int型の数える数字を入れたいので、countNumという名前の箱を作ってみました。

そして、それを初期化するためにonCreateの中で0を代入しています。

 

ここまで分かっていたら500点です!!自信もってよいですよよよよ

分からなかった人も大丈夫です

わからなくて普通なのですから、、、

わからないなりにしっかり読んで手を動かして、学んでいきましょう!!

 

でわ、次は少し難しい問題です

countUpはボタンが押されたら、動き出します

ボタンを押したら、数を数えていきたいですね?

そのためにはcountNumには何を代入したらいいでしょうか?

ヒント:足し算

 

 

 

 

 

 

 

 

 

 

 

f:id:qurangumio:20170513061207p:plain

 

どうですか?

ボタンを押したら、数を数えたいので1を足します

何に対して足すか?countNumですね

 

countNumには0が入ってますね?

0に1を足して、1をcountNumに代入します

またボタンを押したら、1に1を足して2を新しくcountNumに代入します

 

ここでアプリを起動させて、ボタンを押してみましょう!!!

 

 

................

..........................

................................

 

まぁ何も起こらないですよ

 

数を数えただけにしか過ぎないので,,,,,はい

 

数を数えたら、それをnumTextViewちゃんにセットしてあげないとだめですね!!

セットしてあげないといつまでたってもnumTextViewは0のまんまですよ....

かわいそうですね。

 

なので、セットしてあげましょう!!

ここで注意したいのは、セットの仕方です

代入したらいいんじゃないの?と思って、既に

 

mNumTextView = countNum

 

と書いた方は、、、、

もうそれは

f:id:qurangumio:20170513062024j:plain

 

だめウーマン!!!

 

言いたかっただけです。。はい

 

少し考えてみましょう

numTextViewはTextViewという型ですね?

countNumはIntという型ですね?

そもそも型違いなので、それこそPS4にハム入れてるようなものです

 

じゃあ、キャストして

 

numTextView = (TextView) countNumにしたらいいんじゃない?

 

それもだめです!!!!

 

さっきのViewはTextViewになれたからよかったんです!

セミの幼虫がいずれセミになるから、セミの虫かごにまとめて入れちゃうのと同じです!これは許されます

 

ですが、PS4にフリスビーを無理やり入れようとしても無理ですね?

だって無理じゃん、、、、うん、、、わかって、、おねがい (((((

 

 

じゃあどうするんだと!!!

セットの仕方はちゃんと用意しております

我々は賢いので

f:id:qurangumio:20170513062718j:plain

 

 

f:id:qurangumio:20170513063021p:plain

 

おぉ。。なんだこれ....

大丈夫です

そのうちわかるようになります

今はこんな風に書くんだな~程度で

 

mNumTextView つまりTextViewには潜在能力的なのがあります

僕たちも生まれつき手を動かしたり、目をあけて前のものをみたりできますね?そんな感じです

TextViewも文字を表現するために生まれました

じゃあ、文字を書き換えたりする力があってもいいじゃないの!!

 

それがsetTextです

 

更にこれはメソッドといいます。

さらにさらにこれはインスタンスメソッドといいます

今はスルーしてください

 

そのメソッドの呼び方だけ覚えておきましょう!

その型の潜在能力を引き出すには、ドット.を使いましょう

そしたらこの先、わからない型に出会っても、こいつは何ができるのかなーってなったときにドットを打ったら、候補がずらーと出てきます

ある程度英語できないと意味は読み取れないかもしれませんが、、、それでもsetTextくらいは雰囲気でわかってください!!

分からなかったら、調べたらよいのですよおおおおお!!!

 

てことで

mNumTextView.setTextな

 

このかっこの中で行っている

String.valueOf(countNum)

 

おお?

なんか見覚えが、、

String(型)にドットが

じゃあ、valueOfもメソッドか〜〜

そう!!!

 

まあ、そもそもStringの箱用意してねえよとか

IntをStringにしても、TextView型と一致しねえじゃんかとk

色々言いたいことは分かりますよ、、ええ

 

否!!!!!!!!

 

そんなのまた今度だ!!!

とりあえず完成したんだから、起動させて、やったーぁぁあっぁぁっぁぁぁうごいたっぁぁっぁぁ!!!!!ポチポチポチポチ!!!!って1000000回くらいカウントしとけぃ!!!!!!!!

 

......

 

f:id:qurangumio:20170513064733p:plain

 

.....

 

どうですか?

初めて自分でアプリを、動くものを作ってみた感じは

楽しいと思いましたか?

地味だーとかもっとLineみたいなの作りたいーとかなると思う人もいると思います

プログラミングって小さいことの積み重ねなんです

このカウントアプリにはアプリ開発の基礎がたくさんつまっているので、よく読んでほしいと思います

小さいことをたくさんこなして、それをどんどん組み合わせて、大きなものを作れるようになっていきます

なので、修行だと思って、自分の好きなアプリが作れるようになってなると思って頑張ってみてください

 

 

 

バイバイっっ!!!!!!!!!!