hogepiyoエンジニアリング

トラブルシューティングからノウハウ、作ってみた系まで扱う情報系ブログ

CoordinatorLayoutでAppBarLayoutと連動してスクロールするWebView

AndroidのUIでツールバーとコンテンツ領域が連動してツールバーが広がったり縮まったりするやつありますよね

Mastering the Coordinator Layout · Saúl Molinero

あれはCoordinatorLayoutというLayoutを使って実装されているんですが、コンテンツ領域のViewとして使えるものが限られています

例えばRecyclerViewとかNestedScrollViewとか、NestedScrollingChildインタフェースを実装しているクラスとなら一緒に使えます

ただ今回WebViewを使いたかったんですがWebViewではNestedScrollingChildが実装されていないので素のままだとCoordinatorLayoutと一緒に使えません

これについては同じことで困っている人も何人かいるようで、調べてる最中にtwitterとかstackoverflowとかで見かけました

stackoverflow.com

NestedScrollingChildの実装が結構難しいんですよね。。

NestedScrollViewのなかに入れることもやってみたんですが、思った動きにならなかったので結局WebViewを継承したクラスにNestedScrollingChildを実装しました

github.com

実装の都合上AppBarLayout側でも処理が必要だったのでAppBarLayoutを継承したNestedWebViewAppBarLayoutも一緒に使う必要があります

正直AppBarLayoutにも手を加えているのがちょっと微妙な気もしています ふーむ

RecyclerViewとNestedScrollViewの実装を見ながら雰囲気で実装したので変なところも多々あるかもしれません

あとAnrdoidの書き方的に変な部分もあるかもしれません

どんなPRでももらえると泣いて喜びます

issueだけでも歓迎です

ミーティングのすゝめ

普段ウェブサービスのエンジニアとして働いており、新機能の追加や改善などでミーティングをする機会が度々あります

そのなかでもっとこうすれば良いミーティングができるのではと感じることがいくつかあったので夜中の勢いでスライドにしました

スライドを作ってから現時点まで半年くらい経過していたようですが、せっかくなので公開しようと思います

www.slideshare.net

あくまで自分の観測範囲でのことなのであらゆる環境にあてはまることではないと思いますが、すこしでも良いミーティングが増えれば幸いです

Spring BootのJackson時刻設定まとめ

Spring Bootでオブジェクトをシリアライズするときにjava.util.Dateの変換を良きに計らってほしいときの設定

フォーマットとタイムゾーンの組み合わせで出力が変わってくるのでまとめてみた

下記のようなapplication.ymlに設定を書いていると想定

spring:
  jackson:
    dateFormat: yyyy-MM-dd'T'HH:mm:ss.SSS'Z'
    time-zone: Asia/Tokyo

dateFormatとtime-zoneの設定を変えていく

dateFormatが4パターンとtime-zoneが2パターンで計8パターンを試した

出力はすべて同時刻の値

dateFormat time-zone 出力
yyyy-MM-dd'T'HH:mm:ss.SSSZ Asia/Tokyo 2015-03-23T02:07:34.000+0900
yyyy-MM-dd'T'HH:mm:ss.SSSZ 設定なし 2015-03-23T02:07:34.000+0000
yyyy-MM-dd'T'HH:mm:ss.SSS'Z' Asia/Tokyo 2015-03-23T11:07:34.000Z
yyyy-MM-dd'T'HH:mm:ss.SSS'Z' 設定なし 2015-03-23T02:07:34.000Z
com.fasterxml.jackson.
databind.util.ISO8601DateFormat
Asia/Tokyo 2015-03-23T02:07:34Z
com.fasterxml.jackson.
databind.util.ISO8601DateFormat
設定なし 2015-03-23T02:07:34Z
設定なし Asia/Tokyo 1427076454000
設定なし 設定なし 1427076454000

設定なしっていうのはdateFormato: とかの行ごと消してるの意味

最後にシングルクォートなしのZをつけるとtime-zoneによって時差分の値が出力される

シングルクォートありだと+0900のかわりに時差分も時刻の中に含まれて計算される

com.fasterxml.jackson.databind.util.ISO8601DateFormatのときもtime-zoneの設定したら時刻に時差が含まれて計算されるのかと思ったらこっちは時差がスルーされた。なんでや・・・

両方設定なしだとlongな値で返ってくる. 当たり前だけど時差とか関係ない表記だからtime-zone指定しても同じ値になる

以上!

サーバ・Gitブランチ管理ツールつくりました

