もやもやエンジニア

IT系のネタで思ったことや技術系のネタを備忘録的に綴っていきます。フロント率高め。

名言 大事なことはFateから教わった

このエントリーは 名言Advent Calendar 2015の16日目のエントリです。

www.adventar.org

名言

貴様は臣下を救うばかりで導く事をしなかった
王の欲の形を示す事もなく、道を見失った臣下を捨て置き、
ただ一人ですまし顔のまま小奇麗な理想とやらを思い焦がれていただけよ
王とは、誰よりも鮮烈に生き、もろ人を見せる姿を指す言葉
全ての勇者の羨望を束ね、その道標として立つ者こそが王!
故に、王とは孤高にあらず
  • Fate/Zeroの聖杯問答からですね。知らない人置いてきぼりな感じです。僕はFate好きすぎるw
  • このシーンは3人の王が王の在り方をそれぞれ語り合うシーンですが、内容は仕事にも通じるところがあるなーと思ってたりします。プロダクトが目指す未来、目指す世界を共有して、そこについて行きたい!と思わせるような人と仕事したいですね。
  • 観たことない人は今ならNetfrixで配信されている(らしい)ので、正月休みにでも観るとよろし。

Fate/Zero-アニメ公式サイト-

Androidのデザインパターンを考えてみたの続き。Kotlin対応版。

一番最新のやつはこっち

rei19.hatenablog.com

前回はこちら

rei19.hatenablog.com

  • 前のプロジェクトで開発していたアプリをリリースしてからはAndroidから離れていたのですが、会社変えてまた触ることになったので思い出す意味でも再考します。
  • 前回は出来るだけ純正のAPIだけで考えたのですが、今回はライブラリも使いながらもう少し手を入れてみたり。

今一度ポイント

  • 基本的にModelは死なない + 通知で変更を伝える役割を持つ
  • ViewやVCでAPIを呼ぶのはユーザーの操作に影響されやすいからやめれ

変えたところ

  • Android Best Practiceを参考にパッケージ構成を整理

github.com

  • 以下のライブラリ群をGradleに追加。AsyncTaskLoader周りはokHttpとRxを使い、Observer周りはottoのEventBusを使って通知のやり取りをする。

    • okHttp
    • otto
    • rxJava
    • rxAndroid
  • こちらも参考に。たぶん前の投稿のスライドと同じ所属の方の作。ありがたや。

github.com

  • 今、Kotlin楽しい感じなので、Kotlinで書きなおしてます。考え方は変わらないのでJavaに置き換えて読むとよいかと。

パッケージ構成

パッケージ名 役割
entities ドメインで扱うデータクラスを配置
events EventBusを通して発火するイベントクラスを配置
fragments Fragmentを配置
managers 多様なデータを扱うコントローラを配置
models ドメインモデルを配置
network 外部との通信が発生するモジュールを配置
utils ユーティリティー群
views Viewに関連するモジュールを配置

コード

  • managers/ModelLocator
  • こちらは先程のandroid-mvc-sampleをKotlinで焼き直し
package me.rei_m.kotlinmvcsample.managers

import java.util.HashMap

/**
 * 各Modelのインスタンスを管理する
 */
public class ModelLocator private constructor() {

    companion object {
        private val showcase = HashMap<Tag, Any>();

        public enum class Tag {
            ATND,
        }

        public fun register(tag: Tag, model: Any): Unit {
            showcase.put(tag, model)
        }

        public fun get(tag: Tag): Any = showcase[tag]!!
    }
}
  • アプリ起動時にAtndApiのモデルをセットしておく
        // ModelLocatorにModelの参照を登録
        ModelLocator.register(ModelLocator.Companion.Tag.ATND, AtndModel());
  • entities/AtndEntity
  • Kotlinのデータクラスを使って作成
package me.rei_m.kotlinmvcsample.entities

import java.io.Serializable

/**
 * Atndのイベント情報を保持するデータクラス.
 */
public data class AtndEventEntity(val id: Int, val title: String) : Serializable
  • network/AtndApi
  • APIとの通信部分を作成。さんぷるなのでキーワードは固定。
package me.rei_m.kotlinmvcsample.network

