Lock in $30 Savings on PRO—Offer Ends Soon! ⏳
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
それっぽいカルーセルをつくる
Search
Yoshihiro WADA
December 13, 2018
Programming
4
730
それっぽいカルーセルをつくる
2018/12/13に開催された Shibuya.apk #30 にて発表した資料です
Yoshihiro WADA
December 13, 2018
Tweet
Share
More Decks by Yoshihiro WADA
See All by Yoshihiro WADA
AndroidデバイスにFTPサーバを建立する
e10dokup
0
750
Gradleの実行環境設定を見直す
e10dokup
0
1.2k
Firebase App Distributionのテストアプリ配信を試しやすくする
e10dokup
0
670
アプリに署名する 〜GitHub ActionsでのCIも見据えて〜
e10dokup
0
1.3k
Profileable buildでより正確なパフォーマンスを掴む
e10dokup
0
760
[DroidKaigi 2021] メディアアクセス古今東西 / Now and Future of Media Access
e10dokup
0
3.8k
今更「dp」を考える / Let's think about "dp" now
e10dokup
0
5.8k
1から学ぶAndroidアプリデバッグ - アプリの動作を追いかけよう / Learn Android application debugging from the scratch - track apps' behaviors
e10dokup
10
3.4k
Guide to background processingを読んでみる / Reading "Guide to background processing"
e10dokup
0
280
Other Decks in Programming
See All in Programming
Go コードベースの構成と AI コンテキスト定義
andpad
0
120
関数実行の裏側では何が起きているのか?
minop1205
1
680
TUIライブラリつくってみた / i-just-make-TUI-library
kazto
1
360
tsgolintはいかにしてtypescript-goの非公開APIを呼び出しているのか
syumai
6
2.1k
[堅牢.py #1] テストを書かない研究者に送る、最初にテストを書く実験コード入門 / Let's start your ML project by writing tests
shunk031
12
7.1k
生成AIを利用するだけでなく、投資できる組織へ
pospome
0
220
新卒エンジニアのプルリクエスト with AI駆動
fukunaga2025
0
190
Microservices Platforms: When Team Topologies Meets Microservices Patterns
cer
PRO
1
1k
CSC305 Lecture 17
javiergs
PRO
0
340
Level up your Gemini CLI - D&D Style!
palladius
1
180
Tinkerbellから学ぶ、Podで DHCPをリッスンする手法
tomokon
0
120
大体よく分かるscala.collection.immutable.HashMap ~ Compressed Hash-Array Mapped Prefix-tree (CHAMP) ~
matsu_chara
1
210
Featured
See All Featured
Optimizing for Happiness
mojombo
379
70k
Large-scale JavaScript Application Architecture
addyosmani
515
110k
Practical Orchestrator
shlominoach
190
11k
RailsConf 2023
tenderlove
30
1.3k
GraphQLの誤解/rethinking-graphql
sonatard
73
11k
How GitHub (no longer) Works
holman
316
140k
The Invisible Side of Design
smashingmag
302
51k
Why You Should Never Use an ORM
jnunemaker
PRO
61
9.6k
The MySQL Ecosystem @ GitHub 2015
samlambert
251
13k
A Tale of Four Properties
chriscoyier
162
23k
[RailsConf 2023 Opening Keynote] The Magic of Rails
eileencodes
31
9.8k
How to Ace a Technical Interview
jacobian
280
24k
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を書かなくてもなんとかなるのかな • 未検証です
ありがとうございました