手摸手教你用Scene实现炫酷的注册登录动画

项目地址:https://github.com/JeasonWong/SignUpTransition

老规矩,先上效果。

效果

先说下实现细节

  • 『SIGN UP』字符的位移
  • 圆圈扩散动画
  • 注册->登录的动画
  • 背景上升顶部图标渐变

此处插一下嘴~ 动画效果我是在materialup上看到的,实现后在设计师的效果后留言说我实现了他的效果,然后他也回应了下,我相信这样的结果也会是设计师和程序员感到最欣慰的结果。

整个过度动画只要实现这些就好了,然后再说下我使用到的技术

  • 字符唯一、背景上升使用Scene
  • 登录动画使用属性动画
  • 圆圈扩散使用的ViewAnimationUtils

登录动画就是一个简单的Loading效果,在这篇文章中不是重点,我不会赘述,如果想学习,可以看我之前写的两篇手摸手来学习做Loading动画。

好的,手摸手开始了。

创建三个Scene,对应着:

  • 注册静态页面
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
...
<RelativeLayout
android:id="@+id/rlt_bg"
android:layout_width="150dp"
android:layout_height="@dimen/bar_height"
android:layout_gravity="center_horizontal"
android:layout_marginTop="40dp"
android:background="@color/colorBg">
<me.wangyuwei.signuptransition.LoginLoadingView
android:id="@+id/login_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_centerInParent="true" />
</RelativeLayout>
  • 登录页面
1
2
3
4
5
6
7
8
9
10
11
12
13
<RelativeLayout
android:id="@+id/rlt_bg"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/colorBg">
<me.wangyuwei.signuptransition.LoginLoadingView
android:id="@+id/login_view"
android:layout_width="150dp"
android:layout_height="@dimen/bar_height"
android:layout_centerInParent="true" />
</RelativeLayout>
  • 主页面
1
2
3
4
5
6
7
8
9
<RelativeLayout
android:id="@+id/rlt_bg"
android:layout_width="match_parent"
android:layout_height="@dimen/bar_height"
android:background="@color/colorBg">
...
</RelativeLayout>

三个Scene中RelativeLayout的id取为一样,这样在之后操作Transition时才会有效果,这里很容易理解。

在ManActivity中有个用来展示各个Scene的ViewGroup,我这里用的FramLayout。

1
2
3
4
<FrameLayout
android:id="@+id/frt_content"
android:layout_width="match_parent"
android:layout_height="match_parent" />

那么在xml中该干的事我们都干完了,再回顾一下,分别是创建Scenes,创建展示Scene的ViewGroup。

显然接下来就是逻辑控制各个Scene的转换了。

MainActivity中初始化三个Scene:

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
67
68
69
70
71
72
73
74
75
76
77
78
...
private Scene mSceneSignUp;
private Scene mSceneLogging;
private Scene mSceneMain;
...
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mFrtContent = (FrameLayout) findViewById(R.id.frt_content);
mSceneSignUp = Scene.getSceneForLayout(mFrtContent, R.layout.scene_sign_up, this);
mSceneSignUp.setEnterAction(new Runnable() {
@Override
public void run() {
final LoginLoadingView loginView = (LoginLoadingView) mFrtContent.findViewById(R.id.login_view);
//计算LiginView的宽高
ViewTreeObserver vto = loginView.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
setSize(loginView.getMeasuredWidth(), loginView.getMeasuredHeight());
}
});
//绑定点击事件
loginView.setOnClickListener(MainActivity.this);
}
});
mSceneLogging = Scene.getSceneForLayout(mFrtContent, R.layout.scene_logging, this);
mSceneLogging.setEnterAction(new Runnable() {
@Override
public void run() {
final LoginLoadingView loginView = (LoginLoadingView) mFrtContent.findViewById(R.id.login_view);
//省略控制LoginView的状态代码
...
loginView.postDelayed(new Runnable() {
@Override
public void run() {
TransitionManager.go(mSceneMain, new ChangeBounds().setDuration(mDuration).setInterpolator(new DecelerateInterpolator()));
}
}, 6000);
}
});
mSceneMain = Scene.getSceneForLayout(mFrtContent, R.layout.scene_main, this);
mSceneMain.setEnterAction(new Runnable() {
@Override
public void run() {
final ImageView imgMenu = (ImageView) mFrtContent.findViewById(R.id.img_menu);
final ImageView imgUser = (ImageView) mFrtContent.findViewById(R.id.img_user);
//属性动画完成顶部图标的渐变
ValueAnimator animator = ValueAnimator.ofInt(0, 255);
animator.setDuration(mDuration);
animator.setInterpolator(new LinearInterpolator());
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
int alpha = (int) animation.getAnimatedValue();
imgMenu.setImageAlpha(alpha);
imgUser.setImageAlpha(alpha);
}
});
animator.start();
//展示RecyclerView
final RecyclerView recyclerView = (RecyclerView) mFrtContent.findViewById(R.id.rv_common);
CommonAdapter adapter = new CommonAdapter(MainActivity.this);
recyclerView.setLayoutManager(new GridLayoutManager(MainActivity.this, 3));
recyclerView.setAdapter(adapter);
}
});
//进入Activity后默认展示场景mSceneSignUp
TransitionManager.go(mSceneSignUp);
}

重点

  • 使用Scene.getSceneForLayout创建Scene
  • 使用TransitionManager.go()来控制Scene之间的转换
  • 过渡Transition直接使用的ChangeBounds
  • 使用Scene.setEnterAction()来做一些进入Scene时的操作
  • 属性动画哪里都有需求 = =

以上就是Scene过渡的关键。

关于Scene的补充学习,可以看下Google放出来的官方demo,他还实现了一个自定义的Transition,android-CustomTransition

打造一个这样炫酷的效果其实并不难,我也是现学现用,只要把已知知识点进行组合,一定能发挥大能量。

如果有更好的思路,欢迎交流,开源本身就是大家互相喷喷,互相进步嘛,
对各种动画感兴趣的朋友欢迎加群479729938进行交流,
期待各种好看,好玩,实用的动画~

Markdown