文章目录
- 概述
- Android 2.0 转场动画
- Activity转场
- 示例代码
- Fragment转场动画
- 示例代码:
- 效果图
- Fragment扩展技能部分
- Android 4.4 场景动画
- Activity内部布局转场
- Api讲解
- Scene
- Scene概念:
- SceneApi:
- Transition Api
- Android 5.0 共享元素动画
- 参考文献
概述
转场动画: 两个不同界面间的衔接动画
效果如下图(网图 侵立删):
在Android 不同版本提供了对应Api
- Android 2.0 比较简单的淡入淡出 滑出动画 4.1 后被ActivityOptions代替
- Android 4.4 场景动画Transition框架
- Android 5.0 共享元素动画(基于Transition)
Android 2.0 转场动画
Activity转场
使用public void overridePendingTransition(int enterAnim, int exitAnim)
函数进行转场动画,4.1 后被ActivityOptions
代替
Api介绍:
/**
* Call immediately after one of the flavors of {@link #startActivity(Intent)}
* or {@link #finish} to specify an explicit transition animation to
* perform next.
*
* <p>As of {@link android.os.Build.VERSION_CODES#JELLY_BEAN} an alternative
* to using this with starting activities is to supply the desired animation
* information through a {@link ActivityOptions} bundle to
* {@link #startActivity(Intent, Bundle)} or a related function. This allows
* you to specify a custom animation even when starting an activity from
* outside the context of the current top activity.
*
* @param enterAnim A resource ID of the animation resource to use for
* the incoming activity. Use 0 for no animation.
* @param exitAnim A resource ID of the animation resource to use for
* the outgoing activity. Use 0 for no animation.
*/
public void overridePendingTransition(int enterAnim, int exitAnim)
示例代码
第一个界面代码
//OneActivity.java
public class OneActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
//当点击界面的某个按钮跳转到下一个界面
public void onClick(View view) {
startActivity(new Intent(this, TwoActivity.class));
//overridePendingTransition必须在 startActivity之后
//左滑动退出第一个界面,右滑动进入第二个界面
overridePendingTransition(android.R.anim.slide_in_left,android.R.anim.slide_in_left);
}
}
第二个界面代码
public class TwoActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
}
//按下返回键切换到上一个界面 使用overridePendingTransition来进行动画
@Override
public void onBackPressed() {
super.onBackPressed();
overridePendingTransition(android.R.anim.slide_in_left,android.R.anim.slide_in_left);
}
}
效果图:
同样也可以通过如下主题添加动画:
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.DayNight.DarkActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
<item name="android:windowAnimationStyle">@style/ActivityThem</item>
</style>
<style name="ActivityThem">
<item name="android:windowExitAnimation">@android:anim/slide_out_right</item>
<item name="android:windowEnterAnimation">@android:anim/slide_in_left</item>
<!--以下参数大家可以自行尝试-->
<!--<item name="android:activityOpenEnterAnimation">@android:anim/slide_in_left</item>
<item name="android:activityCloseExitAnimation">@android:anim/slide_out_right</item>-->
</style>
</resources>
Fragment转场动画
/**
* 选择一个默认动画完成作为转场动画 ,可选属性如下官方介绍
* Select a standard transition animation for this transaction. May be
* one of {@link #TRANSIT_NONE}, {@link #TRANSIT_FRAGMENT_OPEN},
* {@link #TRANSIT_FRAGMENT_CLOSE}, or {@link #TRANSIT_FRAGMENT_FADE}.
*/
FragmentTransaction.setTransition(int)
//设置进入动画和退出动画
FragmentTransaction setCustomAnimations( int enter,int exit);
//设置进入 退出 弹入栈 弹出栈的动画
FragmentTransaction setCustomAnimations(int enter,int exit,int popEnter,int popExit);
示例代码:
FragmentTransaction.setTransition 比较简单就不演示
FragmentTransaction.setCustomAnimations 这里演示两个参数的函数(四个参数大家可以自行尝试,只不过是添加返回栈动画)
//点击按钮添加一个Old1Fragment
public void onClick(View view) {
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
Old1Fragment old1Fragment=Old1Fragment.newInstance("", "");
//setTransition
fragmentTransaction.setCustomAnimations(android.R.anim.slide_in_left,android.R.anim.slide_out_right).replace(R.id.fl,old1Fragment).addToBackStack("1").commit();
}
//点击按钮添加一个Old2Fragment
public void onClick2(View view) {
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
Old2Fragment old2Fragment=Old2Fragment.newInstance("", "");
fragmentTransaction.setCustomAnimations(android.R.anim.slide_in_left,android.R.anim.slide_out_right).replace(R.id.fl,old2Fragment).addToBackStack("1").commit();
}
效果图
Fragment扩展技能部分
如果你想实现更复杂的动画(不使用高版本Api情况) 可以重写Fragment
方法onCreateAnimation
//此函数声明如下 第一个参数是fragmentTransaction.setTransition()所设置常量值,第二个为是否是进场,第三个参数为动画资源id(setCustomAnimations所设置动画资源id)
public Animation onCreateAnimation(int transit, boolean enter, int nextAnim)
详细说明:
用以下代码说明
//点击按钮添加一个Old1Fragment
public void onClick(View view) {
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
Old1Fragment old1Fragment=Old1Fragment.newInstance("", "");
//设置默认动画
fragmentTransaction.setTransition(TRANSIT_NONE);
//设置自定义动画
fragmentTransaction.setCustomAnimations(android.R.anim.slide_in_left,android.R.anim.slide_out_right).replace(R.id.fl,old1Fragment).addToBackStack("1").commit();
}
按照如上配置后Fragment
会传入上面的配置调用onCreateAnimation
方法 创建动画 。假设此时 添加Old1Fragment
那么 将会回调方法并且对应参数为onCreateAnimation(TRANSIT_NONE,true,android.R.anim.slide_in_left)
你可以重写此方法返回一个更加复杂的动画。如你想要Fragment
中的所有TextView
旋转,并且ImageView
进行移动。
Android 4.4 场景动画
这个其实也是难点 因为5.0也是基于此完成
Activity内部布局转场
效果图在一个Activity中 进行不同布局转场:
界面代码 (随便看看不需要看懂后面讲解):
public class TMDemo1Activity extends AppCompatActivity {
private Scene scene1;
private Scene scene2;
private ViewGroup root;
private LinearLayout llroot;
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_tmdemo1);
root = (ViewGroup) findViewById(R.id.root);
llroot = (LinearLayout) findViewById(R.id.llroot);
scene1 = new Scene(root, findViewById(R.id.screne_root));
scene2 = Scene.getSceneForLayout(root, R.layout.scene_2_layout, this);
}
private static final String TAG = "TMDemo1Activity";
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
public void onClick(View view) throws InterruptedException {
switch (view.getId()) {
case R.id.btn1: {
TransitionManager.go(scene1);
}
break;
case R.id.btn2: {
TransitionManager.go(scene2);
}
break;
case R.id.btn3: {
/**
* 对点击按钮做变化
*/
TransitionManager.beginDelayedTransition(llroot);
LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) view.getLayoutParams();
layoutParams.leftMargin=233;
view.setLayoutParams(layoutParams);
}
break;
}
}
}
界面xml
<!-- activity_tmdemo1.xml-->
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
android:id="@+id/llroot"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".TMDemo1Activity">
<Button
android:layout_width="wrap_content"
android:id="@+id/btn1"
android:text="scene1"
android:onClick="onClick"
android:layout_height="wrap_content"/>
<Button
android:layout_width="wrap_content"
android:id="@+id/btn2"
android:text="scene2"
android:onClick="onClick"
android:layout_height="wrap_content"/>
<Button
android:layout_width="wrap_content"
android:id="@+id/btn3"
android:text="postDelay"
android:onClick="onClick"
android:layout_height="wrap_content"/>
<FrameLayout
android:id="@+id/root"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<include
layout="@layout/scene_1_layout"
/>
</FrameLayout>
</LinearLayout>
<?xml version="1.0" encoding="utf-8"?>
<!--
scene_1_layout.xml
-->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:id="@+id/screne_root"
android:layout_height="match_parent">
<View
android:transitionName="view"
android:layout_width="50dp"
android:background="#0d0"
android:layout_height="50dp"/>
</RelativeLayout>
<!--scene_2_layout.xml-->
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:id="@+id/screne_root"
android:layout_height="match_parent">
<View
android:layout_width="50dp"
android:id="@+id/view"
android:layout_marginTop="30dp"
android:background="#0d0"
android:layout_height="50dp"/>
</RelativeLayout>
Api讲解
Scene
Scene概念:
- 场景:一个布局或者一个
view
对象 将他们封装到Scene
对象中。比如界面A和界面B 那么对应两个Scene
分别为Scene_A
和Scene_B
,界面之前的变化对应Scene_A
到Scene_B
举个例子:
ll_root = findViewById(R.id.root);
//A界面
View aView = getLayoutInflater().inflate(R.layout.demo2_scene_2_layout, ll_root, false);
//B界面
View bView = getLayoutInflater().inflate(R.layout.demo2_scene_3_layout, ll_root, false);
if (true) {
//A界面到B界面
ll_root.removeView(aView);
ll_root.addView(bView);
}else{
//B界面到A界面
ll_root.removeView(bView);
ll_root.addView(aView);
}
我们知道上面的代码实现了两个界面的切换,但是过于生硬。那么用Scene
来试试
ll_root = findViewById(R.id.root);
scene2 = Scene.getSceneForLayout(fl_root, R.layout.demo2_scene_2_layout, this);
scene3 = Scene.getSceneForLayout(fl_root, R.layout.demo2_scene_3_layout, this);
if (true){
//这个Api 先不管 只需要记住 这样做就可以切换界面等价于removeView 和addView,并且增加过渡动画
TransitionManager.go(scene2);
}else {
TransitionManager.go(scene3);
}
SceneApi:
静态实例化方法:
//参数1:切换界面的父布局
//参数2:布局id
//参数3:上下文
public static Scene getSceneForLayout(ViewGroup sceneRoot, int layoutId, Context context)
构造实例化
//参数1:切换界面的父布局
//参数2:布局id
public Scene(ViewGroup sceneRoot, View layout)
//弃用
public Scene(ViewGroup sceneRoot, ViewGroup layout)
但是两个场景之间切换必然有一个中间人,比如淘宝买东西,中间需要快递传递信息和物件才能完成一次交易。
也就是前面 看到的TransitionManager
对象
调用TransitionManager.go()
方法即可做界面切换并使用默认的动画完成两个Scene
过渡
为了执行一个动画每次创建一个Scene
太麻烦的话TransitionManager
提供了另一个方法beginDelayedTransition
来简化这一操作
public static void beginDelayedTransition(final ViewGroup sceneRoot)
函数 会自动检测传入的参数sceneRoot
子布局的变化,然后在下一次系统绘制图形的时候进行过渡动画
现在大家不妨想一个问题,过渡动画能非常自然的衔接两个不同界面之间有关联的View的变化。那么系统提供的Api怎么知道 两个界面之间哪两个View
之间有关联?
关联规则如下:
- 同一例化对象
- 相同id
- transitionName相同
举个例子:
<?xml version="1.0" encoding="utf-8"?>
<!--第一个界面-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:orientation="vertical"
android:id="@+id/inner_root"
android:layout_height="match_parent">
<!--注意设置了android:transitionName-->
<View
android:layout_marginTop="20dp"
android:layout_width="30dp"
android:background="#f0f"
android:transitionName="view"
android:layout_height="30dp"/>
<!--注意设置了id-->
<ImageView
android:id="@+id/iv"
android:layout_marginLeft="100dp"
android:layout_width="150dp"
android:scaleType="centerCrop"
android:src="@mipmap/ic_launcher2"
android:layout_height="130dp"/>
</LinearLayout>
第二个界面:
<?xml version="1.0" encoding="utf-8"?>
<!--第二个界面-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:orientation="vertical"
android:id="@+id/inner_root"
android:layout_height="match_parent">
<!--注意设置了android:transitionName-->
<View
android:layout_marginLeft="30dp"
android:layout_marginTop="20dp"
android:layout_width="30dp"
android:background="#f0f"
android:transitionName="view"
android:layout_height="30dp"/>
<!--注意设置了id-->
<ImageView
android:id="@+id/iv"
android:layout_marginLeft="30dp"
android:layout_marginLeft="100dp"
android:layout_width="150dp"
android:scaleType="centerCrop"
android:src="@mipmap/ic_launcher2"
android:layout_height="130dp"/>
</LinearLayout>
大家看到两个界面中的区别和不同,第一个界面中和第二个界面View
的android:transitionName
相同所以两个界面过渡的时候相关联,第二个ImageView
中id相同故满足关联同理
Transition Api
我们知道
未完待续。。。
Android 5.0 共享元素动画
参考文献
https://www.jianshu.com/p/e497123652b5https://developer.android.google.cn/training/transitions/https://developer.android.google.cn/reference/android/transition/Transitionhttp://www.jcodecraeer.com/plus/view.php?aid=8682https://www.jianshu.com/p/fa1c8deeaa57