翻转卡片动画容器FlipView

概述

今天开发遇到一个视觉需求,翻转一个logo用来强调入口,好吧,反正设计是就是喜欢炫的东西,作为开发来说,没的选咯,在google了一番以后,发现android developer官网有一个页面的切换动画,https://developer.android.com/training/animation/cardflip.html,好像有点意思,于是模仿者来了一发,做了一个FlipView。
无图无真相,把结果发出来再BB吧。

实现原理

其实主要是四个切换动画,两个用于顺时针翻转, 两个用于逆时针翻转。虽然我不喜欢贴代码,但是好像代码更加能说明哈,毕竟程序员都很聪明的。
在main/res/animator目录夹下面心间四个xml
首先是顺时针进入动画anim_in.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
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<!--消失-->
<objectAnimator
android:duration="0"
android:propertyName="alpha"
android:valueFrom="1.0"
android:valueTo="0.0"/>
<!--旋转-->
<objectAnimator
android:duration="@integer/card_flip_time_full"
android:propertyName="rotationY"
android:valueFrom="-180"
android:valueTo="0"/>
<!--出现-->
<objectAnimator
android:duration="0"
android:propertyName="alpha"
android:startOffset="@integer/card_flip_time_half"
android:valueFrom="0.0"
android:valueTo="1.0"/>
</set>

顺时针出动画anim_out.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<!--旋转-->
<objectAnimator
android:duration="@integer/card_flip_time_full"
android:propertyName="rotationY"
android:valueFrom="0"
android:valueTo="180"/>
<!--消失-->
<objectAnimator
android:duration="0"
android:propertyName="alpha"
android:startOffset="@integer/card_flip_time_half"
android:valueFrom="1.0"
android:valueTo="0.0"/>
</set>

然后是逆时针进入动画anim_in_reverse.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
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<!--消失-->
<objectAnimator
android:duration="0"
android:propertyName="alpha"
android:valueFrom="1.0"
android:valueTo="0.0"/>
<!--旋转-->
<objectAnimator
android:duration="@integer/card_flip_time_full"
android:propertyName="rotationY"
android:valueFrom="0"
android:valueTo="-180"/>
<!--出现-->
<objectAnimator
android:duration="0"
android:propertyName="alpha"
android:startOffset="@integer/card_flip_time_half"
android:valueFrom="0.0"
android:valueTo="1.0"/>
</set>

逆时针出动画anim_out_reverse.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<!--旋转-->
<objectAnimator
android:duration="@integer/card_flip_time_full"
android:propertyName="rotationY"
android:valueFrom="180"
android:valueTo="0"/>
<!--消失-->
<objectAnimator
android:duration="0"
android:propertyName="alpha"
android:startOffset="@integer/card_flip_time_half"
android:valueFrom="1.0"
android:valueTo="0.0"/>
</set>

代码拷贝完了,简单的来解释下吧,就拿顺时针的两个动画来看就行了,其实主要的就是将前面的一面进行rotationY旋转,然后在旋转到一半的时候进行隐藏,后面的一面刚好相反,先进行隐藏,然后反向旋转着出现。

AbstractFlipView

考虑到前后两面会有不同的界面,不仅仅是简单的ImageView,那么决定写个抽象类好了,让继承者提供给我们两个翻转的类。

1
2
3
protected abstract View getUpSideView();
protected abstract View getDownSideView();

然后将其加到两个容器中去,布局如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<RelativeLayout
android:id="@+id/up_side"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<RelativeLayout
android:id="@+id/down_side"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>

核心的代码其实就是加载好动画,然后执行,下一次执行必须在上一次动画结束后才行。以顺时针为例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 翻转卡片
public void flipCardClockWise() {
// 正面朝上
if (!isUpSide) {
rightOutAnimator.setTarget(upSideContainer);
leftInAnimator.setTarget(downSideContainer);
rightOutAnimator.start();
leftInAnimator.start();
isUpSide = true;
} else { // 背面朝上
rightOutAnimator.setTarget(downSideContainer);
leftInAnimator.setTarget(upSideContainer);
rightOutAnimator.start();
leftInAnimator.start();
isUpSide = false;
}
}

好了,核心代码到此就结束了。

继承自AbstractView写一个例子

主要实现几个基本的接口:

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
public class FlipView extends AbstractFlipView {
public FlipView(Context context, AttributeSet attrs) {
super(context, attrs);
setDirection(Direction.CLOCK_WISE);
}
@Override
protected View getUpSideView() {
View oneSide = LayoutInflater.from(getContext()).inflate(R.layout.layout_one_side, this, false);
return oneSide;
}
@Override
protected View getDownSideView() {
View oneSide = LayoutInflater.from(getContext()).inflate(R.layout.layout_one_side, this, false);
return oneSide;
}
@Override
protected int getUpSidePadding() {
return 0;
}
@Override
protected int getDownSidePadding() {
return 0;
}
}

以上就是一个例子。好了,在github上有我的一个简单的例子,大家可以参考下,笔者水平有限,大家凑合着看吧,github地址为:https://github.com/gordon-rawe/FlipView