一次在逛Github的时候,看到一个漂亮的登陆界面,用的是Transition作的。我就直接贴上地址:html
MaterialLoginandroid
固然,若是单纯的直接拿过来用,没有任何意义。主要仍是来看具体如何实现的。我就来写下具体如何一步步的来实现这个效果。git
我也按照相应的原理写了个Demo。最后的效果以下图所示(其中layout布局我就直接从github上面拷贝过来了):github
首先咱们来看下什么是Transition。你们看仔细是Transition,而不是Translate。咱们直接看翻译:api
而Translate一般咱们指的是平移的动画操做。bash
因此咱们知道了用的是过渡的方式来作,那什么是过渡呢?app
Android对于开发者提供了愈来愈多的动画API支持。从API 1就存在的Drawable Animation和View Animation,以及API 11(Android 3.0)之后加入的Property Animation。而过渡动画Transition是在API 19(Android 4.4.2)中加入的。ide
基础知识我就不说了,直接看其余文章传送门:布局
Android 过渡(Transition)动画解析之基础篇测试
因此初步咱们能够理解为(可能这么说明有不对,能够提出):
场景(scenes)和变换(transitions)。场景(scenes)定义了当前的UI状态,变换(transitions)则定义了在不一样场景之间动画变化的过程。
当一个场景改变的时候,transition主要负责:
(1)捕捉每一个View在开始场景和结束场景时的状态。
(2)根据两个场景(开始和结束)之间的区别建立一个Animator。
Android 5.0中Transition能够被用来实现Activity或者Fragment切换时的异常复杂的动画效果。
虽然在之前的版本中,已经可使用Activity的overridePendingTransition() 和 FragmentTransaction的setCustomAnimation()来实现Activity或者Fragment的动画切换,可是他们仅仅局限与将整个视图一块儿动画变换。新的Lollipop api更进了一步,让单独的view也能够在进入或者退出其布局容器中时发生动画效果,甚至还能够在不一样的activity/Fragment中共享一个view。
仍是上面那个图,只是变成了二个Activity界面:
咱们在跳转到第二个Activity的时候,咱们会有个过场动画。会第一个Activity的按钮移动到第二个Activity的按钮。效果以下所示:
因此咱们再回头看下面这种效果,是否是就知道怎么实现了,用的是Activity的过渡动画了。
你们也能够看看下面的相关文章连接:
Activity和Fragment Transition介绍
深刻理解共享元素变换(Shared Element Transition)-上
咱们先准备第一个Activity,界面以下:
咱们让那个按钮"+"能移动到顶部:
咱们由前面的demo说明已经知道了,启动第二个Activity,咱们咱们先让第二个Activity的界面以下所示:
咱们设置第二个Activity的主题为:
<style name="Translucent" parent="Theme.AppCompat.Light.NoActionBar">
//第一个activity的状态栏颜色为#0288D1
<item name="colorPrimaryDark">#0288D1</item>
//第二个activity的背景为透明,
//这样能够看获得第一个Activity的界面
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:windowIsTranslucent">true</item>
</style>
复制代码
没错,咱们在第二个界面先写上一个按钮"X",这样咱们启动第二个Activity的时候就盖在了第一个Activity的上面,同时这个fab按钮也有了动画的效果:
代码很简单,只要让第一个Activity的按钮的android:transitionName
与第二个Activity的按钮的android:transitionName
同样就能够。咱们称这个为共享元素。
FloatingActionButton btn = findViewById(R.id.fab);;
ActivityOptionsCompat optionsCompat
= ActivityOptionsCompat.makeSceneTransitionAnimation(LoginMainActivity.this,btn,btn.getTransitionName());
startActivity(new Intent(LoginMainActivity.this,RegisterMainActivity.class),optionsCompat.toBundle());
复制代码
而后经过ActivityOptionsCompat
来记录当前这个Activity的这个fab按钮的状态。而后在startActivity
的时候,经过optionsCompat.toBundle()
把内容带到了第二个Activity
中。第二个Activity就会让如今的相同trasitionName
的fab按钮,以传过来的第一个Activity
的按钮相同位置的为起始点,而后经过动画到了最终的地方。(因此动画是在第二个Activity中完成的,只是按钮的起始状态是以第一个Activity传过来的按钮的状态信息相同,而后到最终用户设置的位置。)
咱们能够看到,共享元素变换并非真正实现了两个activity或者Fragment之间元素的共享,实际上咱们看到的几乎全部变换效果中(不论是B进入仍是B返回A),共享元素都是在B中绘制出来的。Framework没有真正试图将A中的某个元素传递给B,而是采用了不一样的方法来达到相同的视觉效果。A传递给B的是共享元素的状态信息。B利用这些信息来初始化共享View元素,让它们的位置、大小、外观与在A中的时候彻底一致。当变换开始的时候,B中除了共享元素以外,全部的其余元素都是不可见的。随着动画的进行,framework 逐渐将B的activity窗口显示出来,当动画完成,B的窗口才彻底可见。
而且其实动画是绘制在ViewOverlay上面,能够看看这篇文章:ViewOverlay与animation介绍
咱们直接不作任何处理,默认是fab按钮的位置变化是直线。 咱们更但愿是:
咱们能够设置共享元素的进入动画:
<?xml version="1.0" encoding="utf-8"?>
<transitionSet
xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@android:interpolator/linear_out_slow_in"
android:duration="3000">
<changeBounds>
<arcMotion
android:maximumAngle="0"
android:minimumHorizontalAngle="60"
android:minimumVerticalAngle="90" />
</changeBounds>
</transitionSet>
复制代码
//获取过渡动画
Transition transition = TransitionInflater.from(this).inflateTransition(R.transition.fabtransition);
设置共享元素的进入动画
getWindow().setSharedElementEnterTransition(transition);
复制代码
这里使用的是arcMotion来作的曲线路径.
里面的介绍我用的谷歌翻译翻译的,大体应该是这个意思: PathMotion在包含两个点的假想圆上沿圆弧生成曲线路径。 若是点之间的水平距离小于垂直距离,则圆的中心点将与终点水平对齐。 若是垂直距离小于水平距离,则圆的中心点将与终点垂直对齐。 当两点接近水平或垂直时,运动的曲线将会变小,由于圆的中心距两点都很远。 要强制路径的曲率,可使用setMinimumHorizontalAngle(float)和setMinimumVerticalAngle(float)来设置两点之间的弧的最小角度。
其余参考文章:
咱们上一步对fab按钮设置了过渡的动画。咱们能够对这个过渡动画设置结束的监听,而后其余咱们的注册界面的出现:
Transition transition = TransitionInflater.from(this).inflateTransition(R.transition.fabtransition);
getWindow().setSharedElementEnterTransition(transition);
transition.addListener(new Transition.TransitionListener() {
.....
.....
@Override
public void onTransitionEnd(Transition transition) {
transition.removeListener(this);
/*咱们能够在动画结束后,
能够在这里写上代码,
让注册界面出现
*/
.....
.....
}
});
复制代码
这是咱们的第二个Activity的布局变成了这样:
只不过默认这个注册界面是不可见的,等到咱们的fab按钮动画结束后,咱们再让注册界面可见就能够了。
这里咱们能够直接在上面fab按钮动画结束的时候,直接让注册界面出现(由于这个注册界面是用CardView写的,因此这里直接用cardView来指这个实例),咱们能够在上面的结束监听里面直接设置:
@Override
public void onTransitionEnd(Transition transition) {
transition.removeListener(this);
//设置可见
cardView.setVisibility(View.VISIBLE);
}
复制代码
效果以下:
咱们发现,直接忽然出现,虽然功能实现了,但咱们仍是但愿有更好看的效果,就像文章开头那样,这个注册界面是慢慢展开的。因此咱们在fab按钮过渡动画结束后,不是简单的对cardView设置View.VISIBLE就能够。咱们使用揭露动画来实现:
Animator mAnimator = ViewAnimationUtils.createCircularReveal(cardView,cardView.getWidth()/2
,0,0,cardView.getHeight());
mAnimator.setDuration(500);
mAnimator.setInterpolator(new AccelerateInterpolator());
mAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
super.onAnimationStart(animation);
cardView.setVisibility(View.VISIBLE);
}
});
mAnimator.start();
复制代码
揭露动画参考文章:
使用Circular Reveal为你的应用添加揭露动画效果
因此咱们这么使用后效果变成了:
这里有二种方式:
咱们的fab键从左边移动到了上边,而后若是你按返回键,你会发现自动fab键会先执行相应的自动回去动画,而后activity再关闭。好比你直接对fab键设置了点击事件:
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
finish();
}
});
复制代码
直接调用finish()方法的话,你就会发现,没有fab键返回的动画,而是直接第二个activity关闭,显示第一个activity的见面。这样很不友善。
咱们知道默认按返回键是调用了:
@Override
public void onBackPressed() {
super.onBackPressed();
}
复制代码
说明调用onBackPressed
会调用退出动画效果后再finish()
;
参考文章:
最经常使用的Activity的onBackPressed()与finish()的区别.
因此咱们知道了,咱们点击fab键返回的时候不能直接finish,而是最后一步是调用super.onBackPressed();
。
因此咱们最终是先让注册界面慢慢消失,消失后调用super.onBackPressed();
。
//覆写返回键操做,
//执行注册界面消失动画,
//而后再执行super.onBackPressed();
@Override
public void onBackPressed() {
animateRevealClose();
}
//fab的点击事件与上面同样
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
animateRevealClose();
}
});
//注册界面界面慢慢消失,而后调用super.onBackPressed()
//而后fab键会执行动画回到原始位置,而后第二个Activity关闭。
//而后显示了第一个Activity
public void animateRevealClose(){
Animator mAnimator = ViewAnimationUtils.createCircularReveal(cardView,cardView.getWidth()/2
,0,cardView.getHeight(),0);
mAnimator.setDuration(500);
mAnimator.setInterpolator(new AccelerateInterpolator());
mAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
cardView.setVisibility(View.GONE);
btn.setImageResource(R.drawable.plus);
RegisterMainActivity.super.onBackPressed();
}
});
mAnimator.start();
}
复制代码
最后实现就是这样子了:
哪里错误了,你们留言回复哦,多谢支持。o( ̄︶ ̄)o
大佬若是能帮我解答下下面二个问题,很是感谢:
我在使用arcMotion的时候,小米5(6.0)与华为(7.0),呈现的曲线效果差异很大,(gif图是小米的,因此fab键移动的时候更像是直线,可是华为就很明显的是曲线)不知道是什么缘由,知道的能够告诉我下。