fresh digitable

セミコロンたちが躍動する おいらのコードを 皆さんに 見せたいね

マテリアルデザインぽいアイコンをinkscapeで作った

アプリを出すにあたってアイコンを作る必要があったのでinkscape(ver. 0.92.2)でマテリアルデザインっぽいアイコンを作ってみることにした。参考にした動画:

こういうツールをまともに使ったことがなかったので動画を何度も見返したりしてなんとかそれらしい形になった。

初期のお絵かき的模索

最初はあんまり何も考えずにとにかくデフォルメしたアオエリヤケイを描こうとしていた。キャンバスサイズもめちゃくちゃ適当で本当にどうしようもない。最初は本家のアイコンと同じく右を向いている絵を描こうとしていた。

デザインする上で決めたこと

マテリアルデザインのアイコンで抑えておくべきルールは次のページに書いてある。

Icons - Style - Material design guidelines

長さの単位にdpが使われているのだが、これとpxとの関係をどう決めればよいのか分からなかったので、今回はxxxhdpiのフォルダに入っているデフォルトのアイコンのサイズと同じ192x192pxをキャンバスサイズとし、単純にdpをpxに読み替えることにした。キーラインの細かい寸法(角のRとか小さい円の半径)も分からなかったので大体で引いた。edge tintの高さは全部1pxにした。その他、陰影の色の指定のルールなどは上のページに倣った。

アプリアイコンのデザインでポイントとしたのは次の通り。

  • 頭文字のaを使う
  • 背景は緑か青で迷ったけど青だと本家と被りそうだったので緑にした
  • アオエリヤケイのトサカの青をどうしても入れたかった
  • 正方形でまとめるとペンギン感が強くなったのでちょっと縦長にしてヤケイ感が少しでも増すようにした

ざっくりとした手順

  1. キーラインを引く
  2. キーラインの大きい円に合わせて背景の円を置く
  3. aを作る
    • 右上がりのエッジだけ角度を合わせた
  4. aの影(shadow)を作る
    • aをコピーして別のレイヤーに置く
    • 塗りを黒色に変えてぼかす。オブジェクトの面積か何かによってぼかしの度合いが変わるので見た目が揃うように数値を調整する
  5. aの陰(shade)を作る
    • aをコピーして別のレイヤーに置く
    • 塗りを黒色に変えて"エクステンション -> パスから生成 -> モーション"で45度方向に円からはみ出る程度うごかす
    • 背景の円をコピーして陰のレイヤーに置き、"オブジェクト -> クリップ -> 設定"で陰の右下の方を円形にくり抜く
    • くりぬいた陰はいくつかのオブジェクトにわかれているので、パスを統合して一つにまとめる(統合できないオブジェクトができてしまい何度かやり直したので注意)
    • 塗りのグラデーションツールで右下に向かって薄くなるようにしたりレイヤーの透過度を調整したりしていい感じにする
  6. aのedge tintをつける
    • aをコピーして別のレイヤーに置く
    • もう一度コピーして下方向に1pxずらし、クリップする(くちばし以外は下側のedgeしか作っていない。下側のedgeはaに重ならないように置いたら影とかとブレンドされていい感じになったと思う。上側のedgeはaに重ねて置く)
  7. 背景のedge tintをつける
    • aと同じように1pxずらしてクリップしたかったがうまくクリップできなかったので、1px下にずらして色を濃くした円を背景の下レイヤーに差し込んだ
  8. 背景の影を作る
    • 背景をコピーして別のレイヤーに置く
    • aのようにぼかして影を作りたかったのだが同じようにならなかったので、塗りを黒のグラデーションにして背景の下からはみ出すようにする
  9. ラスタライズする
    • AS3Beta2のVector Assetではグラデーションがベタ塗りになってしまったり、陰を円形にクリップしたところをうまく変換できなかったのでそれぞれの画面解像度ごとにラスタライズした(不可視のレイヤーを無視してくれたのはよかった)
    • inkscapeコマンドラインツールでsvg -> pngしてやると便利*1

    inkscape -f [svgファイルの絶対パス] -w [幅px] -h [高さpx] -e [pngファイルの絶対パス]

雑感

  • 作っている途中で何度かスマホの画面に置いたりして調整したけど、他のアプリアイコンと見比べるとやっぱりなんだかくどい感じがする
    • 背景の上にaを浮かせたのはやりすぎたかな
    • 角を取った方がよかったかも
  • トサカと顎の下の部分のバランスをもっと模索してもよかったかも
  • 背景無しでaに直接色を乗せるなどもっといろんなパターンを試してもよかった
  • オブジェクトのコピー(ctrl + D)が分からなくて毎回似たようなオブジェクトを作って位置と高さと幅の値をコピペしてた

akihito104.hatenablog.com

Twitterクライアント`aoeliyakei`のベータ版をリリースした

自分で使うために1年半ほどずっと作っていたTwitterクライアントのβ版をplayアプリストアでオープンベータ版として公開した。FABをフリックしてRTとかlikeとかを行うクレイジーな操作感のアプリである。リポジトリ名はUdonRoadだがアプリとしてリリースするにあたり真剣に考えた方がよかろうということでaoeliyakeiという名前をつけた。

