微官网和移动网站区别,营销软文广告,建设部网站查询,提供零基础网站建设教学培训前言 前两天看到鲍永章分享的Great animations with PageTransformer以及农民伯伯分享的Create ViewPager transitions 文章#xff0c;都是通过ViewPager来实现酷炫的动画#xff0c;而现在的App中ViewPager的动画使用也非常的广泛。正好最近一直研究动画#xff0c;那么就… 前言 前两天看到鲍永章分享的Great animations with PageTransformer以及农民伯伯分享的Create ViewPager transitions 文章都是通过ViewPager来实现酷炫的动画而现在的App中ViewPager的动画使用也非常的广泛。正好最近一直研究动画那么就趁热打铁分析一下相关的开源库吧。 本篇文章介绍的ViewPager动画可以分为两类第一类是针对于ViewPager的界面滑动动画(这个是PageTransformer的真正用途)分析并比较了AndroidImageSlider和JazzyViewPager两种实现第二类是对ViewPager中的内容进行动画处理这个是这个是PageTransformer的巧妙应用处理好了可以达到很棒的交互效果示例是Yahoo天气的视差效果。 ViewPager动画的实现原理 从3.0开始ViewPager开始支持自定义切换动画暴露的接口为PageTransformer因此只要实现PageTransformer接口和其唯一的方法transformPage(View view, float position)即可。 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20/*** A PageTransformer is invoked whenever a visible/attached page is scrolled.* This offers an opportunity for the application to apply a custom transformation* to the page views using animation properties.** pAs property animation is only supported as of Android 3.0 and forward,* setting a PageTransformer on a ViewPager on earlier platform versions will* be ignored./p*/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.*/public void transformPage(View page, float position);}参数position 给定界面的位置相对于屏幕中心的偏移量。在用户滑动界面的时候是动态变化的。那么我们可以将position的值应用于setAlpha(), setTranslationX(), or setScaleY()方法从而实现自定义的动画效果。 另外在ViewPager滑动时内存中存活的Page都会执行transformPage方法在滑动过程中涉及到两个Page当前页和下一页而它们的position值是相反的因为是相对运动,一个滑入一个滑出比如页面A向右滑动到屏幕一半页面B也正好处于一半的位置那么A和B的position为0.5 和 -0.5 position 0 当前界面位于屏幕中心的时候 position 1 当前Page刚好滑出屏幕右侧 position -1 当前Page刚好滑出屏幕左侧 AndroidImageSlider动画库解析 要说到动画库肯定会想到代码家没错代码家也开源了一个ViewPager效果的库AndroidImageSlider 我们就来分析下这个库的实现。AndroidImageSlider除了基本的page动画外也支持用户为Page内容添加自定义的动画比如下面描述框的动画。 AndroidImageSlider兼容性的实现原理 因为API 11才开始支持PagerTransformer. 这里面修改了Android系统的ViewPager名为ViewPagerExViewPager里面有一段if逻辑判断是否在3.0以上使用PagerTransformer。因为PagerTransformer动画效果的实现依赖了PropertyViewAnim。但代码家的这个库使用NineOldAndroids实现了向3.0之前的兼容。因此就把这个if条件去掉了其它部分都没变。 1
2
3
4
5
6/*** author daimajia : I just remove the if condition in setPageTransformer() to make it compatiable with Android 2.0* of course, with the help of the NineOldDroid.* Thanks to JakeWharton.* http://github.com/JakeWharton/NineOldAndroids*/AndroidImageSlider的总体设计 BaseTransformer 所有Transformer的基类实现了ViewPagerEx.PageTransformer接口以及transformPage方法并提供了onPreTransform(View view, float position)、onPostTransform(View view, float position)、onTransform(View view, float position); 分别在transformPage前后调用用来处理为每一次的执行动画前的准备和结束动作比如还原所有的动画状态。 BaseAnimationInterface: ViewPagerEx执行Transformer动画的时候注入一些自己的动画。你需要实现该接口然后实现以下4个方法获取SlideView中的View实现自己的动画。比如底部的DescriptionText动画DescriptionAnimation 出现时的动画就是onNextItemAppear中添加的,你可以点入看下源码 onPrepareCurrentItemLeaveScreen(View current) onPrepareNextItemShowInScreen(View next) onCurrentItemDisappear(View view) onNextItemAppear(View view) 而这4个方法的调用是在BaseTransformer中。BaseTransformer统一管理了Page滑动时的所有动画。为了获取这4个方法的调用时机也是煞费苦心啊先说下思路 因为ViewPager滑动的时候transformPage方法是实时调用的这里获取最初两次调用时传入的position进行比较。通过一个HashMap来维护不同pageView的多个position。但这里为了优化只取前两个position做比较。 先判断起始滑动的方向然后再判断下一次滑动的方向两次结果作差来判断到底哪个page要离开界面还是进入界面。 确定2个界面的4种临界状态 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58if(mCustomAnimationInterface ! null){if(h.containsKey(view) false || h.get(view).size() 1){if(position -1 position 1){if(h.get(view) null){h.put(view,new ArrayListFloat());//为每个View创建一个List来存储偏移分数position}h.get(view).add(position);//向指定的View中添加偏移分数if(h.get(view).size() 2){float zero h.get(view).get(0);float cha h.get(view).get(1) - h.get(view).get(0);//当前滑动的位移偏移量分数差 newfraction - oldfractionif(zero 0){//起始时向左滑动当前Page中心位于屏幕中心左侧//判断滑动趋势如果继续向左侧滑动position从0到1越来越大 newfraction oldfractionif(cha -1 cha 0){//向右侧滑动//inmCustomAnimationInterface.onPrepareNextItemShowInScreen(view);//下一个Page将要进入屏幕}else{//cha 0 继续向左侧滑动//outmCustomAnimationInterface.onPrepareCurrentItemLeaveScreen(view);//当前Page将要离开屏幕}}else{//起始时向右滑动当前Page中心位于屏幕中心右侧//判断滑动趋势如果继续向右侧滑动position从0到-1越来越小 newfraction oldfractionif(cha -1 cha 0){//负值继续向右滑动因此当前page将要滑出屏幕//outmCustomAnimationInterface.onPrepareCurrentItemLeaveScreen(view);//当前Page将要离开屏幕}else{//向左滑动//inmCustomAnimationInterface.onPrepareNextItemShowInScreen(view);//下一个Page将要离开屏幕}}}}}}boolean isApp,isDis;/*** Called each {link #transformPage(View, float)} call after {link #onTransform(View, float)} is finished.** param view* param position*/protected void onPostTransform(View view, float position) {if(mCustomAnimationInterface ! null){if(position -1 || position 1){//当前界面刚好完全移除界面mCustomAnimationInterface.onCurrentItemDisappear(view);isApp true;}else if(position 0){mCustomAnimationInterface.onNextItemAppear(view);//下一个Page刚好完全显示isDis true;}if(isApp isDis){h.clear();isApp false;isDis false;}}}BaseSliderView 该类就是SlideView的基类持有SlideView的一些公共方法比如设置SliderView的image资源的方法image(File file)图片加载异常和失败的处理empty(URL)用于展示errorDisappear(boolean disappear)。这种思想和一个EmptyView有些类似在我公司的项目也是使用了这种方式比如通常一个ListView的界面会有一个包装后的progressbar包含各种情况的处理来显示进度当加载失败的时候或者无数据的时候可以调用该progressbar身上的方法去决定显示何种布局。使用起来很方便。当然如果你的SliderView更复杂你可以通过实现BaseSliderView,然后实现自己的SliderView。 下面两个类就是切换的View DefaultSliderView实现了BaseSliderView该View默认就是一张图片 TextSliderView带有图片和描述性文字的View Sliderlayout: 一个控制中心将InfiniteViewPagerPageIndicator粘合在一起控制动画的样式ViewPager轮循的播放。 addSlider:向ViewPager中添加一个SlideView startAutoCycle启动轮播 pauseAutoCycle停止轮播 setDuration 轮播间隔 setPagerTransformer 为ViewPager设置自定义的PageTransformer onInterceptTouchEvent 在用户手势按下的时候就停止轮循 Note: 这里在onInterceptTouchEvent中处理而不能再onTouchEvent中处理。因为SlideView会消费掉点击事件事件被消费了没法返回给SlideLayout的onTouchEvent中。 OK总体设计弄清楚后剩下来就是动画中最核心的部分了就是动画效果的实现,这里只简单的介绍Accordion动画的实现其它的大家可以自己分析。关于PropertyView动画的使用可以参考我的另一篇文章PropertyAnim实际应用 Accordion 1
2
3
4
5
6
7
8
9public class AccordionTransformer extends BaseTransformer {Overrideprotected void onTransform(View view, float position) {ViewHelper.setPivotX(view,position 0 ? 0 : view.getWidth());ViewHelper.setScaleX(view,position 0 ? 1f position : 1f - position);}}代码家在README中注明了Thanks JazzyViewPager开始没注意后来看JazzyViewPager的源码时候偶然发现AndroidImageSlider的一些实现的灵感是来自JazzyViewPager项目。不过JazzyViewPager的实现方式略显复杂没有使用PageTransformer接口而是使用了OnPageChangeListener接口的onPageScrolled方法。 下面简单的看下JazzyViewPager库的动画实现它将positionOffset作为参数控制。由于positionOffset的值为[0,1),所以就需要分别处理正负的情况。另外JazzyViewPager是通过维护一个LinkedHashMap来持有Page的引用。在Adapter添加界面的时候会调用JazzyViewPager的 setObjectForPosition(Object obj, int position) 方法存入到集合中去。然后在onPageScrolled方法中再根据position获取当前Page的前一个mLeft和后一个mRight界面分别对前后两个界面添加动画。 两种方式的关键参数区别 onPageScrolled(int position, float positionOffset, int positionOffsetPixels)方法的参数 1
2
3
4
5
6
7
8
9
10
11/*** This method will be invoked when the current page is scrolled, either as part* of a programmatically initiated smooth scroll or a user initiated touch scroll.** param position Position index of the first page currently being displayed.* Page position1 will be visible if positionOffset is nonzero.* param positionOffset Value from [0, 1) indicating the offset from the page at position.* param positionOffsetPixels Value in pixels indicating the offset from position.*/public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels);下面是打印的PageTransformer接口的transformPage时position的值相邻两界面的绝对值的和为1.而正负则表示了滑入和滑出状态 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
1812-15 21:03:13.025 I/System.out﹕ transformPage---------------------- -0.4361111212-15 21:03:13.025 I/System.out﹕ transformPage---------------------- 0.563888912-15 21:03:13.045 I/System.out﹕ transformPage---------------------- -0.3805555712-15 21:03:13.045 I/System.out﹕ transformPage---------------------- 0.6194444312-15 21:03:13.045 I/System.out﹕ transformPage---------------------- -0.3777777912-15 21:03:13.045 I/System.out﹕ transformPage---------------------- 0.6222222412-15 21:03:13.055 I/System.out﹕ transformPage---------------------- -0.3291666812-15 21:03:13.055 I/System.out﹕ transformPage---------------------- 0.6708333512-15 21:03:13.065 I/System.out﹕ transformPage---------------------- -0.3222222312-15 21:03:13.065 I/System.out﹕ transformPage---------------------- 0.6777777712-15 21:03:13.075 I/System.out﹕ transformPage---------------------- -0.2791666712-15 21:03:13.075 I/System.out﹕ transformPage---------------------- 0.72083336onPageScrolled方法的positionOffset的值为 [0, 1) 而PageTransformer接口的transformPage(View page, float position) 已经标好了正负值滑入和滑出如果你的动画正好是相对的那么用transfromPage就简单的多。 我们取同一种动画效果Cube来比较一下JazzyViewPager和AndroidImageSlider两者的实现: JazzyViewPager部分源码 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61Overridepublic void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {...float effectOffset isSmall(positionOffset) ? 0 : positionOffset;mLeft findViewFromObject(position);mRight findViewFromObject(position1);switch (mEffect) {case Standard:break;...case animateCube:animateCube(mLeft, mRight, effectOffset);break;}}...private void animateCube(View left, View right, float positionOffset, boolean in) {if (mState ! State.IDLE) {if (left ! null) {manageLayer(left, true);mRot (in ? 90.0f : -90.0f) * positionOffset;ViewHelper.setPivotX(left, left.getMeasuredWidth());ViewHelper.setPivotY(left, left.getMeasuredHeight()*0.5f);ViewHelper.setRotationY(left, mRot);}if (right ! null) {manageLayer(right, true);mRot -(in ? 90.0f : -90.0f) * (1-positionOffset);ViewHelper.setPivotX(right, 0);ViewHelper.setPivotY(right, right.getMeasuredHeight()*0.5f);ViewHelper.setRotationY(right, mRot);}}}public void setObjectForPosition(Object obj, int position) {//每次Adapter实例化View的时候调用该方法将实例化的View存入集合mObjs.put(Integer.valueOf(position), obj);}public View findViewFromObject(int position) {//根据position从集合中获取到对应的ViewObject o mObjs.get(Integer.valueOf(position));if (o null) {return null;}PagerAdapter a getAdapter();View v;for (int i 0; i getChildCount(); i) {v getChildAt(i);if (a.isViewFromObject(v, o))return v;}return null;}....}AndroidIamgeSlider中的CubeIn: 1
2
3
4
5
6
7
8
9
10public class CubeInTransformer extends BaseTransformer {Overrideprotected void onTransform(View view, float position) {// Rotate the fragment on the left or right edgeViewHelper.setPivotX(view,position 0 ? 0 : view.getWidth());ViewHelper.setPivotY(view,0);ViewHelper.setRotation(view,-90f * position);}
}JazzyViewPagerCubeIn上面代码中已经贴出 1
2
3
4
5
6
7
8
9if (left ! null) {...mRot (in ? 90.0f : -90.0f) * positionOffset;...}if (right ! null) {mRot -(in ? 90.0f : -90.0f) * (1-positionOffset);
}可以精简为 1
2positionOffset * value
-(1-positionOffset) * value与从上面的transformPage打印的position的值再比较下 1
212-15 21:03:13.075 I/System.out﹕ transformPage---------------------- -0.2791666712-15 21:03:13.075 I/System.out﹕ transformPage---------------------- 0.72083336结论 发现transformPage已经帮我们处理好了一切直接用。我们可以根据正负符号来判断滑入和滑出的View不用像JazzyViewPager那样去维护一个集合了从而针对滑入滑出做出不同或相对的的动画。最简单的两个界面如果是简单的相对动画滑入对滑出则什么都不用处理直接用就行了就像下面介绍的Yahoo视差的实现一样具体可以看下面的讲解。 大家仔细看下相同的动画效果的处理方式CubeInTransformer不用单独处理上一个和下一个界面而只管根据position的正负判断当前Page和下一个Page去自定义不同的动画即可。 所以可以把AndroidImageSlider中的一些动画效果看做是JazzyViewPager的精简版本。 对于ViewPager如何帮我们处理View的呢可以看下源码 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19protected 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);}}
...}小结 上面主要介绍了ViewPager的Page的滑动动画两种实现方式 PageTransformer.transformPage方式在执行onPageScrolled方法的时候会遍历ViewPager的所有View并执行其transformPage方法。position是已经处理好的方向和值 onPageScrolled方式略显复杂因为没法拿到View还要自己去维护一个View集合并且positionOffset的限制需要自己去处理不同View的position : PageAposition , PageB-(1-position)。 为ViewPager的Page内容添加动画实现炫酷的交互效果 Tholotis 实现原理 为Page内部的View处理不同的平移速度达到视差的效果,作者的代码 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36public void transformPage(View view, float position) {int pageWidth view.getWidth();if (position -1) { // [-Infinity,-1)// This page is way off-screen to the left.view.setAlpha(0);} else if (position 1) { // [-1,1]mBlur.setTranslationX((float) (-(1 - position) * 0.5 * pageWidth));mBlurLabel.setTranslationX((float) (-(1 - position) * 0.5 * pageWidth));mDim.setTranslationX((float) (-(1 - position) * pageWidth));mDimLabel.setTranslationX((float) (-(1 - position) * pageWidth));mCheck.setTranslationX((float) (-(1 - position) * 1.5 * pageWidth));mDoneButton.setTranslationX((float) (-(1 - position) * 1.7 * pageWidth)); // The 0.5, 1.5, 1.7 values you see here are what makes the view move in a different speed.// The bigger the number, the faster the view will translate.// The result float is preceded by a minus because the views travel in the opposite direction of the movement.mFirstColor.setTranslationX((position) * (pageWidth / 4));mSecondColor.setTranslationX((position) * (pageWidth / 1));mTint.setTranslationX((position) * (pageWidth / 2));mDesaturate.setTranslationX((position) * (pageWidth / 1));// This is another way to do it} else { // (1,Infinity]// This page is way off-screen to the right.view.setAlpha(0);}
}GitHub上类似的效果还有 https://github.com/prolificinteractive/ParallaxPager https://github.com/flavienlaurent/discrollview 这两个项目都封装成了库使用的时候也简单但一般我们可以通过ViewPager.PageTransformer来实现这样的效果从上面AndroidImageSlider与JazzyViewPager的对比中也能看到PageTransformer的实现方式更简单。而且ViewPager的动画库也很多比如上面的JazzyViewPager。你只需把某一个动画效果的transformPage方法的逻辑拿来就可用当然如果你的需求更复杂或者ViewPager实现起来较麻烦你可以考虑上面的两个项目扩展自己的思维。 JazzyViewPager: https://github.com/jfeinstein10/JazzyViewPager/blob/master/lib/src/com/jfeinstein/jazzyviewpager/JazzyViewPager.java https://github.com/prolificinteractive/ParallaxPager https://github.com/flavienlaurent/discrollview Yahoo天气 原理很简单只是这次处理的动画对象是背景图片减慢其平移的速度而ViewPager的内容正常移动从而达到视差的效果。 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23public class ParallaxPageTransformer implements ViewPager.PageTransformer {public void transformPage(View view, float position) {int pageWidth view.getWidth();if (position -1) { // [-Infinity,-1)// This page is way off-screen to the left.view.setAlpha(1);} else if (position 1) { // [-1,1]dummyImageView.setTranslationX(-position * (pageWidth / 2)); //Half the normal speed} else { // (1,Infinity]// This page is way off-screen to the right.view.setAlpha(1);}}}OK本来想自己写但刚巧在GitHub上发现了一个ParallaxPagerTransformer 并提供了APK 实现了Yahoo天气的效果但多了一些缩放的效果将缩放的代码注释掉就和Yahoo天气的效果完全一样了。另外想clone到本地的朋友注意了由于作者的AndroidStudio版本太老导入的时候你需要做一些处理。 作者把该Transformer抽取出来做为了一个lib就一个类核心代码很简单说明ViewPager.PageTransformer接口很强大啊 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20Overridepublic void transformPage(View view, float position) {View parallaxView view.findViewById(id);if (parallaxView ! null) {if (position -1 position 1) {float width parallaxView.getWidth();parallaxView.setTranslationX(-(position * width * speed));float sc ((float)view.getWidth() - border)/ view.getWidth();if (position 0) {//这里处理了缩放的效果去掉即和Yahoo天气的效果一样view.setScaleX(1);view.setScaleY(1);} else {view.setScaleX(sc);view.setScaleY(sc);}}}}看了上面的实现大家应该知道如何去实现类似的交互动画了而且现在的App导航页中也越来越多的使用到了这样的交互作者在文章还列举了News Digest的导航页,有了思路实现起来应该就不难了第一、三页的动画上面已经介绍过对于第二页其实就是一个旋转动画。实时的旋转可以通过实时变化的position来处理。 类似的项目 https://github.com/andraskindler/parallaxviewpager 总结 整篇文章介绍的东西其实很简单只是以前没有研究过ViewPager.PageTransformer,使用简单但功能强大position参数在动画处理中相当重要因为是实时的百分比所以省去了自己不少计算如果你需要为ViewPager自定义动画那么选择PageTransformer对于本文的第二类巧妙动画效果的介绍看具体情况决定是否使用PageTransformer如果position能很方便帮助动画的计算那是最好的如果不是那么你完全可以使用onPageScrolled来处理源码中也可看到PageTransformer就是在onPageScroll中调用的。 使用时要注意它只支持3.0以上的系统。如果要兼容可以参考上面AndroidImageSlider的兼容实现。在文章中也插入了不少ViewPager动画项目的链接也许不是使用PageTransformer但也是一种思路的扩展供大家参考。下一篇文章我会介绍Fragment的动画大家敬请期待。 参考文献 官方文档 Great animations with PageTransformer Create ViewPager transitions GitHub上相关的ViewPager动画的项目 https://github.com/daimajia/AndroidImageSlider https://github.com/inovex/ViewPager3D 轮循的ViewPager https://github.com/antonyt/InfiniteViewPager https://github.com/JakeWharton/salvage https://github.com/Trinea/android-auto-scroll-view-pager VerticalViewPager https://github.com/JakeWharton/Android-DirectionalViewPager https://github.com/LambergaR/VerticalViewPager https://github.com/VenomVendor/AutoNotifyViewPager https://github.com/Dreddik/AndroidTouchGallery 特效的ViewPager https://github.com/kmshack/Android-ParallaxHeaderViewPager https://github.com/andraskindler/parallaxviewpager https://github.com/MoshDev/BackgroundViewPager 与ViewPager一起使用的导航 https://github.com/astuetz/PagerSlidingTabStrip 不支持TextView颜色的变化 https://github.com/jpardogo/PagerSlidingTabStrip (支持TextView颜色变化) https://github.com/DSofter/SmoothTabIndicator FragmentAnim https://github.com/DesarrolloAntonio/FragmentTransactionExtended #Anim #ViewPager