import com.squareup.okhttp.CacheControl
import com.squareup.okhttp.HttpUrl
import com.squareup.okhttp.OkHttpClient
import com.squareup.okhttp.Request
import me.rei_m.kotlinmvcsample.entities.AtndEventEntity
import org.json.JSONObject
import rx.Observable
import java.net.HttpURLConnection

/**
 * Atnd Apiへの接続部分を定義する.
 */
public final class AtndApi private constructor() {

    companion object {

        /**
         * Atnd APIへのリクエストを行いAtndEventのEntityを返すObservableを作成する.
         */
        public fun request(): Observable<AtndEventEntity> {

            return Observable.create({ t ->

                // リクエストのURLを作成.
                val url = HttpUrl.Builder()
                        .scheme("https")
                        .host("api.atnd.org")
                        .addPathSegment("events")
                        .addQueryParameter("keyword_or", "google,cloud")
                        .addQueryParameter("format", "json")
                        .build()

                val request = Request.Builder()
                        .url(url)
                        .cacheControl(CacheControl.FORCE_NETWORK)
                        .build()

                // リクエスト開始.
                val response = OkHttpClient().newCall(request).execute()

                if (response.code() == HttpURLConnection.HTTP_OK) {
                    // 正常に取得できたら、パースしてonNextでObserverに流す.
                    val responseJson = JSONObject(response.body().string());
                    val eventCount = responseJson.getInt("results_returned")
                    if (0 < eventCount) {
                        val events = responseJson.getJSONArray("events")
                        for (i in 0..eventCount - 1) {
                            val e = events.getJSONObject(i).getJSONObject("event");
                            t.onNext(AtndEventEntity(e.getInt("event_id"), e.getString("title")))
                        }
                    }
                } else {
                    // エラーの場合はレスポンスコードをThrowableにつめて投げる
                    t.onError(Throwable(response.code().toString()))
                }

                t.onCompleted()
            })
        }
    }
}
  • models/AtndModel
package me.rei_m.kotlinmvcsample.models

import me.rei_m.kotlinmvcsample.entities.AtndEventEntity
import me.rei_m.kotlinmvcsample.events.AtndLoadedEvent
import me.rei_m.kotlinmvcsample.events.EventBusHolder
import me.rei_m.kotlinmvcsample.network.AtndApi
import rx.Observer
import rx.android.schedulers.AndroidSchedulers
import rx.schedulers.Schedulers
import java.util.*
import me.rei_m.kotlinmvcsample.events.AtndLoadedEvent.Companion.Type as EventType

/**
 * Atndに関する情報を管理するドメインモデル.
 */
public class AtndModel {

    /** API読込中など処理中か判定するフラグ */
    public var isBusy = false
        private set

    /** アテンドのイベント情報を保持するリスト */
    public val atndEventList = ArrayList<AtndEventEntity>()

    /**
     * AtndのAPIからイベント情報を取得し、内部に保持する.
     */
    public fun fetch() {

        // ビジー状態の場合は処理終了.
        if (isBusy) {
            return
        }

        // ビジー状態にする
        isBusy = true

        // APIリクエストのオブザーバーを作成
        val observer = object : Observer<AtndEventEntity> {
            override fun onNext(t: AtndEventEntity?) {
                // リストに追加する.
                atndEventList.add(t!!)
            }

            override fun onCompleted() {
                // onCompletedを受け取ったら完了のLoadedEventを発火する
                EventBusHolder.EVENT_BUS.post(AtndLoadedEvent(EventType.COMPLETE))
            }

            override fun onError(e: Throwable?) {
                // onErrorを受け取ったらエラーのLoadedEventを発火する
                EventBusHolder.EVENT_BUS.post(AtndLoadedEvent(EventType.ERROR))
            }
        }

        // AtndのAPIにリクエストを飛ばしてModelへの配信を開始する
        AtndApi.request()
                .onBackpressureBuffer()
                .subscribeOn(Schedulers.newThread())
                .observeOn(AndroidSchedulers.mainThread())
                .finallyDo({
                    // 必ずビジー状態を解除する.
                    isBusy = false
                })
                .subscribe(observer)
    }
}
  • fragments/AtndEventListFragment
package me.rei_m.kotlinmvcsample.fragments

