Material Design 下 ViewPager 切换动效的实现方式

在 Android 开发中,ViewPager 是常用的一种控件,常常用于实现轮播图、图片浏览等功能。在 Material Design 中,ViewPager 切换动效被要求具有更加流畅自然以及针对性强的效果。本文将探讨 Material Design 下 ViewPager 切换动效的实现方式。

Material Design 下 ViewPager 切换动效的设计理念

Material Design 规范中关于 ViewPager 切换动效的设计理念如下:

  1. 能够让用户感受到物体移动的连续性和逐渐变化的速度。
  2. 提供物体的路径、速度和位置的透明度,使得用户可以预测物体下一个可能的位置。
  3. 支持用户在旅程的各个阶段中能够进行正常控制。

基于以上设计原则,我们可以尝试实现以下动效特征:

  1. 切换过程中页面元素的视觉感受存在连续性,呈现出一种自然的流动感。
  2. 切换过程中,页面元素的路径、速度和位置的透明度应该是清晰的。可以让用户在这个过程中感到跟踪操作非常简单。
  3. 不会出现卡顿现象,造成用户的不适。

ViewPager 切换动效的实现方式

在 Android 中,ViewPager 切换动效的实现,有三种方案:

1. 通过设置系统默认动画实现 ViewPager 切换动效

在 Android 中,ViewPager 默认的切换动画是 PageTransformer。我们可以直接在代码中调用系统默认提供的 View 的 API 来进行页面切换的翻页效果:

viewPager!!.setPageTransformer(true, DepthPageTransformer())

2. 自定义切换动效的实现方式:使用 ViewPager.PageTransformer 接口

使用 ViewPager.PageTransformer 接口可以实现自定义的页面切换动效。这种方法的实现仅需要简单的使用 ViewPager.setPageTransformer(boolean, PageTransformer) 方法即可。

3. 自定义切换动效的实现方式:通过多个动画协同实现 ViewPager 切换动效

此方法是最复杂的方案,它是通过加入多个动画实例,结合使用的方式,实现 ViewPager 的切换效果。他不仅要理解多种不同的动画实例,还需要处理它们的交互和晨合,也需要处理 这些动画实例在相互上下文中的演绎关系。

viewPager!!.addOnPageChangeListener(object : ViewPager.OnPageChangeListener {
    override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
        var realCurrentPosition: Int
        var nextPosition: Int
        var realPositionOffset: Float
        var left: View?
        var right: View?
        val isLeftToRight = lastPosition < position

        if (isLeftToRight) {
            realCurrentPosition = position - 1
            nextPosition = position
            realPositionOffset = positionOffset
            left = getViewByPosition(realCurrentPosition)
            right = getViewByPosition(nextPosition)
        } else {
            realCurrentPosition = position + 1
            nextPosition = position
            realPositionOffset = 1 - positionOffset
            left = getViewByPosition(nextPosition)
            right = getViewByPosition(realCurrentPosition)
        }
        if (left != null) {
            val lp = left.layoutParams as FrameLayout.LayoutParams
            lp.width = (mContainerWidth - mTransformer!!.getOffsetWidth() - mTransformer!!.getMarginWidth()) / 2 + (mTransformer!!.getOffsetWidth() + mTransformer!!.getMarginWidth()) * realPositionOffset.toInt()
            left.layoutParams = lp
        }
        if (right != null) {
            val lp = right.layoutParams as FrameLayout.LayoutParams
            lp.width = (mContainerWidth - mTransformer!!.getOffsetWidth() - mTransformer!!.getMarginWidth()) / 2 + (mTransformer!!.getOffsetWidth() + mTransformer!!.getMarginWidth()) * (1 - realPositionOffset).toInt()
            right.layoutParams = lp
        }
    }

    override fun onPageScrollStateChanged(state: Int) {
        if (state == ViewPager.SCROLL_STATE_IDLE) {
            lastPosition = viewPager!!.currentItem
        }
    }

    override fun onPageSelected(position: Int) {}
})

案例分析

接下来我们将通过一个实例来展示以上三种实现方式效果的区别。

在这个实例中,我们将使用三张图片实现轮播效果。预期轮播效果如下:

1. 使用系统默认动画实现切换动效

我们可以使用系统默认动画实现切换效果,效果如下:

viewPager!!.setPageTransformer(true, DepthPageTransformer())

可以看到,通过设置系统默认动画,效果显得简单生硬,没有华丽的效果,缺乏足够的设计感。

2. 使用 ViewPager.PageTransformer 接口实现切换动效

