在原生系统Launcher3中,滑动桌面,壁纸跟着滑动,查看一下源码,这部分代码比较简单,主要在Workspace类中,主要是WallpaperOffsetInterpolator内部类。这个类实现了Choreographer.FrameCallback类。
Choreographer是一个很重要的和UI绘制相关的类,我对这个类也不太了解,但网上相关资料很多。WallpaperOffsetInterpolator实现了Choreographer.FrameCallback的doFrame方法,查看注释Called when a new display frame is being rendered.再加上网上的资料即可大概了解这个方法的调用时机。doFrame方法调用了updateOffset方法,这个方法即是处理壁纸的位移的。
syncWithScroll方法也调用了updateOffset方法,Workspace的initWorkspace里创建了WallpaperOffsetInterpolator的对象,并在computeScroll方法调用了syncWithScroll方法。syncWithScroll方法里wallpaperOffsetForCurrentScroll()方法是计算屏幕滚动时壁纸的偏移量,其中的常量MIN_PARALLAX_PAGE_SPAN决定壁纸滑动至显示完整时的滚动次数,即屏幕的数量减一。当屏幕的数量减一少于MIN_PARALLAX_PAGE_SPAN时,桌面壁纸显示不完整。MIN_PARALLAX_PAGE_SPAN决定了桌面Workspace滚动时壁纸的最大偏移量。
随后调用的setFinalX方法set了壁纸当前滑动的目标值并根据Workspace屏幕数量的变化判断滚动屏幕时是否增减了屏幕数量,如果是的话调用animateToFinal方法把mAnimating设为true,在computeScrollOffset方法中以指定的Interpolator根据时间变化计算到最后偏移量每次移动的值,达成一个增减屏幕数量松手后壁纸渐渐偏移的效果。如果壁纸的数量没有变化则mAnimating为false,在computeScrollOffset方法中直接指定壁纸偏移量,根据屏幕滑动而偏移。
updateOffset方法中调用
mWallpaperManager.setWallpaperOffsets(mWindowToken,
mWallpaperOffset.getCurrX(), 0.5f);
...
mWallpaperManager.setWallpaperOffsetSteps(xOffset, 1.0f);
则是对壁纸进行了处理。
很多关闭或不支持循环翻页的launcher在滑到第一页继续向前滑动或滑到最后一页继续向后滑动时都有一个壁纸回弹的效果。通过观察可以发现:屏幕向后滑动到最后一页时,如果继续向后拖动屏幕,壁纸的滑动方向就向前滑动了,松手后弹回壁纸末端,向前滑动类似。如果launcher支持循环滚动,有一种壁纸的滑动方案是最后一页到第一页时,壁纸反向滑到最前,最后一页到第一页则反过来,这个的原理和弹回的是一样的。
在wallpaperOffsetForCurrentScroll()方法中根据滚动的距离scrollX进行处理:
if(mWallpaperOverScroll) {
int width = getChildWidth(firstIndex);
if (scrollX > mMaxScrollX && mCurrentPage == lastIndex) {
int offset = scrollX - mMaxScrollX;
scrollX = (int) (mMaxScrollX * (1 - ((float) offset)
/ width));
} else if (scrollX < 0 && mCurrentPage == firstIndex) {
scrollX = (int) (mMaxScrollX * ((float) (-scrollX)
/ width));
}
}