import android.os.Bundle
import android.support.v4.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.AdapterView
import android.widget.ListView
import com.squareup.otto.Subscribe
import me.rei_m.kotlinmvcsample.R
import me.rei_m.kotlinmvcsample.entities.AtndEventEntity
import me.rei_m.kotlinmvcsample.events.AtndEventClickEvent
import me.rei_m.kotlinmvcsample.events.AtndLoadedEvent
import me.rei_m.kotlinmvcsample.events.EventBusHolder
import me.rei_m.kotlinmvcsample.managers.ModelLocator
import me.rei_m.kotlinmvcsample.models.AtndModel
import me.rei_m.kotlinmvcsample.views.adaptors.AtndEventListAdapter
import me.rei_m.kotlinmvcsample.events.AtndLoadedEvent.Companion.Type as EventType
import me.rei_m.kotlinmvcsample.managers.ModelLocator.Companion.Tag as ModelTag

/**
 * AtndEventをリスト表示するFragment.
 */
public class AtndEventListFragment : Fragment() {

    /** ListViewのアダプター */
    private var mListAdapter: AtndEventListAdapter? = null

    companion object {
        /**
         * ファクトリ.
         */
        fun newInstance(): AtndEventListFragment {
            return AtndEventListFragment()
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        mListAdapter = AtndEventListAdapter(activity, R.layout.list_item_atnd_event)
    }

    override fun onDestroy() {
        super.onDestroy()
        mListAdapter = null
    }

    override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? {

        val view = inflater!!.inflate(R.layout.fragment_atnd_event_list, container, false)

        val listView = view.findViewById(R.id.list_atnd_event) as ListView

        listView.onItemClickListener = AdapterView.OnItemClickListener { parent,
                                                                         view,
                                                                         position,
                                                                         id ->

            // リスト内の項目がクリックされたらクリックイベントを発火.
            val atndEventEntity = parent?.adapter?.getItem(position) as AtndEventEntity
            EventBusHolder.EVENT_BUS.post(AtndEventClickEvent(atndEventEntity))
        }

        // リストビューにアダプターをセット
        listView.adapter = mListAdapter

        return view
    }

    override fun onResume() {
        super.onResume()

        // EventBus登録
        EventBusHolder.EVENT_BUS.register(this);

        // AtndModelとリストビューに表示中のイベント数を取得
        val atndModel = ModelLocator.get(ModelTag.ATND) as AtndModel

        val displayedCount = mListAdapter?.count!!

        if (displayedCount != atndModel.atndEventList.size) {
            // 表示済の件数とModel内で保持している件数をチェックし、
            // 差分があれば未表示のイベントがあるのでリストに表示する
            mListAdapter?.clear()
            mListAdapter?.addAll(atndModel.atndEventList)
            mListAdapter?.notifyDataSetChanged()
        } else if (displayedCount === 0) {
            // 1件も表示していなければイベント情報を取得する
            atndModel.fetch()
        }
    }

    override fun onPause() {
        super.onPause()

        // EventBus登録解除
        EventBusHolder.EVENT_BUS.unregister(this);
    }

    @Subscribe
    @SuppressWarnings("unused")
    public fun onAtndEventLoaded(event: AtndLoadedEvent) {
        // ModelからLoad完了のイベントを受け取った時の処理
        when (event.type) {
            EventType.COMPLETE -> {
                // 読み込みが正常に完了したらModel内のイベント情報をアダプターにセットして再描画する
                val atndModel = ModelLocator.get(ModelTag.ATND) as AtndModel
                mListAdapter?.clear()
                mListAdapter?.addAll(atndModel.atndEventList)
                mListAdapter?.notifyDataSetChanged()
            }
            EventType.ERROR -> {
                // エラー表示する
            }
        }
    }
}

動かしてみる

f:id:Rei19:20151123103250p:plain

  • 今回のコードはこちらに

GitHub - rei-m/Kotlin_mvc_sample

  • Retrofitとか使うともっと短く書けるのかな。まだまだAndroid歴浅いのでなんとも修行足りない感。
  • ちゃんとテストを書かないとというところがまだ考えきれてないのでその辺もやらないとですね。Stateパターンとかも取り入れてみたら規模がでかくなったら幸せになる気も。しばらく触りそうなので色々試してみます。

自分用 MacBookセットアップメモ

転職して新たなMacBookが支給されたけど、セットアップするときにあれ何入れてたっけ?となったので自分用のメモ。(USキーボード向け)

英数/かな切り替え

