ViewPager2でもPageTransformerを使うことで、様々なページ遷移を実装できます。
ただ、これだけではページそのものに細工することしかできません。
例えば、2ページ目がかぶってくるように変更した場合は、以下のようにページ境界にドロップシャドウぐらい入れたくなりますよね。
この実装方法について説明します。
PageTransformer
まずは左側のページが右側のページの上にかぶってくるような遷移をするPageTransformerを実装します。
binding.viewPager.setPageTransformer { page, position ->
page.translationX = if (position < 0f) -page.width * position else 0f
}
以上!
軽く説明すると、PageTransformerは何もしないと、ViewPagerのデフォルトの動作になります。
positionが負の値の場合、何もしなければposition*ページ幅分左側に移動しているので、同じ移動量右方向に移動させて、動かさないようにしているという形です。
Drawableを用意する
描画方法はいろいろあるとは思いますが、Drawableを用意するのが安直でしょう。今回は以下のようなxmlを用意しました
<?xml version="1.0" encoding="utf-8"?>
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle"
>
<size android:width="24dp" />
<gradient
android:angle="180"
android:endColor="#00000000"
android:centerColor="#04000000"
android:startColor="#10000000"
android:type="linear"
/>
</shape>
ItemDecoration
ViewPager2は内部の実装にRecyclerViewが使われており、ViewPager2にもRecyclerView.ItemDecorationを設定することができます。
RecyclerView.ItemDecorationは以下の3つのメソッドをOverrideすることで様々な装飾を実装できます。
メソッド | 意味 |
---|---|
getItemOffsets |
要素間のOffsetを定義します。デフォルトではOffsetなしとなります。 |
onDraw |
デコレーションの描画を行います。要素の描画前に実行されるため、要素の下に描画されます。 |
onDrawOver |
デコレーションの描画を行います。要素の描画後に実行されるため、要素の上に描画されます。 |
今回の目的ではonDrawOver
を実装するだけで良いです。
ItemDecorationではparent(RecylerView)に対するCanvasが与えられ、childViewを調べて描画することになります。
pageの左座標が、canvas内にあるとき、その左側に描画します。
class DropShadowDecoration(context: Context) : ItemDecoration() {
private val drawable = AppCompatResources.getDrawable(context, R.drawable.shadow)!!
override fun onDrawOver(c: Canvas, parent: RecyclerView, state: RecyclerView.State) {
parent.forEach {
if (it.left in 1 until c.width) {
drawable.setBounds(it.left - drawable.intrinsicWidth, 0, it.left, c.height)
drawable.draw(c)
}
}
}
}
併せて、以下のような指定をすることで冒頭のようなページ境界にドロップシャドウを表示させることができます。
binding.viewPager.addItemDecoration(DropShadowDecoration(this))
binding.viewPager.setPageTransformer { page, position ->
page.translationX = if (position < 0f) -page.width * position else 0f
}
以上です。
追記
左向きにスライドさせる場合
グラデーションを逆向きにして
<?xml version="1.0" encoding="utf-8"?>
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle"
>
<size android:width="24dp" />
<gradient
android:angle="0"
android:endColor="#00000000"
android:centerColor="#04000000"
android:startColor="#10000000"
android:type="linear"
/>
</shape>
右側にシャドウを描画
class DropShadowDecoration(context: Context) : ItemDecoration() {
private val drawable = AppCompatResources.getDrawable(context, R.drawable.shadow)!!
override fun onDrawOver(c: Canvas, parent: RecyclerView, state: RecyclerView.State) {
parent.forEach {
if (it.right in 0 until c.width) {
drawable.setBounds(it.right, 0, it.right + drawable.intrinsicWidth, c.height)
drawable.draw(c)
}
}
}
}
PageTransformerでtranslationXの操作を右側のページに行うように変更しますが、
そのままだと右側のページの方が上になってしまうため、translationZにも細工をして、上下逆に表示させます。
binding.viewPager.addItemDecoration(DropShadowDecoration(this))
binding.viewPager.setPageTransformer { page, position ->
page.translationZ = if (position > 0f) -1f else 0f
page.translationX = if (position > 0f) -page.width * position else 0f
}