一. ViewPager翻页动画

1.介绍

① PageTransformer接口

ViewPager提供了PageTransformer接口用于实现翻页动画。有两个参数:page 和 position。

  • page :ViewPager每一页的视图。

  • position:page对应的位置,是百分比位置。

1
2
3
4
public interface PageTransformer {

void transformPage(@NonNull View page, float position);
}

② position的理解

如果超过屏幕的绝对距离超过屏幕的宽度,那么绝对距离就会变成无限大。仔细想想,只有page 的 position 处于 ( -1 , 1 ) 的位置,我们才能在屏幕看到它们的内容。

上面的理解是来自别人博客的理解,这样子理解有点小问题。我在自己实现 PageTransformer接口 的时候,在函数里面打印了 position 。

如果你有六个页面,由于ViewPager默认只会加载两个页面,所以只会有第一个页面和第二个页面进入到我们的 transformPage 方法中,position 的值依次是 0.0 和 1.0。这样子,我们就不能对其他的页面进行动画,毕竟其他的页面不进来这个方法中。

当你位于第一个页面时,如果想要滑到第二个页面。此时变化的只有第一个页面和第二个页面的 position。第一个页面的 position 从 0.0 -> -1.0,第二个页面的 position 从 1.0 -> 0.0。此时,第三个页面仍然不会进入 transformPage 方法中。

但是,如果我们设置默认加载的页面是6个,会怎么样呢?看到下面的打印结果我们终于解开了疑惑,也知道了该怎么对其他页面进行动画了,也知道了之前的错误。超过屏幕的绝对距离超过屏幕的宽度,那么绝对距离不会变成无限大,而是慢慢累加的。同理,反方向也不是无限大,而是 -1.0 , -2.0 等等

1
2
3
4
5
6
position:0.0 + Page:android.widget.LinearLayout{b42dbdb V.E...... ......ID 0,0-1080,1730}
position:1.0 + Page:android.widget.LinearLayout{d68678 V.E...... ......ID 1080,0-2160,1730}
position:2.0 + Page:android.widget.LinearLayout{1200151 V.E...... ......ID 2160,0-3240,1730}
position:3.0 + Page:android.widget.LinearLayout{ad6db6 V.E...... ......ID 3240,0-4320,1730}
position:4.0 + Page:android.widget.LinearLayout{2095ab7 V.E...... ......ID 4320,0-5400,1730}
position:5.0 + Page:android.widget.LinearLayout{8299224 V.E...... ......ID 5400,0-6480,1730}

③ 使用方式

1
2
public void setPageTransformer(boolean reverseDrawingOrder,
@Nullable PageTransformer transformer)

参数reverseDrawingOrder: true表示提供的PageTransformer画view时是倒序,false则是正序。不理解的可以看接下里的gif图。

参数transformer: 就是我们 PageTransformer接口 的实现对象啦。

2. 官方例子:DepthPageTransformer

从小猫切换到小狗的过程中:

  • 对于 position 处于 [-1,0] 的页面(小猫),不设置透明度,缩放,平移动画。

  • 对于 position 处于 [0,1] 的页面(小狗),设置透明度,缩放,平移动画,平移动画用来让视图一直处于屏幕位置。因为默认情况下处于 [0,1] 的页面 是一步一步平移到屏幕位置的,我们使用平移动画让它一直处于屏幕位置。

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
//设置翻页方式
pager.setPageTransformer(true, new DepthPageTransformer());

//翻页动画实现类
public class DepthPageTransformer implements ViewPager.PageTransformer {
//最小缩放比例
private static final float MIN_SCALE = 0.75f;

@Override
public void transformPage(@NonNull View page, float position) {
//视图宽度
int pageWidth = page.getWidth();

if (position < -1) { // [-Infinity,-1)
// This page is way off-screen to the left.
page.setAlpha(0);

} else if (position <= 0) { // [-1,0]
// Use the default slide transition when moving to the left page
page.setAlpha(1);
page.setTranslationX(0);
page.setScaleX(1);
page.setScaleY(1);

} else if (position <= 1) { // (0,1]
// Fade the page out.
page.setAlpha(1 - position);

// Counteract the default slide transition
page.setTranslationX(pageWidth * -position);

// Scale the page down (between MIN_SCALE and 1)
float scaleFactor = MIN_SCALE
+ (1 - MIN_SCALE) * (1 - Math.abs(position));
page.setScaleX(scaleFactor);
page.setScaleY(scaleFactor);

} else { // (1,+Infinity]
// This page is way off-screen to the right.
page.setAlpha(0);
}
}
}