  • Google IMEを入れる
  • Karabiner-Elementsを入れる
    • Complex ModificationsからJP向けのruleを入れてコマンドキーで切り替えるルールを有効にする

Homebrew入れる

shell

  • Login Shellをzshにしよう。ついでに下のどちらかを入れるとよい。

パッケージ管理

  • brewコマンドで必要なものを入れるようにしておくためにとりあえずぶっこむ。
  • homebrew
  • home-brew-cask

iTerm2

brew cask install iterm2
  • iTerm2でプロフィールを新しく作成してデフォルトにしておく。
  • プロフィールのWindowタブを開いて、styleを Top of ScreenにRowsを40にしておく。iTerm2を開くとモニター上段が表示領域になる

tmux

  • ターミナルを多重化するツール
brew install tmux
  • tmux plugin用にtpmを入れる

github.com

  • iTerm2のプロフィールのKeysタブを開いて追加する。Send Hex CodeでiTerm2からtmuxにキーを送れるようにする。
ショートカットキー HexCode 動作 tmux のコマンド
Ctrl Shift ] 0×2 0x6e 次のWindowに移動 Ctrl b n
Ctrl Shift [ 0x2 0x70 前のWindowに移動 Ctrl b p
Ctrl N 0x2 0x63 新しいWindowを開く Ctrl b c
Ctrl Shift % 0x2 0x25 Windowを左右分割 Ctrl b %
Ctrl ] 0×2 0x6f 次のPaneに移動 Ctrl b o

ほかいろいろ

brew install go
brew install peco
brew install ctags
brew install nodebrew
brew install rbenv
brew install elixir
brew cask install java
なんかほかあれば

エディタ

brew cask install atom
  • 設定は自分の記事参考

rei19.hatenablog.com

  • sync-setting使ったけど職場のAtomとの共有は失敗した。。。なぜだ。

dot files

だいたいおっけーなはず

  • 何かあったらこの日記に追加していく。

飛騨の古民家を借りて開発合宿に行ってきた

目的

  • 非日常な空間にこもって新しい技術に挑戦したり、作ろうと思っていたけど普段はなかなか時間が・・・というようなものを集中して作る
    • もともと身内でプライベートでも何か作ったりするのが好きな人達が集まってわいわい開発するイベントだったり。今回は2回目で前職のつながりで技術顧問のNaoyaさん含む6人で参戦。

場所

www.satoyama-office.com

  • 飛騨古川駅より徒歩8分で駅からのアクセス抜群。まあ飛騨までが遠いんですけど。
  • 宿泊代は1軒あたりの金額なので参加人数が多いほど一人あたりの金額が安くなります。なお、布団代は別で一人3000円かかりました。

スケジュール

## 11/7(土)
9:00 東京駅集合
9:32 東京駅発
| かがやき507
11:46 富山駅着 昼食取る
13:02 富山駅発
14:17 飛騨古川着 古民家チェックイン
15:00 飲食物買い出し。
15:30 スケジュール確認して開発開始
19:00頃 出前で夕飯
23:00頃 初日は軽く宴。聖地で氷菓観るw

## 11/8(日)

食事以外は基本開発

## 11/9(月)

朝食後開発追い込みと発表準備
10:50頃 成果発表LT。5分から10分
11:30頃 LT終了。チェックアウト。開発はここまで。

12:00 〜 高山の方までいって軽く観光して名古屋回りで帰還

やったこと

  • 最近Kotlinをちょいちょいつまんで触っているので、KotlinでHBFavのAndroid版を再現してみるみたいなことやってました。
  • 完成まではまだ遠いけど進捗 rei-m/HBFav_kotlin · GitHub

行ってみて

  • ちょっと遠いかなと思ったけど、風情ある1軒屋を貸しきって開発するというのはとてもよい時間でした。周りも静かで超集中できます。他人が気になるなら余ってる部屋に行けば一人になれたり。
  • 開発合宿は最後にLTやるの大事です。限られた時間でデモできるものを作ってLTのスライドも作って発表すると達成感がすごい。
  • 場所遠かったのでもう一日くらい使っても良かった感。また来年も行きたいですね!