最近プロジェクトの開発環境を徐々に変えていまして、いままでは複数の案件の変更をstagingブランチにまとめたうえで1台の動作確認用サーバにデプロイしていたんですが、やっぱり案件ごとに確認したいだろうということで案件の数だけサーバを用意してそれぞれに案件のブランチをデプロイする環境に移行しています。

しかし、こういう体制に移行しようという話が出た時からの懸案事項がありました。

つまり、どのサーバにどのブランチがデプロイされているのか混乱しないのかと

うっかり他の人が使っているサーバにデプロイしてしまっては、殴り合いが起きかねません。大変です。

これを解決するために、現在各サーバにデプロイされているブランチを一覧できるツール Pigeonholeをつくりました(真の目的はScalaの練習だったりするんですが)

github.com

このツールでは下の図のような表形式でサーバとブランチの対応関係を表示します


f:id:marshi:20150712005717p:plain

Pigeonholeのポイントはこの対応関係の登録方法です

PigeonholeではHTTPのPOSTリクエストを受け付ける口を持っていて、そこにJSON形式でホスト名とブランチ名の対応関係を投げることによって登録が行われます

これによって、CIでデプロイしたあとにPigeonholeにリクエストを投げることによって自動的に登録、更新を行うことができます

HTTPリクエストは以下のような感じ

