こんにちは。delyでAndroidのエンジニアをしているkenzoです。
この記事はdely Advent Calendar 2018の6日目の記事です。
Qiita: https://qiita.com/advent-calendar/2018/dely Adventar: https://adventar.org/calendars/3535
昨日は検索エンジニアのsakuraが「クラシルの検索をよくするために1年間取り組んだこと」を書きました。
普段検索に携わる方はもちろん、それ以外の方にとっても面白い内容となっていますので、ぜひご覧ください。
はじめに
弊社のプロダクト開発はデザインフェーズと実装フェーズの2つのフェーズに分かれています。(詳しくはこちら↓)
後半の実装フェーズでは、前半のデザインフェーズで出来上がったデザインを元に画面・機能を作成していくことになります。
デザインフェーズでは、かなりいい感じに動くプロトタイプを作成したり、それを用いてユーザーテストを行ったりと、より良いものをユーザーに提供できるように弊社のデザイナーやデザインエンジニアが頑張ってくれています。
で、下のようなデザインが出来上がっているわけです。(これはデザインの指示ではなくて実際に実装した画面ですが)
え、めちゃ動く。。。 (はじめてデザインを見たときのきもち)
初めて見ると一瞬たじろぐかもしれませんが、動きも含めてユーザーに提供する価値なので、難しそうだからといって実装しないわけにはいきません。
今回はこのようないい感じに動くデザインを実装に落とし込んでいく際にやったことの一例を紹介します。
ViewPagerとその他Viewとの連動
まずはこちらをご覧ください。
このようにViewPager
を用いたアプリの場合、現在開いているページによってViewPager
外に表示される内容が変わることがあると思います。(画面下部や右上の★)
もちろんこれでOKな場合も多いとは思いますが、もう少し細かい動きにこだわったUIを作成したい場合もあるかと思います。
たとえばこんな感じです。
今回はこれの実装方法をご紹介します。
用いるのはOnPageChangeListener
のonPageScrolled
です。
val viewPager: ViewPager = findViewById<ViewPager>(R.id.view_pager) viewPager.addOnPageChangeListener(object : ViewPager.OnPageChangeListener { override fun onPageScrollStateChanged(state: Int) { } override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) { } override fun onPageSelected(position: Int) { } })
onPageScrolled
はViewPager
のスクロール中に呼び出され、その際に引数として下記の値が渡されます。
(positionOffsetPixels
は今回は使いません)
position
: 表示されている最初のページのindexpositionOffset
:position
で指し示されるページからのオフセットの割合を0以上1未満の値でpositionOffsetPixels
: position`で指し示されるページからのオフセットのpixel値
つまり、どのタイミングでどんな値が返るかというと、、
position
: 0 positionOffset
: 0.0
position
: 0 positionOffset
: 0.31944445
position
: 0 positionOffset
: 0.6259259
position
: 1 positionOffset
: 0.0
という感じです。
以上のような値がスクロールしている間に何度も呼ばれるonPageScrolled
で渡されます。
この値を使ってその他Viewのtranslationやalpha、scaleをセットします。
上のサンプルの「いいいいい」のViewの例だと、
position
が0の時はpositionOffset
の増加に従って表示位置が上がるposition
が1の時はpositionOffset
の増加に従って表示位置が下がるposition
が2(上記以外)の時は常に表示位置が下のまま
なので、下記のようにtransilationY
をセットします。
override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) { view.translationY = when (position) { 0 -> view.height - view.height * positionOffset 1 -> view.height * positionOffset else -> view.height.toFloat() } } // viewは「いいいいい」のviewです
このような動きになります。
もう1つ、上のサンプルの「★」のViewの例だと、
position
が0の時(下記以外)は不透明度が0のままposition
が1の時はpositionOffset
の増加に従って不透明度が上がる(だんだん見えるようになる)position
が2の時は不透明度が1のまま(ページが増えるようならpositionOffset
の増加に従って不透明度を下げる)
なので、下記のようにalpha
をセットします。
override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) { star.alpha = when (position) { 1 -> positionOffset 2 -> 1f // 今回はpositionの最大が2なのでこれでよいですが、ページが増える場合は1f - positionOffsetとします else -> 0f } } // starは「★」のviewです
このような動きになります。
これで、ViewPagerをスクロールさせた時に他のViewと連動させることができました。
おわりに
Androidでも他のフロントエンドでも、動きのある画面を作るのはとっつきにくかったり難しかったりと、実装に若干のハードルはあると思います。
ですが、やっぱり自身がユーザーとしてサービスに触れる際に、いい感じに動いてくれると気持ちよかったり、わかりやすかったりと、良い体験ができていると感じます。
使ってくれる人がより良い体験をできるように、ものづくりをする人間としてはそういうとこ頑張らないとなーと思うところです。
ちなみに上でデザインの例としてあげた「クラシルかんたん献立機能」が今回リリースされました!
使ってみてもらえるととても嬉しいです。
アプリはこちら: Android iOS
明日はiOSエンジニアの堀口の「iOS版クラシルの開発からリリースまでの流れ」です。お楽しみに!