一休を退職しました

ご報告

  • 10月いっぱいで退職しますという話をし、本日が最終出社でした。社内外の方にはお世話になりました。ありがとうございました。気がつけば入社してから6年。。。6年前だとガラケサイトの開発からはじまり、最近だとネイティブアプリの開発とかいろいろやったなあという感じです。
  • 最近の一休は技術顧問としてNaoyaさんがジョインしてから組織改善プロジェクトがはじまり、すごい勢いでいろいろなことが変わっていきました。といっても技術顧問は銀の弾丸ではないので大事なのは自分たちが課題を認識して自分たちで変わろうとする意思を持つことかなと思います。最初の頃から携わってた身としてはよい経験ができました。ありがとうございました。

参考

【前編】CTO不在で、開発組織改善に着手! 一休のエンジニアが語る苦悩の1年 / 飲み会で探るエンジニアのホンネ #naoya_sushi 編

【後編】CTO不在で、開発組織改善に着手! 一休のエンジニアが語る苦悩の1年 / 飲み会で探るエンジニアのホンネ #naoya_sushi 編

今後

  • 2週間ほど休んだ後、表参道の方のベンチャーでまたエンジニアとしてがりがり作ります。新卒でSIerに入ってエンジニアのキャリアがはじまり、次にWebサービス作りたいなーと思い一休に入って、今回で3社目ですね。テクノロジーの力で世の中に面白いものを提供できるようにがんばります。
  • という感じであっさりですが身近な方々へのご報告でした。業界狭いのでまた勉強会とかで顔をあわせるかと思いますが、変わらずお付き合いくださいな。

雑兵MeetUp #1で「REVEAL.jsとMilkcocoaで双方向LTする」というLTをしてきました

  • Twitterのタイムライン上でイベントをみかけて、あまり見ないコンセプトの勉強会で面白いなーと思ったので盛り上げる側で参加してきました。主催の @yodatomato さん、会場を貸してくれた 21cafeさんありがとうございました。ほかの参加者の方々もお疲れさまでした。

イベント

zohyo.connpass.com

まとめ

自分のLT資料

コメント

  • 外でLTするのは超久しぶりだったので案の定、時間内には微妙に収まりきらず、この辺は場数が必要だなという印象ですね。継続して何かネタを投下していきたいと思います。
  • 発表してるときに気づいたのですが、流れるコメントにz-indexつけ忘れてたっぽくてスライドの画像の後ろにコメントが回ってしまっていました。Github上のコードは直しています。
  • 今回はゆるふわなネタで発表しましたが、スライドで使ったMilkcocoaは国産のBaasとしてはめっちゃ簡単に導入できてアイデアも広がるので知らない方はぜひ触ってみてほしいです。npmでNode.js用のパッケージも提供されてるので、IOTにNodeが動かせる環境が乗っていれば簡単にIOTで集めたデータをPublishすることもできます。IOTの入門としてはハードルは低めではないでしょうか。
  • LT後にMilkcocoaの中の人からTシャツをもらいました! @bakuonboogie さんありがとうございました!!

f:id:Rei19:20151030142946j:plain

Android StudioとKotlinでAndroidアプリを作る

Kotlinとは

導入

  • Android Studioは同じJetBrains製品のInteliJベースで作られているだけあって、プラグインを入れるだけで簡単に導入可能。Preferences -> pluginから以下の2つをインストールして再起動するだけです。なお自分の環境はAndroid Studio 1.4、compileSdkVersionは23、minSdkVersionは16にしてます。
    • Kotlin
    • Kotlin Extensions For Android

プロジェクトを作ってJavaのコードをKotlinに変換する

  • 適当にBlankActivityだけ追加したプロジェクトを作ります。そうするとこんなActivityが出来ますね。
package me.rei_m.kotlinsample;

