

写了个视差滚动布局 ParallaxLayout
source link: https://antiless.com/write-a-parallax-layout/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

新项目用到了大量的视差滚动效果,今天写了控件做支持,并非成熟但适用大多数简单场景,把大致思路在这里写下。
什么是视差滚动, 简单说就是
不同组件按不同速度滚动
像这种效果:

我们今天介绍的主要是以纵向的滚动效果为主,当然如果你要实现横向思路也一样。
1. 思路
简单实现这个效果思路很简单,只需要监听滚动控件的滚动行为,再根据不同组件的速度要求调整偏移量就:accept:️了。
要做的通用点,我的思路是实现一个视差滚动的父控件,子 view
添加一个 layout_parallax_speed
的属性,这样无需多写 java
代码,就可以得到所需要的参数,在布局代码中就可以完成定义。
最后的调用类似这种效果:
<ScrollView> <ParallaxLayout> <TextView layout_parallax_speed="1"/> <ImageView layout_parallax_speed="1.5"/> <LinearLayout layout_parallax_speed="0.8"/> <AnyView layout_parallax_speed="0.7"/> <AnyView layout_parallax_speed="1"/> </ParallaxLayout> </ScrollView>
其实这里要注意,我们的布局是介于 ScrollView
和视差组件之间的一个布局,因此它可能是 LinearLayout
可能是 RelativeLayout
也可能是 ConstraintLayout
, 可能是任意一个 ViewGroup
的子类,因为我也不知道开发者需要一个什么样的内部结构。
再一点,这个布局本身不具备 Scroll
能力,仍需要嵌套在外部的可滚动组件。
可能你有疑问,为什么不直接写一个 ParallaxScrollView
之类的呢。
这样就不需要担心内部结构的问题,不需要写多种布局的子类,自身也能控制滚动行为。
也就是类似下面这种结构的:
<ParallaxScrollView> <LinearLayout> <TextView layout_parallax_speed="1"/> <ImageView layout_parallax_speed="1.5"/> <LinearLayout layout_parallax_speed="0.8"/> <AnyView layout_parallax_speed="0.7"/> <AnyView layout_parallax_speed="1"/> </LinearLayout> </ParallaxScrollView>
原因也很简单,
Android
的 ViewGroup
没有跨越父子关系设置 LayoutParam
的能力(只能父子间,不能爷孙间),也就是说,这种结构上,最外侧的 ParallaxScrollView
不能以正常方式获取到最内层的 layout_parallax_speed
属性。
2. 实现
需要解决两个问题
- 定义并获取各子控件的速度属性 layout_parallax_speed
- 检测滚动,对子控件做相对移动
2.2 定义并获取子控件布局属性
这里先介绍一下自定义 ViewGroup
,定义和获取子 View
布局参数的流程。
我们知道 xml
中定义的这些属性都会转换成 AttributeSet
, 然后在代码中保存在 LayoutParam
里。
然后会在 addView
方法时, 然后把它和 view
联系在一起。
ViewGroup 添加 View 流程图:

因此我们要做的 三件事 是:
- 定义一个
layout_parallax_speed
属性 - 定义一个
LayoutParam
,添加一个成员变量parallaxSpeed
用来保存 1 中定义的值 - 默认为子View 生成我们定义的
LayoutParams
相对的, 我们的流程图 应该是:
- 定义一个
layout_parallax_speed
属性
<declare-styleable name="ParallaxLayout"> <attr name="layout_parallax_speed" format="float"/> </declare-styleable>
- 定义一个
LayoutParam
,解析layout_parallax_speed
值并保存
class LayoutParams : RelativeLayout.LayoutParams { var parallaxSpeed: Float = 1f constructor(context: Context, attrs: AttributeSet?): super(context, attrs) { val a: TypedArray = context.obtainStyledAttributes(attrs, R.styleable.ParallaxLayout) parallaxSpeed = a.getFloat(R.styleable.ParallaxLayout_layout_parallax_speed, 1f) a.recycle() } constructor(width: Int, height: Int): super(width, height) constructor(layoutParams: MarginLayoutParams): super(layoutParams) constructor(layoutParams: LayoutParams): super(layoutParams) }
- 重写
generateLayoutParams
, 默认为子 View 生成我们定义的LayoutParams
override fun generateLayoutParams(attrs: AttributeSet?): RelativeLayout.LayoutParams { return LayoutParams(context, attrs) }
2.2 检测滚动
在添加到窗口时,对可滚动的父布局添加 addOnScrollChangedListener
方法
// TODO 添加关键代码
override fun onAttachedToWindow() { super.onAttachedToWindow() if (parent != null && parent is ScrollView) { (parent as ViewGroup).viewTreeObserver.addOnScrollChangedListener(this) } } override fun onDetachedFromWindow() { super.onDetachedFromWindow() if (parent != null && parent is ScrollView) { (parent as ScrollView).viewTreeObserver.removeOnScrollChangedListener(this) } }
如果你的最低API在24之上,那你就不需要用 viewTreeObserver
这么暴力的东西,可以直接 parent.addOnScrollChangedListener
在 onScrollChanged
事件中获取偏移量,计算各子 View
的相对偏移值
/** * parent 的 onScrollChanged 事件 * 父亲滚动改变时,根据每个元素的滚动速度进行调整 view.translationY */ private var parentLastScrollY = 0 override fun onScrollChanged() { val currentScrollY = (parent as ViewGroup).scrollY val delta = currentScrollY - parentLastScrollY for (child in parallaxChildren) { val translationDelta = -delta * (child.speed - 1f) child.view.translationY += translationDelta.toInt() } parentLastScrollY = currentScrollY }
3. 总结
最后再贴一遍整体流程图:
** 开头的部分是我们要做的工作
Recommend
-
122
简介在天猫消费升级、品质升级的大背景下,一个交互体验优秀的APP变得尤为重要。而首页的首焦Banner更应该精雕细琢,因此我们挖掘了体验上的小创新,希望能给用户眼前一亮的体验小惊喜。我们实现了多层基于不同位移系数,接收重力感应及手势滑动的banner,从而达到...
-
61
何为滚动视差 视差滚动(Parallax Scrolling)是指让多层背景以不同的速度移动,形成立体的运动效果,带来非常出色的视觉体验。 作为网页设计的热点趋势,越来越多的网站应用了这项技术。 通常而言,滚动视差在前端需要辅助 Javascript 才能实
-
75
何为滚动视差 视差滚动(Parallax Scrolling)是指让多层背景以不同的速度移动,形成立体的运动效果,带来非常出色的视觉体验。 作为网页设计的热点趋势,越来越多的网站应用了这项技术。 通常
-
37
翻译对照 原文: PART 1 ・ PART 2 ・ PART 3 ・
-
39
翻译对照 原文: PART 1 ・ PART 2 ・ PART 3 ・
-
48
某个月黑风高的夜晚,前端小白菜收到一个任务:做一张像王者荣耀登录界面一样的图片。 对面的猪八戒也太讨厌了! --沉迷游戏两小时以后,小白菜如是说到。 回到正题,让我们先上效果图,当当当: 是不是看起来很简单?其实,真的,很简单! 接下来,是手摸手教学时...
-
18
基于MAP-MRF的视差估计 ...
-
19
双目立体视觉 II:块匹配视差图计算 ...
-
5
小tip: 纯CSS实现视差滚动效果 浏览:2147次 出处信息 一、效果Demo先行~...
-
6
使用 Pixi.js 构建一个视差滚动器(第三篇) 2019-3-19 19:20 PM ·
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK