美文网首页工作生活
PageTransformer 源码解析

PageTransformer 源码解析

作者: 没有颜色的菜 | 来源:发表于2019-07-05 20:35 被阅读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

    总结

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

    相关文章

      网友评论

        本文标题:PageTransformer 源码解析

        本文链接:https://www.haomeiwen.com/subject/iulbcctx.html