文章目录

  • 概述
  • Android 2.0 转场动画
  • Activity转场
  • 示例代码
  • Fragment转场动画
  • 示例代码:
  • 效果图
  • Fragment扩展技能部分
  • Android 4.4 场景动画
  • Activity内部布局转场

  • Api讲解
  • Scene
  • Scene概念:
  • SceneApi:
  • Transition Api
  • Android 5.0 共享元素动画
  • 参考文献


概述

转场动画: 两个不同界面间的衔接动画

效果如下图(网图 侵立删):

在Android 不同版本提供了对应Api

  1. Android 2.0 比较简单的淡入淡出 滑出动画 4.1 后被ActivityOptions代替
  2. Android 4.4 场景动画Transition框架
  3. 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);

    }
}

效果图:

Android 圆形转场动画 android转场动画框架_Android 圆形转场动画

同样也可以通过如下主题添加动画:

<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();
    }

效果图

Android 圆形转场动画 android转场动画框架_xml_02

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中 进行不同布局转场:

Android 圆形转场动画 android转场动画框架_Android 圆形转场动画_03

界面代码 (随便看看不需要看懂后面讲解):

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概念:
  1. 场景:一个布局或者一个view对象 将他们封装到Scene对象中。比如界面A和界面B 那么对应两个Scene 分别为 Scene_AScene_B ,界面之前的变化对应Scene_AScene_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之间有关联?

关联规则如下:

  1. 同一例化对象
  2. 相同id
  3. 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>

大家看到两个界面中的区别和不同,第一个界面中和第二个界面Viewandroid: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