转载

类似 Launcher 壁纸的视差背景效果

1
概述

相信大家在切换手机launcher图标的时候,都会注意到后面的壁纸,仿佛是一张很大的壁纸在后面,每次切换图标页,壁纸会有一定的视差滚动效果。

那么这样一个效果是如何制作的呢?

那么本文的主要目的就是介绍这样的一个效果的制作原理。

首先看下效果图:

类似 Launcher 壁纸的视差背景效果

恩,其实这个是我在为 wanandroid.com 收录文章的时候发现的开源项目,项目地址是:

  • https://github.com/zhe525069676/BgMoveViewPager

当然本文不是造轮子,而是我觉得这样的效果有一定的实用价值,特地写出来分享下。

类似的库还有:

  • https://github.com/andraskindler/parallaxviewpager

如果大家有兴趣,建议大家在阅读完本文后,可以去看看上述两个项目的源码。

最后,需要说明本文的代码与上述库代码有一定的出入。

2
思路

其实写起来还是蛮简单的,只是如果找不到切入点就会觉得毫无头绪。

值得开心的是,在阅读完本文后,遇到类似的效果,你可能会会心一笑。

首先我们考虑下思路:

对于控件的选择,肯定首选ViewPager了,对于这样一个效果,仅仅将图片设置为ViewPager的背景是肯定不行的。

通过效果图,可以看得出来,图片可以伴随控件的移动,能够进行同步的移动。

所谓同步的移动,就是随着控件的scorllX的改变,绘制图片的区域会发生变化,那么我们在ViewPager滑动的过程中,我们需要找到一个方法,这个方法可以全程监听滑动,并且能够提供一定的参数帮助我们去定位图片该显示的区域。

2.1 思路一

这样的话,我们好像有思路了,我们可以为ViewPager设置setOnPageChangeListener,然后在onPageScrolled方法中,根据position和positionOffset的值,就可以确定图片需要显示的位置了,然后调用invalidate,去绘制指定的区域即可。

这样的确是可以的, parallaxviewpager 就是这么做的,有兴趣的可以去看下代码。

除此以外还有别的思路。

2.2 思路二

既然ViewPager在滑动的时候,会不断的改变scrollX,那么我们可以直接在onDraw或者dispatchDraw中,去读取scrollX,根据scrollX确定需要绘制图像的区域,直接使用canvas绘制出来即可。

该思路为今天的代码主要演示的,对于onDraw和dispatchDraw,由于我们是ViewGroup,我们可以选择dispatchDraw来实现;当然,onDraw也是可以的,注意如果有必要记得调用setWillNotDraw(false)。

这样的话思路就明确了,我们重写dispatchDraw,再其内部根据scrollX区计算需要显示的区域,然后就想绘制,那么下面看代码。

3
实现
3.1 初步实现

类似 Launcher 壁纸的视差背景效果

是不是感觉代码很短~

的确不长,为了方便,我们直接在类中加载了作为背景的图。

可以看到我们首先拿到图片的宽高,然后除以item的个数,确定每个item可以显示的图片宽度 widthForItem

然后根据scrollX,比如初始在第1页(currentItem=0),scrollX为0,那么绘制的图片宽度区域就为:(0,widthForItem).

随着scrollX的变化,那么绘制的区域随之变为

(scrollX * widthForPerPx,  scrollX * widthForPerPx + widthForItem)

widthForPerPx 的意思是,当控件移动一个像素,图片需要移动的像素值。

确定了需要绘制的图片区域,那么绘制的目标宽度区域,相比就很简单了,肯定是:

(scrollX , scrollX + getWidth());

认真分析完代码,感觉没什么问题,赶紧跑起来,效果已经实现了。

但是,不要高兴的太早,代码里面有个潜在的问题,不知道大家发现没有。

3.2 处理存在的问题

上述代码非常依赖scrollX,我们预期scrollX是从:

0 -> 控件width -> 控件width*2 这样变化。

但是,假设我们默认ViewPager设置为第2页(currentItem=1),你会发现效果图极其奇怪。

类似 Launcher 壁纸的视差背景效果

造成这样的原因是什么呢?

其实是因为,当currentItem=1时,scrollX的变化为:

-控件width -> 0 -> 控件width

当currentItem=2时,scrollX的变化为:

-控件width * 2 -> -控件width -> 0

而我们上述代码一直是以scrollX从0到最大来计算的。

所以,我们这里需要对scrollX进行处理:

  • 怎么处理呢?

if分类判断么,那太夸张了,页面一多得写多少if呀。

如果你对数字比较敏感,应该可以看出,上述虽然currentItem不同,但是变化的规律还是很明显的,我们可以总结规律,将其统一转化为:

0->控件width->控件width*2

那么就简单了,首先我们得到currentItem的值,然后让其乘以控件宽度,再加上scrollX,即:

int x = getScrollX() + mFirstPos * getWidth();

相关于给原本的变化统一加上了mFirstPos * getWidth(),看看上面的变化,恰好完成我们的转化需求。

那么最后完善下代码:

类似 Launcher 壁纸的视差背景效果

这样就基本完成所有的代码,当然很多细节的地方需要你自己去完善。

不过从该例,可以发现针对一个问题,找到切入点,先从最简单的情况出发,编写完成后,在针对各种情况做调整即可。

4
扩展

最后,我们根据上例效果,可以做的事情还很多,假设我们有一张宽度大概为1.6倍屏幕宽度的图片,ViewPager包含3个Item。

均分为3等分拉伸效果肯定不好,其实你可以不拉伸,让当前屏幕显示同等宽度的图片,每切换一页,你图片移动总宽度为屏幕宽度的0.3即可,相信根据上文的代码你应该可以做出来。

5
写在最后

昨天的推送文章被指出太简单,有人发出类似疑问:

你是没有东西写了么?能不能发点干货?

这里我简单解释下,首先我感谢提出的意见,相比大家关注我的公众号肯定都是为了能够学到一些东西的,当然不排除那些为了和我搞基的。

为了这样一个目标,我也保持着每周3-5篇,尽可能推送 `看起来不太费劲` 又能 `学到知识` 的文章。

需要抱歉的是,我目前没有任何办法做到每天的文章都能符合所有人的口味,所以我希望如果文章对你来说太简单,你可以选择不看或者简单的看一下,权当巩固下记忆也好。

过分的抱怨会伤害到我以及原创作者,毕竟一篇文章,能够从作者写出来到推送到大家的手机上消耗了大量的时间与精力。并且个人长期的坚持分享与大家的支持是分不开的。

你可以试想下,你有个花了了大量时间与精力做的东西,给别人分享时,被别人泼冷水的时感受。

所以有可能的话,尽量可以:不要吐槽,随手点赞,给作者一点支持和鼓励。

当然,合理的意见是没问题的,比如你可以换个语气:

最近的文章好像有点偏简单,我最近在研究 长得太帅的人如何与他人相处 ,可否推送相关文章?

我会根据大家的留言,对文章的选择有个大致的方向,并且我可以承诺,所有的文章我都有细心的编辑,在我所能做到的情况下,保证大家有个良好的阅读体验。

最后衷心的感谢投稿的作者、长期支持我的你们、默默关注我的你们以及给我提建议的朋友。

源码地址:

https://github.com/hongyangAndroid/Android_Blog_Demos

类似 Launcher 壁纸的视差背景效果

原文  http://mp.weixin.qq.com/s?__biz=MzAxMTI4MTkwNQ==&mid=2650820494&idx=1&sn=1af3e5bd98e347ce9bde72f69d3802eb&scene=0
正文到此结束
Loading...