我们之前的效果是设置 true,倒序绘图。我们改成 false 正序后的效果如下:

3. 官方例子:ZoomOutPageTransformer

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
//设置翻页方式
pager.setPageTransformer(true, new ZoomOutPageTransformer());

//翻页动画的实现类
public class MainActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

//找到控件
ViewPager pager = findViewById(R.id.pager);
TabLayout tab = findViewById(R.id.tab);

//fragment数据源
List<Fragment> fragments = new ArrayList<>();
fragments.add(new MyFragment1());
fragments.add(new MyFragment2());

//标题数据源
String[] titles = getResources().getStringArray(R.array.title);

//设置适配器
pager.setAdapter(
new MyAdapterFragment(
getSupportFragmentManager(),
FragmentPagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT,
fragments,
titles
)
);

//绑定tab和fragment
tab.setupWithViewPager(pager);

//设置翻页方式
pager.setPageTransformer(true, new ZoomOutPageTransformer());
}
}

二. 自定义翻页动画

1. 旋转翻页动画

自定义的播放动画类

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
public class RotationTransformer implements ViewPager.PageTransformer {

@Override
public void transformPage(@NonNull View page, float position) {
//一个页面上移的基本高度
float margin = 40;

//获取视图的宽度
int width = page.getWidth();

//控制水平偏移,保持页面都在屏幕中
page.setTranslationX(-width * (position));
//设置页面的垂直偏移
page.setTranslationY(-position * margin);

if (position > 0.0f) {
//针对的是在屏幕的页面以及在当前页面下边(右边)的页面

//设置旋转点
page.setPivotX(0);
page.setPivotY(0);

//设置旋转度数
page.setRotation(0f);
}else if (position >= -0.1f && position <= 0.0f) {
//针对的是即将划出或者划入屏幕的页面

//设置选准点
page.setPivotX(0);
page.setPivotY(0);

//设置旋转度数
page.setRotation(-180f * position);
}else {
//针对的是已经划出屏幕的页面,或者说是当前页面的上面(左边)的页面

//设置旋转点
page.setPivotX(0);
page.setPivotY(0);

//设置旋转度数
page.setRotation(180f);
}
}
}

注意:setOffscreenPageLimit设置的预加载数量一定要是:页面的数量。不然的话页面的垂直偏移不会全部社会成功。因为我们每次的动画范围都涉及到所有的页面,需要所有的页面都需要进入到 transformPage 方法中。

2.缩放翻页动画

自定义的播放动画类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class ScaleTransformer implements ViewPager.PageTransformer {

@Override
public void transformPage(@NonNull View page, float position) {
if (position >= -1.0f && position <= 0.0f) {
//针对的是当前页面左边即将划出或者划入的页面
page.setScaleX(1 + position * 0.1f);
page.setScaleY(1 + position * 0.2f);
} else if (position > 0.0f && position <= 1.0f) {
//针对的是当前页面右边即将划出或者划入的页面
page.setScaleX(1 - position * 0.1f);
page.setScaleY(1 - position * 0.2f);
} else {
//针对的是除了当前页面以及当前页面左边的页面
page.setScaleX(0.9f);
page.setScaleY(0.8f);
}
}
}

注意:这个时候因为ViewPager默认加载的页面是两个,当前的页面和即将划出当前的页面会进入 transformPage 方法中,而我们的每次的动画范围只涉及这两个页面,故对页面加载的数量没有要求。

三. 结合 CardView 实现

参考:CardView 简介和使用

参考:CardView+ViewPager实现ViewPager翻页动画

四. 各种各样的翻页动画

参考:ViewPager 16种切换动画,早晚能用到

参考文章

ViewPager 全面剖析及使用详解

巧用ViewPager实现驾考宝典做题翻页效果