PageTransformer 源码解析

前言

作为一个很久没写过 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

总结

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342