Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
それっぽいカルーセルをつくる
Search
Yoshihiro WADA
December 13, 2018
Programming
4
700
それっぽいカルーセルをつくる
2018/12/13に開催された Shibuya.apk #30 にて発表した資料です
Yoshihiro WADA
December 13, 2018
Tweet
Share
More Decks by Yoshihiro WADA
See All by Yoshihiro WADA
Gradleの実行環境設定を見直す
e10dokup
0
810
Firebase App Distributionのテストアプリ配信を試しやすくする
e10dokup
0
540
アプリに署名する 〜GitHub ActionsでのCIも見据えて〜
e10dokup
0
1.1k
Profileable buildでより正確なパフォーマンスを掴む
e10dokup
0
700
[DroidKaigi 2021] メディアアクセス古今東西 / Now and Future of Media Access
e10dokup
0
3.4k
今更「dp」を考える / Let's think about "dp" now
e10dokup
0
5.5k
1から学ぶAndroidアプリデバッグ - アプリの動作を追いかけよう / Learn Android application debugging from the scratch - track apps' behaviors
e10dokup
10
3.1k
Guide to background processingを読んでみる / Reading "Guide to background processing"
e10dokup
0
250
よしなに頑張る画像ロードの話 / image load mettya tsurai
e10dokup
2
480
Other Decks in Programming
See All in Programming
Stackless и stackful? Корутины и асинхронность в Go
lamodatech
0
1.3k
Fibonacci Function Gallery - Part 2
philipschwarz
PRO
0
210
EC2からECSへ 念願のコンテナ移行と巨大レガシーPHPアプリケーションの再構築
sumiyae
3
590
PSR-15 はあなたのための ものではない? - phpcon2024
myamagishi
0
400
Flatt Security XSS Challenge 解答・解説
flatt_security
0
730
週次リリースを実現するための グローバルアプリ開発
tera_ny
1
1.2k
Lookerは可視化だけじゃない。UIコンポーネントもあるんだ!
ymd65536
1
130
生成AIでGitHubソースコード取得して仕様書を作成
shukob
0
630
Оптимизируем производительность блока Казначейство
lamodatech
0
950
React 19でお手軽にCSS-in-JSを自作する
yukukotani
5
560
技術的負債と向き合うカイゼン活動を1年続けて分かった "持続可能" なプロダクト開発
yuichiro_serita
0
300
はてなにおけるfujiwara-wareの活用やecspressoのCI/CD構成 / Fujiwara Tech Conference 2025
cohalz
3
2.7k
Featured
See All Featured
Git: the NoSQL Database
bkeepers
PRO
427
64k
What's in a price? How to price your products and services
michaelherold
244
12k
Optimizing for Happiness
mojombo
376
70k
Product Roadmaps are Hard
iamctodd
PRO
50
11k
The Pragmatic Product Professional
lauravandoore
32
6.4k
Reflections from 52 weeks, 52 projects
jeffersonlam
348
20k
Dealing with People You Can't Stand - Big Design 2015
cassininazir
365
25k
What’s in a name? Adding method to the madness
productmarketing
PRO
22
3.2k
Intergalactic Javascript Robots from Outer Space
tanoku
270
27k
Thoughts on Productivity
jonyablonski
68
4.4k
Visualizing Your Data: Incorporating Mongo into Loggly Infrastructure
mongodb
44
9.4k
Learning to Love Humans: Emotional Interface Design
aarron
274
40k
Transcript
それっぽいカルーセルを作る Cyber Agent Inc. Yoshihiro Wada a.k.a. @e10dokup 2018/12/13 Shibuya.apk
自己紹介 • Yoshihiro Wada a.k.a. @e10dokup • 大体右のアイコンで息をしています • CyberAgant
Inc. / Ameba • Amebaブログやってます • We’re Hiring • カメラ沼 • 財布の透過率が高いのが最近の悩み
今日のコンテンツ • カルーセルを作る • ループするしないとかするとかいろいろあるが… • 今回は画面横方向に面が並んだものと定義します • 正直EpoxyにCarouselItemってあるやん…? 2
1 3 4
特に… • 画面横幅いっぱいにコンテンツアイテムとかを出すスナップで行き来できるカルーセル • マージンがついたり • 触っていないときは隣の面が見えるように…とかだったり • 今回はRecyclerViewで実装していきます(ViewPager) •
本音:Groupieで雑にAdapterを作って管理したかった 2 1 3
やっていきましょう
レベル1 単純に画面いっぱいのカルーセル
どうということはない • RecyclerViewに対してlayout_widthがmatch_parentなItemのLayoutを用意する • Adapterにはそのレイアウトを渡してInflateさせる • SnapHelperにRecyclerViewを渡してスナップによるスクロールをさせるようにする
SnapHelper • RecyclerView.onFlingListenerを継承したAbstract class • フリングしたときに子Viewにスナップさせる • 継承クラスは2つ • LinearSnapHelper
• snap対象のViewがRecyclerViewの中心に来るようにスナップ • PagerSnapHelper • ViewPagerっぽい挙動のスナップ • SnapHelper#attachToRecyclerView(recyclerView)でアタッチ • nullを入れるとdetach。多重attachしないように気をつける
レイアウトを用意して <android.support.constraint.ConstraintLayout android:layout_width="match_parent" android:layout_height="150dp" android:background="#EEEEEE"> <TextView android:id="@+id/textView" android:layout_width="wrap_content" android:layout_height="wrap_content" ɾɾɾ/>
</android.support.constraint.ConstraintLayout>
RecyclerViewにAdapterをセットする // container͕RecyclerViewͰ͢ adapter = RecyclerAdapter(list) // onDestroyͳΓͰdetach͢Δ snapHelper.attachToRecyclerView(container) container.setHasFixedSize(true)
container.layoutManager = LinearLayoutManager( this, LinearLayoutManager.HORIZONTAL, // ਫฏํͳͷͰ… false) container.adapter = adapter
レベル2 アイテム間にスペースを つけるカルーセル
Spaceを入れるItemDecorationをつけよう • getItemOffsetsをoverrideしたItemDecorationを用意する • 引数のoutRectのleft/rightを必要なspace分ずらす • 実装したItemDecorationをRecyclerViewにaddするだけ container.addItemDecoration(SpaceItemDecoration(SPACE_SIZE))
ItemDecorationの中はこんな感じ override fun getItemOffsets( outRect: Rect, view: View, parent: RecyclerView,
state: RecyclerView.State) { outRect.set(0, 0, space, 0) // ඞཁͳϚʔδϯΛηοτ // outRect.right = space ͱ͔ͰOKɻͨͩ͠pxͳͷͰdp -> px͢Δ }
なんか左寄りなので真ん中にしたい override fun getItemOffsets( outRect: Rect, view: View, parent: RecyclerView,
state: RecyclerView.State) { // ࠨӈۉʹϚʔδϯ͕ͨΔΑ͏ʹ͢Δ outRect.set(space / 2, 0, space / 2, 0) }
良さそう ちゃんと真ん中に寄ってる
レベル3 隣のアイテムが見えるカルーセル
やりたいことは… • 触っていないときは隣のアイテムが見えている状態にしたい • 画面幅に対して左右60dpずつマージンをとる • itemが並ぶ間隔はマージンの半分である30dpにする 2 3 1
60dp 30dp
変えないといけないこと • 元々がwidthがmatch_parentなので、このままではいくらItemDecorationでOffsetをつけて も隣のViewは同じようには見えない • OffsetでItemのサイズが変わるので無理やり一つ見えてもほかのサイズが変わったり… • アイテム自体を固定値に…できたら良かった • 世の中の端末すべてが横幅360dpではない
• 隣のアイテムが見える + アイテムが中央に来るは達成できるが… • 端末によっては中央にアイテムが来るように調整した結果 余白が大きくスカスカに見える • Eg. Nexus 5X -> 411dp、Essential PH-1 -> 431dp…
つまり… • match_parent時にしたときのitemのサイズを制限できればいい • ということは親が小さくなればいい • 親になるRecyclerView自体にPaddingを設定する
少し算数する • アイテム間のスペースが30dpの時、RecyclerViewのPaddingを60dpにして、かつ中央にア イテムが来るような値のとり方を考える • 前述の通り、アイテムの中央寄せの為に左右に同じスペースをとる • つまりアイテムの左右には15dpを与える • 最初のアイテムも左側には15dp持つことになるので、Paddingを安直に60dpにすると75dp
になる。ここは60dpで抑えたい • よってRecyclerView取るべきPaddingは60-15=45dp 2 3 1 60dp 30dp
少し算数する(一般化) • アイテム間のスペースがx[dp]の時、RecyclerViewのPaddingを2x[dp]にして、かつ中央に アイテムが来るような値のとり方を考える • 前述の通り、アイテムの中央寄せの為に左右に同じスペースをとる • つまりアイテムの左右には0.5x[dp]を与える • 最初のアイテムも左側には0.5x[dp]持つことになるので、Paddingを安直に2x[dp]にすると
2.5x[dp]になる。ここは2x[dp]で抑えたい • よってRecyclerViewが取るべきPaddingは2x-0.5x=1.5x[dp] • XML上に書いてもItemDecorationには反映されないのでこれをコードで定義する • SpaceItemDecorationを作る際に一緒にRecyclerViewを引数に渡してみる
いい感じにマージンを設定する fun from( recyclerView: RecyclerView, itemSpaceDp: Int ): SpaceItemDecoration {
val paddingPx = itemSpaceDp.toPx() // ͍͍ײ͡ʹdp -> pxม // RecyclerViewʹ༩͑Δpaddingʢ1.5x[dp]Λ༻ҙ͢Δʣ val edgePaddingPx = (paddingPx * 3 / 2).toInt() recyclerView.setPadding(edgePaddingPx, 0, edgePaddingPx, 0) return SpaceItemDecoration(paddingPx.toInt()) }
やったか…? 隣が見えていない (´・ω・`)
つまり… • match_parent時にしたときのitemのサイズを制限できればいい • ということは親が小さくなればいい • 親になるRecyclerView自体にPaddingを設定する • RecyclerView自体にPaddingをつけても隣は結局見えない
つまり… • match_parent時にしたときのitemのサイズを制限できればいい • ということは親が小さくなればいい • 親になるRecyclerView自体にPaddingを設定する • RecyclerView自体にPaddingをつけても隣は結局見えない •
clipToPaddingをfalseにすることで見えるようになる • デフォルトではPaddingによる余白の描画は切り取るようになっているがそれを表示する • paddingされたコンテンツの外側にあるものが見えるようになる
いい感じにマージンを設定する fun from( recyclerView: RecyclerView, itemSpaceDp: Int ): SpaceItemDecoration {
val paddingPx = itemSpaceDp.toPx() // ͍͍ײ͡ʹdp -> pxม // RecyclerViewʹ༩͑Δpaddingʢ1.5x[dp]Λ༻ҙ͢Δʣ val edgePaddingPx = (paddingPx * 3 / 2).toInt() recyclerView.setPadding(edgePaddingPx, 0, edgePaddingPx, 0) recyclerView.clipToPadding = false // ͜ΕͰྡ͕ݟ͑Δ return SpaceItemDecoration(paddingPx.toInt()) }
良さそう ちゃんと隣も見えていて 中央に寄ってる
まとめ • RecyclerViewでいろんなカルーセルを作る話でした • 結局作りたかったのは「隣のitemがちらっと見えるカルーセル」 • 今回は「画面幅に対するPaddingの半分の間隔でitemが並んでいる時」の話なので デザインで異なる場合はそれにあった算数が必要 • EpoxyのCarouselItemを使うと
setItemSpacingPx でアイテム間のスペースを 設定できるのでItemDecorationを書かなくてもなんとかなるのかな • 未検証です
ありがとうございました