Thuy's Blog

Hack showcase: WheelPager

February 26, 2019

What?

This demo shows how to create a wheel-like page transformation for ViewPager2 (that supports both vertical & horizontal page transition).

Vertical Horizontal
Vertical Horizontal

(If the gifs don’t show up, please check out this YouTube video for a demo)

How?

The key is to implement a PageTransformer which makes the page items rotating around a pivot point like below:

viewPager.setPageTransformer { page: View, position: Float ->
  page.apply {
    // To allow only rotation around the pivot point.
    x = 0f
    y = 0f

    // To adjust pivot point correctly based on ViewPager orientation.
    when {
      viewPager.orientation == ViewPager2.ORIENTATION_HORIZONTAL -> {
        pivotX = width.toFloat() / 2f
        pivotY = height.toFloat()
      }
      viewPager.orientation == ViewPager2.ORIENTATION_VERTICAL -> {
        pivotX = width.toFloat()
        pivotY = height.toFloat() / 2f
      }
    }

    val direction = when {
      viewPager.orientation == ViewPager2.ORIENTATION_HORIZONTAL -> 1f
      viewPager.orientation == ViewPager2.ORIENTATION_VERTICAL -> -1f
      else -> error("Oops! Sorry, this only works for vertical & horizontal.")
    }

    when {
      position == 0f -> {
        // When the page is front-and-center.
        Log.i(logTag, "${page.tag} is now a front-and-center page")

        rotation = direction * position * 180
      }
      position == 1f -> {
        // When the page is a full page to the right (or bottom in vertical orientation).
        Log.i(logTag, "${page.tag} is now a full page to the right")

        rotation = direction * position * 180
      }
      position == -1f -> {
        // When the page is a full page to the left (or up in vertical orientation).
        Log.i(logTag, "${page.tag} is now a full page to the left")

        rotation = direction * position * 180
      }
      position < 0 && position > -1f -> {
        // [-1, 0]
        // This is when moving a page in the LHS.
        Log.i(logTag, "${page.tag} is moving w/ position $position")

        rotation = direction * position * 180
      }
      position < 1 && position > 0f -> {
        // (0, 1]
        // This is when moving a page in the RHS.
        Log.i(logTag, "${page.tag} is moving w/ position $position")

        rotation = direction * position * 180
      }
      else -> {
        // This is when `position` is out of the bounds of `[-1, 1]`.
        // In other words, when the page is way off-screen.
        Log.i(logTag, "${page.tag} is way off-screen")
      }
    }
  }
}

Source code is available on my GitHub.


Thuy Trinh

Written by Thuy Trinh who lives and works in Frankfurt, Germany building robust Android apps. You should follow him on Twitter