引导动画
引导动画
项目地址:Github
动画效果:
实现原理分析
清单文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22<manifest package="cn.lry.animation"
xmlns:android="http://schemas.android.com/apk/res/android">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity
android:name=".guide.MainActivity"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".guide.GuideActivity"/>
</application>
</manifest>主要分为连个页面,一个是墨迹天气的引导页面,就是GIF中涨势的效果,另外一个也是差不多的动画效果的只不过是左右滑动的引导页面
主页面布局MainActivity,主页面主要有一个viewpager构成,滑动方式为vertical
1
2
3
4
5
6
7
8
9
10
11
12
13
14<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<cn.lry.animation.viewpager.ViewPager
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:orientation="vertical"/>
</LinearLayout>ViewPager的初始化,通过xml文件来初始化每个页面的视图,然后通过pagers.add(view1)添加到ViewPager中,总共有四个
1
2
3
4
5
6
7View view1 = LayoutInflater.from(this).inflate(R.layout.layout_tutorial_1, null);
t1_icon1= (ImageView) view1.findViewById(R.id.t1_icon1);
t1_icon2 = (ImageView) view1.findViewById(R.id.t1_icon2);
t1_fixed = (ImageView) view1.findViewById(R.id.t1_fixed);
t1_next = (ImageView) view1.findViewById(R.id.t1_next);
pagers.add(view1);
.........四个类似的页面我们看其中一个页面的布局R.layout.layout_tutorial_1,也就是将页面上的几个图片分别用ImageView来进行布局分布。每个图片都是一个png的图片
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
61
62
63
64
65
66<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="@android:color/white"
android:gravity="center"
android:orientation="vertical" >
<ImageView
android:id="@+id/t1_fixed"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="15.0dip"
android:src="@mipmap/tutorial1_fixed" />
<RelativeLayout
android:id="@+id/center_layout"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_centerInParent="true" >
<ImageView
android:id="@+id/t1_icon1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="180.0dip"
android:layout_marginTop="53.0dip"
android:background="@drawable/t1_frame_animation" />
<ImageView
android:id="@+id/t1_icon2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/t1_icon1"
android:layout_centerHorizontal="true"
android:layout_marginTop="30.0dip"
android:src="@mipmap/tutorial1_icon3"
android:visibility="visible" />
<ImageView
android:id="@+id/t1_static"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:src="@mipmap/tutorial1_static"
android:visibility="visible" />
<ImageView
android:id="@+id/t1_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/t1_static"
android:layout_centerHorizontal="true"
android:layout_marginTop="10.0dip"
android:src="@mipmap/tutorial1_text"
android:visibility="visible" />
</RelativeLayout>
<ImageView
android:id="@+id/t1_next"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20.0dip"
android:src="@mipmap/tutorial_next" />
</LinearLayout>
动画的实现主要靠private void animal(int position) 方法,在页面切换的时候,去启动或者禁用视图控件的动画
每个部分的动画实现
第一页分析
极低耗电 这几个字的放大缩小动画
极低耗电为一个图片 t1_fixed = (ImageView) view1.findViewById(R.id.t1_fixed);
1
2
3
4
5
6<ImageView
android:id="@+id/t1_fixed"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="15.0dip"
android:src="@mipmap/tutorial1_fixed" />顶部动画加载
1
animationTop = AnimationUtils.loadAnimation(MainActivity.this, R.anim.tutorail_scalate_top);
启动动画
1
t1_fixed.startAnimation(animationTop);
主要分析这个动画的描述R.anim.tutorail_scalate_top,主要定义了两个动画效果scale,放大缩小,动画时长1000ms,x,y从1.0变为1.5倍大,重复模式reverse为倒序回复,如果设置为restart的话,表示从头回放,repeatcount负一表示无限循环;另外一个是alpha,时间也是1000,这样就是一边放大缩小,一边透明度渐变,透明度从1变为0.5
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
61
62
63<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<scale
android:duration="1000"
android:fromXScale="1.0"
android:fromYScale="1.0"
android:interpolator="@android:anim/decelerate_interpolator"
android:pivotX="50%"
android:pivotY="50%"
android:toXScale="1.5"
android:toYScale="1.5"
android:repeatMode="reverse"
android:repeatCount="-1"/>
<alpha
android:fromAlpha="1.0"
android:toAlpha="0.5"
android:duration="1000"
android:repeatMode="reverse"
android:repeatCount="-1"/>
</set>
<!-- 尺寸伸缩动画效果 scale
属性:interpolator 指定一个动画的插入器
常见动画插入器:
accelerate_decelerate_interpolator 加速-减速 动画插入器
accelerate_interpolator 加速-动画插入器
decelerate_interpolator 减速- 动画插入器
anticipate_interpolator 先回退一小步然后加速前进
anticipate_overshoot_interpolator 在上一个基础上超出终点一小步再回到终点
bounce_interpolator 最后阶段弹球效果
cycle_interpolator 周期运动
linear_interpolator 匀速
overshoot_interpolator 快速到达终点并超出一小步最后回到终点
浮点型值:
fromXScale 属性为动画起始时 X坐标上的伸缩尺寸
toXScale 属性为动画结束时 X坐标上的伸缩尺寸
fromYScale 属性为动画起始时Y坐标上的伸缩尺寸
toYScale 属性为动画结束时Y坐标上的伸缩尺寸
说明:
以上四种属性值
0.0表示收缩到没有
1.0表示正常无伸缩
值小于1.0表示收缩
值大于1.0表示放大
pivotX 属性为动画相对于物件的X坐标的开始位置
pivotY 属性为动画相对于物件的Y坐标的开始位置
说明:
以上两个属性值 从0%-100%中取值
50%为物件的X或Y方向坐标上的中点位置
长整型值:
duration 属性为动画持续时间
说明: 时间以毫秒为单位
布尔型值:
fillAfter 属性 当设置为true ,该动画转化在动画结束后被应用
-->中间电池图标的旋转动画
电池图标
1
2
3
4
5
6
7
8
9<ImageView
android:id="@+id/t1_icon2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/t1_icon1"
android:layout_centerHorizontal="true"
android:layout_marginTop="30.0dip"
android:src="@mipmap/tutorial1_icon3"
android:visibility="visible" />加载并开始动画
1
2
3
4
5
6Animation animation1 = AnimationUtils.loadAnimation(MainActivity.this, R.anim.tutorail_rotate);
LinearInterpolator lin = new LinearInterpolator();
animation1.setInterpolator(lin);
t1_icon2.setVisibility(View.VISIBLE);
t1_icon2.startAnimation(animation1);关于旋转动画的描述,刚开始的时候,电池图标是从无到有的一个从小到大的scale,android:fromXScale=”0.0”到1.2,然后就结束了,同时会无限的旋转,rotate,从0度到359度旋转,时间为3000ms,pivotX,Y 50%表示从以中心点旋转
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<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android" >
<scale
android:duration="800"
android:fillAfter="false"
android:fromXScale="0.0"
android:fromYScale="0.0"
android:interpolator="@android:anim/accelerate_decelerate_interpolator"
android:pivotX="50%"
android:pivotY="50%"
android:toXScale="1.2"
android:toYScale="1.2" />
<rotate
android:duration="3000"
android:fromDegrees="0"
android:pivotX="50%"
android:pivotY="50%"
android:repeatCount="-1"
android:toDegrees="359.0" />
<!-- rotate 旋转动画效果
属性:interpolator 指定一个动画的插入器
浮点数型值:
fromDegrees 属性为动画起始时物件的角度
toDegrees 属性为动画结束时物件旋转的角度 可以大于360度
说明:
当角度为负数——表示逆时针旋转
当角度为正数——表示顺时针旋转
(负数from——to正数:顺时针旋转)
(负数from——to负数:逆时针旋转)
(正数from——to正数:顺时针旋转)
(正数from——to负数:逆时针旋转)
pivotX 属性为动画相对于物件的X坐标的开始位置
pivotY 属性为动画相对于物件的Y坐标的开始位置
说明: 以上两个属性值 从0%-100%中取值
50%为物件的X或Y方向坐标上的中点位置
长整型值:
duration 属性为动画持续时间
说明: 时间以毫秒为单位
-->
</set>电池图片数字的闪烁变换
闪烁的图片
1
2
3
4
5
6
7<ImageView
android:id="@+id/t1_icon1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="180.0dip"
android:layout_marginTop="53.0dip"
android:background="@drawable/t1_frame_animation" />图片定义t1_frame_animation.xml,animation-list实现的是逐帧动画,oneshot表示不停的循环播放,每个帧的图片和时长定义
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
android:oneshot="false" >
<!--
根标签为animation-list,其中oneshot代表着是否只展示一遍,设置为false会不停的循环播放动画
根标签下,通过item标签对动画中的每一个图片进行声明
android:duration 表示展示所用的该图片的时间长度
-->
<item
android:drawable="@mipmap/tutorial1_icon1"
android:duration="200"/>
<item
android:drawable="@mipmap/tutorial1_icon2"
android:duration="200"/>
</animation-list>开始启动动画
1
2
3
4t1_icon1.setImageResource(R.drawable.t1_frame_animation);
t1_icon1_animationDrawable = (AnimationDrawable) t1_icon1.getDrawable();
t1_icon1_animationDrawable.start();最下角箭头下移和透明度变换
底部箭头
1
2
3
4
5
6
7
<ImageView
android:id="@+id/t1_next"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20.0dip"
android:src="@mipmap/tutorial_next" />加载动画
1
animationBottom = AnimationUtils.loadAnimation(MainActivity.this, R.anim.tutorail_bottom);
启动
1
t1_next.startAnimation(animationBottom);
动画描述文件tutorail_bottom.xml,转移动画和透明度的组合
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<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:repeatMode="reverse" >
<translate
android:duration="1000"
android:fromXDelta="0"
android:fromYDelta="-15"
android:repeatCount="infinite"
android:toXDelta="0"
android:toYDelta="20" />
<!-- translate 平移动画效果
整型值:
fromXDelta 属性为动画起始时 X坐标上的位置
toXDelta 属性为动画结束时 X坐标上的位置
fromYDelta 属性为动画起始时 Y坐标上的位置
toYDelta 属性为动画结束时 Y坐标上的位置
注意:
没有指定fromXType toXType fromYType toYType 时候,
默认是以自己为相对参照物 ,默认参考物最重要
长整型值:
duration 属性为动画持续时间
说明: 时间以毫秒为单位
-->
<alpha
android:duration="1000"
android:fromAlpha="1.0"
android:repeatCount="infinite"
android:toAlpha="0.3" />
<!--
透明度控制动画效果 alpha
浮点型值:
fromAlpha 属性为动画起始时透明度
toAlpha 属性为动画结束时透明度
说明:
0.0表示完全透明
1.0表示完全不透明
以上值取0.0-1.0之间的float数据类型的数字
长整型值:
duration 属性为动画持续时间
说明:
时间以毫秒为单位
整型值:
repeatCount:重复次数
说明:
infinite:循环执行,
具体正整数表示循环次数
repeatMode:重复模式,
说明:
restart:重新从头开始执行
reverse:反方向执行
-->
</set>
其他页面的也都差不多原理实现
第三页分析
可以看一下火箭飞后面云朵的动画
每个云朵都是一个image view
1 | <ImageView |
启动动画
1 | t3_icon2.startAnimation(transAnimation2); |
动画定义
1 | final TranslateAnimation transAnimation2 = new TranslateAnimation(fx1, tx1, fy1, ty1); |
每个云朵的移动速度不一样,其实位置fx1,fy1到目标位置tx1,ty1也不一样
第四页分析
挂牌左右摇晃动画
挂牌
1 | <ImageView |
左右摇晃动画
1 | int pivot = Animation.RELATIVE_TO_SELF; |
其他基本都是类似的实现:
小结
以上动画使用了Android的基本动画类型alpha,rotate,scale,translate,另外引入插值器完成动画
下面火箭的上升有一个自定义属性动画,我们现认识一下,后续在细细学习分析
火箭的上升动画
火箭view
1 | <ImageView |
加载动画
1 | animationRocket = ArcAnimator.createArcAnimator(page_one_rocket, centerX + r - x*3/2 + 20, centerY - r + 3*x/4 - 20 , 90f, Side.RIGHT); |
其中 ArcAnimator是自定义的属性动画
1 | package cn.lry.animation.arcanimator; |
关注一下
1 | ArcAnimator.createArcAnimator(page_one_rocket, centerX + r - x*3/2 + 20, centerY - r + 3*x/4 - 20 , 90f, Side.RIGHT); |
centerX + r - x*3/2 + 20
和centerY - r + 3*x/4 - 20
是计算出来火箭图标的最终目标位置的x,y坐标
看几条路线轨迹
1 | 01-10 04:57:03.822 11717-11717/cn.lry.animation I/GuideActivity: r=270.0中间背景的长宽 |
轨迹的示意图大概如下,90度是一个四分之一圆弧,180度就是一个半圆弧