此方案可以实现自定义动画,更好地实现 Material Design 设计效果。我们可以通过创建具有高度自定义性的 PageTransformer 子类,满足 Material Design 设计要求的需要,实现更加华丽的效果。

val pageTransformer = ParallaxPageTransformer()
    viewPager!!.setPageTransformer(true, pageTransformer)
class ParallaxPageTransformer : ViewPager.PageTransformer {

    override fun transformPage(page: View, position: Float) {

        if (position >= -1 || position <= 1) {
            transformOutline(page, position)
        }

        if (position >= -1 && position <= 1) {
            transformPageView(page, position)
        } else {
            page.alpha = 0f
        }

        if (position >= -0.5f && position <= 0.5f) {
            transformBody(page, position)
        }
    }

    private fun transformBody(view: View, position: Float) {
        val width = (view.width / 2 * position).toInt()
        view.findViewById<ImageView>(R.id.img_body).scrollTo(-width, 0)
    }

    private fun transformPageView(page: View, position: Float) {
        val pageWidth = page.width
        val pageHeight = page.height

        when {
            position < -1 -> page.alpha = 0f
            position <= 1 -> {
                val depth = 1f - abs(position)
                page.scaleX = depth
                page.scaleY = depth

                page.translationX = if (position > 0) {
                    width * -position
                } else {
                    width * position
                }

                page.alpha = 1f
            }
            else -> page.alpha = 0f
        }
    }

    private fun transformOutline(view: View, position: Float) {
        val width = view.width

        when {
            position <= -1 -> view.alpha = 0f
            position <= 0 -> {
                view.alpha = 1f
                view.findViewById<View>(R.id.img_back).translationX = -width * position / 2
                view.findViewById<View>(R.id.img_shadow).translationX = -width * position / 8
            }
            else -> {
                view.alpha = 1f
                view.findViewById<View>(R.id.img_back).translationX = -width * position / 2
                view.findViewById<View>(R.id.img_shadow).translationX = -width * position / 8
            }
        }
    }
}

3. 自定义切换动效的实现方式:通过多个动画协同实现 ViewPager 切换动效

这种方式最为复杂,需要理解多种不同的动画实例,还要处理它们的交互和晨合,也需要处理 这些动画实例在相互上下文中的演绎关系。

viewPager!!.addOnPageChangeListener(object : ViewPager.OnPageChangeListener {
    override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
        var realCurrentPosition: Int
        var nextPosition: Int
        var realPositionOffset: Float
        var left: View?
        var right: View?
        val isLeftToRight = lastPosition < position

        if (isLeftToRight) {
            realCurrentPosition = position - 1
            nextPosition = position
            realPositionOffset = positionOffset
            left = getViewByPosition(realCurrentPosition)
            right = getViewByPosition(nextPosition)
        } else {
            realCurrentPosition = position + 1
            nextPosition = position
            realPositionOffset = 1 - positionOffset
            left = getViewByPosition(nextPosition)
            right = getViewByPosition(realCurrentPosition)
        }
        if (left != null) {
            val lp = left.layoutParams as FrameLayout.LayoutParams
            lp.width = (mContainerWidth - mTransformer!!.getOffsetWidth() - mTransformer!!.getMarginWidth()) / 2 + (mTransformer!!.getOffsetWidth() + mTransformer!!.getMarginWidth()) * realPositionOffset.toInt()
            left.layoutParams = lp
        }
        if (right != null) {
            val lp = right.layoutParams as FrameLayout.LayoutParams
            lp.width =
                (mContainerWidth - mTransformer!!.getOffsetWidth() - mTransformer!!.getMarginWidth()) / 2 + (mTransformer!!.getOffsetWidth() + mTransformer!!.getMarginWidth()) * (1 - realPositionOffset).toInt()
            right.layoutParams = lp
        }
    }

    override fun onPageScrollStateChanged(state: Int) {
        if (state == ViewPager.SCROLL_STATE_IDLE) {
            lastPosition = viewPager!!.currentItem
        }
    }

    override fun onPageSelected(position: Int) {}
})

总结

为了提高用户的体验,我们应当在 Material Design 下,高效应对 ViewPager 切换效果的实现。给用户带来更为豪华、自然、流畅的视觉体验。本文中讨论了在 Material Design 下 ViewPager 切换动效的设计理念、三种实现方式及其案例分析。希望本文内容能为读者在开发中提供一些有益的技术指导。

来源:JavaScript中文网 ,转载请注明来源 本文地址:https://www.javascriptcn.com/post/6594e100eb4cecbf2d92bc59


纠错反馈