aoeliyakei(アオエリヤケイ)という名前に決めるまでには実は紆余曲折あったのだが、かいつまんで話すと次のような流れである。

  • 鳥の名前にしたいとは思っており、なんとなくフクロウをイメージしていたがすでにいろんなアプリのモチーフに使われているのでやめた
  • あるときたまたまホロホロ鳥が目に入り、FABを操作するアプリなのでホロホロ鳥の丸っこいシルエットとイメージが合って良さそうだなと思ってそれに近い感じの名前を色々と模索したが(holoholoとかholoroとか、漢字では珠鶏と書くようなのでtamadoriとか)よく似た感じの人名や焼き鳥屋さんなどがヒットしたのでやめた
  • ホロホロ鳥は英語でguinea fowlというのだが、fowlは家禽(鳥の家畜)という意味らしく、自家用のTwitterクライアントとして作っていたこのアプリにぴったりでは?と思ってfowlを生かすことにした
  • もともとは毎日コードを書いてgithubに上げようというのが目的だったのでcontributionの緑色のタイルの要素を入れようと思ってgreenを生かすことにした

akihito104.hatenablog.com

アプリアイコンは頭文字のaを鳥っぽいデザインにして背景に緑を使った。マテリアルアイコンっぽくなるようにedge tintや陰影をつけてみた。青色を入れたのは本家リスペクト。

f:id:akihito104:20170830005810p:plain

自分のために作っていたので、自分で使わないような機能は作り込みが甘いか、またはまだ実装していない*1。それでもとにかく出すことにしたのは、それをやって初めてスタートラインに立ったことになるのではないかと思ったから。完成させて人に見てもらう、使ってもらうから上手になっていけるのかな、とMFTやコミケを回っているうちにそんな風なことを考えたりした。出来上がっているものがとても眩しく見えた。Twitterクライアントアプリとしての機能は少ないかもしれないが、自分はそんなに不自由せずに使えているので、ここまでの成果をいろんな人に見てもらえるようにした。

FABをフリックするというのが受け入れられるかどうか不安で、最初は出すべきではないとも思っていたが、自分の感覚だけで結論を出すのもよくないような気もしたのでいろんな人に試してもらって意見が欲しい。

https://play.google.com/apps/testing/com.freshdigitable.udonroad

*1:リスト関連とかDMとか

ViewPagerの中に入れたImageViewで拡大縮小やスクロールをするためにやったこと

UdonRoadではツイートに添付されている画像を表示させるため、ViewPagerの中にImageViewを入れて使っている。これまでは特に何も考えず表示させていたが、パノラマっぽい横長の画像やマンガの中の小さな文字などを詳しく見るために画像を拡大できるようにしたくなったので次のような処理を追加した。

github.com

ViewPagerは左右のフリックジェスチャを受け取ってページを切り替えるため、それと競合するような横方向のジェスチャを受け取って処理したいとなると工夫が必要である。具体的には次の二つ。

  • 真横方向のピンチイン/アウト
  • 真横方向のスクロール(ドラッグ)

タッチイベントを受け取った後の処理の流れを大雑把に説明すると次のような感じ。画像の拡大縮小、移動でMatrixを使っている理由は特にありません。

  • onTouchEvent
    • ScaleGestureDetector.onTouchEvent()MotionEventを渡してピンチイン/アウトのジェスチャを認識させる
    • GestureDetector.onTouchEvent()MotionEventを渡してスクロール(ドラッグやスワイプ)ジェスチャを認識させる
    • ピンチイン/アウトまたはスクロールイベントが起きたらスケールファクタや移動量を受け取り、"画像変換のためのMatrix“を更新する
    • ViewParent.requestDisallowInterceptTouchEvent(true)を呼ぶ
    • View.invalidate()を呼んでviewを更新する
  • onDraw
    • 画像の現在のMatrixImageView.getImageMatrix()で取得して"画像変換のためのMatrix“をMatrix.postConcat()で渡す
    • スケールの値が初期値より小さくならないよう補正する
    • 画像が縦横方向に移動しすぎてフレームアウトしないように(若しくは、はみ出すようなサイズのときに背景が見えないように)補正する
    • 補正したMatrixImageView.setImageMatrix()で渡す
    • “画像変換のためのMatrix"をリセットする

肝はViewParent.requestDisallowInterceptTouchEvent(true)で、ImageViewで消費したいイベントが起きた時にこれを呼ばないと親のView(ここではViewPager)にイベントを取られてしまう。

あとは細かい処理やハマりポイントとして、次の2点を挙げておく。

  • 隣のページに行きたそうなドラッグはImageViewで受け付けずに親に渡す(shouldGoNextPage()みたいな名前のメソッドで実装している)
  • ScaleGestureDetector.OnScareGestureListener.onScale()falseを返してもScaleGestureDetector.onTouchEvent()trueを返してくる。スクロールが起きているかどうかを知りたかったらScaleGestureDetector.isInProgress()を呼ぶ

この記事の全ての事象はNexus5X(Android 7.1.2)で確認した。