curl -X POST -d Payloads={\"host\":\"${HOST_NAME}\"\,\"branch\":\"${BRANCH_NAME}\"} http://<Pigeionholeのホスト>:<ポート>/hook/deploy

今はプロジェクトで試験稼働中です

Slick 3 系スタートキット

今回はSlick 3系の使い方のお話。

Slickとは

SlickはScalaで使えるORM.

3系は2015年4月29日にリリースされたということで割りと最近に出てきた代物。
Slick 3.0.0 | Slick

日本語のドキュメントも用意してくれている。
slick-doc-ja 3.0 — はじめよう

実際に使ってみた方もいらっしゃる。takezoe.hatenablog.com

そのうえサンプルプロジェクトも用意されている。www.typesafe.com

Slickを使用方法サンプル

もはやこの上なにを書くんだという感じだが、検索してみつかるサンプルはクエリの書き方だけ紹介してるものが多かったり、Scala初級者の自分にはhello-slick-3.0さえ難しく感じたのでだいぶ簡略化した動く状態のサンプルを作ってみた(PostgreSQL用)。

これgithub.com

サンプルではローカルに立てられているPostgreSQLに対して、Readmeに書いてあるDDLを実行してテーブルを作成している前提でCRUD操作を行う。

また、SlickにはDBのテーブルに接続して自動的にテーブルに対応したエンティティクラスを作成してくれる機能もあるのでそのためのコードも入れてある。

Slickではまった箇所の紹介

暗黙的型変換

import slick.driver.PostgresDriver.api._
import scala.concurrent.ExecutionContext.Implicits.global

これないとresultメソッドが呼び出せない/(^o^)\
これが暗黙的型変換か。ここで定義されているメソッドだからimportしてあげないと使えないんですね。
よくサンプルで出てくるクエリでは .resultって呼び出しているのに自分の環境でやるとそんなメソッドないよって怒られて「なんで???」ってなってた。

Future

クエリを発行したはずなのに結果が受け取れない

Slick3系はFutureモデルで作られているのでクエリを発行してから結果を受け取るまでは非同期になる.
が、Await.readyとかしてクエリの結果を受け取るまで待っていないとメインスレッドが終わってしまって結果が受け取れないまま終わってしまう。
Futureがわかってなかった。

そのうちSlickを使って作ってたアプリケーションの紹介でもする。

Scalaで学ぶ共変と反変

最近コップ本を読みながらScalaをちょろちょろ勉強しているんですが、共変と反変のところで頭がこんがらがってきたので頭の整理の意味もこめて記事にします。

こちらがまとまっていて非常に助かりました。qiita.com
基本的にこちらの記事を参照すればいいと思うけど、反変は戻り値に使えないとか共変は引数に使えないという部分が理解するのに時間かかったので具体的なソースコードを交えてまとめます。

まず具体的なソースコードはこちら。
コンパイルエラーが出ます。

class Creature

class Animal extends Creature

class Cat extends Animal

// +Tが共変、-Uが反変の意味
class Container[+T, -U] {

  //引数でエラー
  def f(arg: T): T = {
    new T()
  }

  //戻り値でエラー
  def f2(arg: U): T = {
    new T()
  }
  
  //戻り値でエラー  
  def f3(): U = {
    new U()
  }

}

さて、なんでこれでコンパイルエラーになるのかっていう話ですね。

共変を引数にできない話

f関数から見て行きましょう。
これは引数でエラーが出ています。

変数cの左辺の型はContainer[Animal, Animal]です。
Tは共変であるから、左辺のAnimal型に対して右辺をAnimalかCatで指定できます。
しかしCat型で定義した場合に、Containerクラスのf関数の引数がCat型を受け入れるのに対し、変数cがContainer[Animal, Animal]型であるためf関数にAnimal型を渡せてしまいます。
ここで、Cat型にAnimal型は入れられないので矛盾が発生します。
そのため共変は引数に使えません。

def main(args: Array[String]): Unit = {
  val c: Container[Animal, Animal] = new Container[Cat, Animal]()
  c.f(new Animal()) //Cat型の定義にAnimal型を渡せてしまう
}

//共変の型にCatを指定した場合のContainer定義
class Container[Cat, Animal] {
  //f2, f3関数は省略

  def f(arg: Cat): Cat = { //引数の型がCat型
    new Cat()
  }
}
共変を戻り値にできる話

f2関数を見ます。
これは引数も戻り値もエラーは出ていません。

変数cの左辺の型はContainer[Animal, Animal]であるから、戻り値の型はAnimal型です。
共変の場合、右辺として許される型はAnimalかCatなので戻り値として許される型もAnimal型かCat型になります。
これは左辺と矛盾しません。
よって共変は戻り値として使えます。

def main(args: Array[String]): Unit = {
  val c: Container[Animal, Animal] = new Container[Cat, Animal]()
  val a: Animal = c.f(new Animal()) //共変のため戻り値としてはAnimalかCatしかありえないのでAnimal型変数aで受け取れる。
}

//TにCatを指定した場合のContainer定義
class Container[Cat, Animal] {
  //f, f3関数は省略

  def f2(arg: Animal): Cat = {
    new Cat()
  }
}
反変を戻り値にできない話

f3関数を見てみましょう。
これは戻り値でエラーが出てます。

Uは反変ですから、左辺のAnimal型に対して右辺をAnimalかCreatureで指定できます。
変数cの左辺はAnimal型が指定されているので「戻り値としてAnimal型が返ってきますよ」と定義されています。
しかし、右辺としてCreature型を指定した場合、戻り値がCreature型になってしまうのでこれは左辺と矛盾します。
よって反変は戻り値として使えません。

def main(args: Array[String]): Unit = {
  val c: Container[Animal, Animal] = new Container[Animal, Creature]()
  val a: Animal = c.f3()
}

//UにCreatureを指定した場合のContainer定義
class Container[Animal, Creature] {
  //f, f2関数は省略

  def f3(): Creature = {
    new Creature()
  }
}
反変を引数にできる話

f2関数を見ます

左辺の型がAnimalなので、引数として渡される可能性があるのはAnimal型かCat型です。
反変なので、右辺のジェネリック型としてCreature型かAnimal型を指定できます。
つまり「Creature型かAnimalを引数にとるように定義されている関数に対して、Animal型かCat型を渡して実行することができる」ということなので、これは矛盾しません。
よって反変の型は引数に指定できます。

def main(args: Array[String]): Unit = {
  val c: Container[Animal, Animal] = new Container[Animal, Creature]()
 c.f2(new Animal())
  c.f2(new Cat())
}

//UにCreatureを指定した場合のContainer定義
class Container[Animal, Creature] {
  //f関数は省略

  def f2(arg: Creature): Creature = {
    new Creature()
  }
}

Intellij14でGoのSDKが設定できない件

IntellijでGoのプロジェクトを扱うためにはGoのプラグインを入れる必要がある

pluginsの設定からgolangで探すと確かにプラグインがある。よし入れよう。

が、Intellij14.0.2だとSDKの設定画面でGoのホームディレクトリを設定しよとしてOKボタンを押しても反応しない・・なんだよこれ

f:id:marshi:20150131175607p:plain


どうやら14.0.2ではこのプラグインを入れても動かないらしい。go-lang-plugin fails to set SDK for go 1.4 and intellj 14.0.2 · Issue #1132 · go-lang-plugin-org/go-lang-idea-plugin · GitHub

ということで一旦goのプラグインをアンインストールして、下記の0.9.16-alpha.9 google-go-language.jarを落としてきてpluginsの設定からInstall plugin from diskでこのjarを指定してプラグインをインストールすればOKReleases · go-lang-plugin-org/go-lang-idea-plugin · GitHub

動いたー\(^o^)/