import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.View;
import android.view.Menu;
import android.view.MenuItem;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
                        .setAction("Action", null).show();
            }
        });
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }
}
  • プロジェクトツリーにappがフォーカスされている状態でメニューバーからcodeを選ぶと一番下にConvert Java File to Kotlin Fileが追加されていているのでこれを選択します。すると、Kotlinへのコンバートが開始されます。
  • コンバートが完了するとMainActivityはこのように生まれ変わり、拡張子も.ktになります。ハイライト効かないのでみづらいですね。。。早速、valが見えているあたり、Scalaに近い雰囲気を感じます。
package me.rei_m.kotlinsample

import android.os.Bundle
import android.support.design.widget.FloatingActionButton
import android.support.design.widget.Snackbar
import android.support.v7.app.AppCompatActivity
import android.support.v7.widget.Toolbar
import android.view.View
import android.view.Menu
import android.view.MenuItem

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val toolbar = findViewById(R.id.toolbar) as Toolbar
        setSupportActionBar(toolbar)

        val fab = findViewById(R.id.fab) as FloatingActionButton
        fab.setOnClickListener(object : View.OnClickListener {
            override fun onClick(view: View) {
                Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG).setAction("Action", null).show()
            }
        })
    }

    override fun onCreateOptionsMenu(menu: Menu): Boolean {
        // Inflate the menu; this adds items to the action bar if it is present.
        menuInflater.inflate(R.menu.menu_main, menu)
        return true
    }

    override fun onOptionsItemSelected(item: MenuItem): Boolean {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        val id = item.itemId

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true
        }

        return super.onOptionsItemSelected(item)
    }
}
  • 次にメニューのTools -> Kotlin -> Configure Kotlin Projectを選択してそのままOKを押します。

f:id:Rei19:20151017201814p:plain

  • するとbuild.gradleにKotlinでビルドするための設定が追加されるので、この状態でビルドをかけて実行します。

f:id:Rei19:20151017204542p:plain

  • 動きました!
  • 次にKotlin Extensionを使うための設定を追加します。build.gradleを開いてdependentciesにextensionsを追加します。
buildscript {
    ext.kotlin_version = '0.14.451'
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
        classpath "org.jetbrains.kotlin:kotlin-android-extensions:$kotlin_version" <- これを追加
    }
}
  • そしてMainActivity.ktを開いてimportを追加します。syntheticから後ろはxmlの名前です。
import kotlinx.android.synthetic.activity_main.*
import kotlinx.android.synthetic.content_main.*
  • これで何ができるようになるかというとfindViewByIdを使わなくてもViewのコンポーネントを参照できるようになります。content_main.xmlTextViewのテキストをActivityから書き換えてみましょう。参照するためにはIDが必要なのでxmlを開いて適当にTextViewにidを追加します。ここではhelloとしています。
  • MainActivity.ktに戻り、findViewByIdを使っているところを書き換えてみましょう。
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // val toolbar = findViewById(R.id.toolbar) as Toolbar
        // setSupportActionBar(toolbar)
        setSupportActionBar(this.toolbar)

        // val fab = findViewById(R.id.fab) as FloatingActionButton
        // fab.setOnClickListener(object : View.OnClickListener {
        this.fab.setOnClickListener(object : View.OnClickListener {
            override fun onClick(view: View) {
                Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG).setAction("Action", null).show()
            }
        })

        // TextViewを書き換える処理を追加
        this.hello.text = "Hello Kotlin !!"
    }
  • 今までfindViewByIdを使っていたところはimportしたことでthisから参照できるようになりました。わかりやすくthisを書いてますが、もちろん無くても動きます。これでビルドして動かしてみましょう。TextViewの表示が変わったはずです。

f:id:Rei19:20151017210329p:plain

  • まだReferenceを読んでいる途中ですが、Javaで書くよりだいぶ簡潔に書ける印象です。しばらく触ってみて既存の資産の使い方など確かめながら何か作ろうかと思います。

追記

  • 早速書き始めたらいきなりExtensionが原因でこけた。。。下のようにViewに追加するとクラスが見つからないと言われて死にます。
    <AppCompatButton
        android:id="@+id/open_hogehoge"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="なんか開く" />
  • AppCompatButtonをフルパスで指定したらビルド通りました。Support library系のコンポーネント使うときは注意しないといけないですねー
    <android.support.v7.widget.AppCompatButton
        android:id="@+id/open_list"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Open sample of listView" />