工作生活

PageTransformer 源码解析

2019-07-05  本文已影响0人  没有颜色的菜

前言

作为一个很久没写过 Android 业务的人,心里有点慌了,于是拿起 Android Studio,还是找点东西学习一下,并且记录一下。一直觉得 ViewPager 是个好东西,偶然间看到一些很好的案例,很酷炫的翻页效果。直到了解了这个东西的实现原来没有想象中的那么复杂,但如果没有深刻理解,还是很难写出酷炫的效果的。于是, ViewPager Transformer 的学习就提上了日程。

Hello World

首先我们来实现一个场景,很简单,只需要一个 ViewPager,然后给他设置几页用来展现效果就行了

Layout 文件

只需要放入一个 ViewPager

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">
    <androidx.viewpager.widget.ViewPager
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:id="@+id/view_pager">
    </androidx.viewpager.widget.ViewPager>

</androidx.constraintlayout.widget.ConstraintLayout>

之后,我们创建一个 PageAdapter,可以直接使用 FragmentPagerAdapter,getItem 返回一个 Fragment 就好了

class PageAdapter(fm: FragmentManager) : FragmentPagerAdapter(fm) {

    override fun getItem(position: Int): Fragment {
        return PageFragment("Fragment $position", position)
    }

    override fun getCount(): Int {
        return 4
    }

    @SuppressLint("ValidFragment")
    class PageFragment(private var content: String, private var position: Int) : Fragment() {

        private val colors = Arrays.asList(Color.GRAY, Color.RED, Color.BLUE, Color.YELLOW)!!
        override fun onActivityCreated(savedInstanceState: Bundle?) {
            super.onActivityCreated(savedInstanceState)
            text_view.text = content
        }

        override fun onCreateView(
            inflater: LayoutInflater,
            container: ViewGroup?,
            savedInstanceState: Bundle?
        ): View? {
            val view = inflater.inflate(R.layout.tab_item, container, false)
            view.setBackgroundColor(colors[position % colors.size])
            view.tag = "$position"
            return view
        }
    }
}

在我们的 Activity 中,设置 view_pager,这里需要注意的是,由于我们使用了 FragmentPagerAdapter,所以我们在展示是如果需要展示多页的话,必须设置为 offscreenPageLimit 一个比较大的值,以便 ViewPager 能够渲染足够多的页面满足我们的需求。
最后为 ViewPager 设置一个 PageTransformer

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        view_pager.adapter = PageAdapter(supportFragmentManager)

        view_pager.offscreenPageLimit = 4

        view_pager.setPageTransformer(true, ViewPagerTransformer(TransformType.DEPTH))
    }
}

一个比较简单的 PageTransformer 的实现如下

class ViewPagerTransformer : ViewPager.PageTransformer  {
    override fun transformPage(page: View, position: Float) {
          page.rotationY = position * -30f
    }
}

效果如下,翻页时会根据位置修改页面的显示,页面将会绕 Y 轴进行旋转一定的角度,效果很赞吧!!!只用了一点代码


页面翻转

ViewPager.PageTransformer

定义

PageTransfomer 接口只有一个方法,该方法有两个参数,一个是 page,指的是 ViewPage 的一个内容页

    public interface PageTransformer {
        /**
         * Apply a property transformation to the given page.
         *
         * @param page Apply the transformation to this page
         * @param position Position of page relative to the current front-and-center
         *                 position of the pager. 0 is front and center. 1 is one full
         *                 page position to the right, and -1 is one page position to the left.
         */
        void transformPage(@NonNull View page, float position);
    }

position 指的是该内容页的位置偏移,该偏移是相对的,具体表示请看一张图,页面静止时,以屏幕左边界为 0,屏幕内的页面 position 为0,左边为-1,依次递减,右侧为1,依次递增。当屏幕滑动时,page2只出现一半,此时,page2 的 position 为-0.5,page3 为0.5,依次类推可得出其他page 回调的 position 值

Page Transformer

实践

1、淡入淡出 效果


淡入淡出

页面随着位置改变透明度,alpha = 0 是透明,alpha = 1 是不透明

if (position <= -1.0f || position >= 1.0f) {
    page.alpha = 0.0f
} else if (position == 0.0f) {
    page.alpha = 1.0f
} else {
    page.alpha = 1.0f - Math.abs(position)
}

2、缩放变大效果


缩放变大

同时改变位移与透明度

if (position > 0 && position < 1) {
      page.alpha = 1 - position
      page.scaleXY = 0.85f + (1 - 0.85f) * (1 - Math.abs(position))
      page.translationX = page.width * -position
} else {
      page.alpha = 1f
      page.scaleXY = 1f
      page.translationX = 0f
}

更多效果

等你去发现

源码解析

其实这个原理很简单,在每一次滚动的时候,在 ViewPager 内部,计算出 每一个view 的 position ,并且调用这个接口的方法就可以实现了
源码如下

   protected void onPageScrolled(int position, float offset, int offsetPixels) {
        // 省略.......
        if (mPageTransformer != null) {
            final int scrollX = getScrollX();
            final int childCount = getChildCount();
            for (int i = 0; i < childCount; i++) {
                final View child = getChildAt(i);
                final LayoutParams lp = (LayoutParams) child.getLayoutParams();

                if (lp.isDecor) continue;
                final float transformPos = (float) (child.getLeft() - scrollX) / getClientWidth();
                mPageTransformer.transformPage(child, transformPos);
            }
        }
        // ....
    }

首先判断 mPageTransformer 是否存在,存在的话就可以调用了,获取 scrollX,根据 childCount 对每一个 view 执行 mPageTransformer.transformPage 方法 transformPos 是由 (float) (child.getLeft() - scrollX) / getClientWidth() 计算得出。此处使用 getLeft - scrollX 计算验证了我们对想法。

demo

总结

看似复杂的功能,其实没那么复杂,静下心来研究,原来这么简单

上一篇下一篇

猜你喜欢

热点阅读