本系列文章一共5篇
谁说Android的动画不廉价(一)之项目分层
谁说Android的动画不廉价(二)之转场动画
谁说Android的动画不廉价(三)之共享元素动画
谁说Android的动画不廉价(四)之元素动画
谁说Android的动画不廉价(五)之水波纹动画
GitHub源码
引言
本篇博文是基于上一篇博文谁说Android的动画不廉价(三)之共享元素动画的基础上做的拓展。
目标效果图
前提说明
其实这篇博文分为两个动画。
- 第一种就是标题上的元素动画。就是View的位置,大小发生改变时伴随着动画效果。
- 另一种就是场景动画,同一些元素,在不同layout里面的位置大小不同,然后通过切换场景来模拟View的位置大小改变。不同的layout元素可以不一样,Android根据id来判断是否为同一个元素。两个layout没有相同元素的旧layout元素将会使用退出动画,然后没有相同元素的新layout元素将会使用进入动画
元素动画
如何开启
改变View的大小,位置之前使用TransitionManager.beginDelayedTransition(ViewGroup sceneRoot, [Transition transition]);
开启
- sceneRoot : 元素所在的ViewGroup
- transition : 改变时的动画效果
场景动画
如何加载场景
通过Scene.getSceneForLayout(ViewGroup viewGroup,int layoutID,Context context);
方法加载一个场景
-
viewGroup
: layout需要加入的ViewGroup,类似于Fragment使用中的FrameLayout容器 -
layoutID
: 场景的布局文件资源id
如何切换
通过TransitionManager.go(Scene scene,[Transition transition]);
方法来切换场景
元素动画
布局文件
activity_view_animation.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<include layout="@layout/tool_bar" />
<ImageView
android:id="@+id/img"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_below="@+id/toolBar"
android:layout_gravity="center"
android:layout_marginTop="30dp"
android:src="@drawable/circle_orange" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:orientation="horizontal">
<Button
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
android:layout_weight="1"
android:onClick="changeSize"
android:text="改变大小" />
<Button
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
android:layout_weight="1"
android:onClick="changePosition"
android:text="改变位置" />
</LinearLayout>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
android:layout_marginTop="20dp"
android:onClick="next"
android:text="Next(SCENES)" />
</LinearLayout>
代码
MainActivity.java
public void viewAnimations(View v) {
Intent intent = new Intent(this, ViewAnimationActivity.class);
ActivityOptionsCompat activityOptionsCompat = ActivityOptionsCompat.makeSceneTransitionAnimation(this);
startActivity(intent, activityOptionsCompat.toBundle());
}
ViewAnimationActivity.java
package demo.august1996.top.transitionanimationsdemo;
import android.content.Intent;
import android.support.v4.app.ActivityOptionsCompat;
import android.transition.TransitionManager;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import demo.august1996.top.transitionanimationsdemo.Activity.ToolbarActivity;
public class ViewAnimationActivity extends ToolbarActivity {
ViewGroup container;
View imageView;
boolean isChangeSize = false;
boolean isChangePosition = false;
@Override
protected String getToolbarTitle() {
return "元素动画";
}
@Override
protected void initView() {
imageView = findViewById(R.id.img);
container = (ViewGroup) findViewById(R.id.container);
}
@Override
protected int getContentViewID() {
return R.layout.activity_view_animation;
}
@Override
protected boolean canBack() {
return true;
}
/**
* 修改指定View的大小,根据isChangeSize来判断
* isChangeSize == true:为修改过的大小,大小*2
* isChangeSize == false:为原始的大小,大小/2
* @param v
*/
public void changeSize(View v) {
TransitionManager.beginDelayedTransition(container);
LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) imageView.getLayoutParams();
if (isChangeSize) {
layoutParams.width = layoutParams.width * 2;
} else {
layoutParams.width = layoutParams.width / 2;
}
layoutParams.height = layoutParams.width;
imageView.setLayoutParams(layoutParams);
isChangeSize = !isChangeSize;
}
/**
* 与changeSize类似
* @param v
*/
public void changePosition(View v) {
TransitionManager.beginDelayedTransition(container);
LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) imageView.getLayoutParams();
if (isChangePosition) {
layoutParams.gravity = Gravity.CENTER;
} else {
layoutParams.gravity = Gravity.LEFT;
}
imageView.setLayoutParams(layoutParams);
imageView.postInvalidate();
isChangePosition = !isChangePosition;
}
public void next(View v) {
Intent intent = new Intent(this, SceneActivity.class);
ActivityOptionsCompat activityOptionsCompat = ActivityOptionsCompat.makeSceneTransitionAnimation(this);
startActivity(intent, activityOptionsCompat.toBundle());
}
}
改变View的布局参数前开启动画效果TransitionManager.beginDelayedTransition(container);
场景动画
布局文件
比较多,1个Activity的layout加上5个场景的layout
第一个是初始化的场景,其余四个是含有相同的元素,只是位置不同。
activity_scene.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activityRoot"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<include layout="@layout/tool_bar" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:orientation="vertical">
<FrameLayout
android:id="@+id/viewRoot"
android:layout_width="match_parent"
android:layout_height="wrap_content"></FrameLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="@+id/one"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
android:layout_weight="1"
android:alpha="0"
android:onClick="one"
android:scaleX="0"
android:scaleY="0"
android:text="Scene One" />
<Button
android:id="@+id/two"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
android:layout_weight="1"
android:alpha="0"
android:onClick="two"
android:scaleX="0"
android:scaleY="0"
android:text="Scene TWO" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:orientation="horizontal">
<Button
android:id="@+id/three"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
android:layout_weight="1"
android:alpha="0"
android:onClick="three"
android:scaleX="0"
android:scaleY="0"
android:text="Scene Three" />
<Button
android:id="@+id/four"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
android:layout_weight="1"
android:alpha="0"
android:onClick="four"
android:scaleX="0"
android:scaleY="0"
android:text="Scene Four" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
-
FrameLayout
就是我们场景需要加载的地方 - 而且我们看到按钮有放大且透明出现的效果,所以我们先把
scaleX
、scaleY
和alpha
设置成0
sences_layout_0.xml
<?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"
android:orientation="vertical">
<TextView
android:id="@+id/tv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="Demo"
android:textColor="#f0f"
android:textSize="100sp"
android:textStyle="bold" />
</RelativeLayout>
sences_layout_1.xml
<?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"
android:orientation="vertical">
<ImageView
android:id="@+id/circle_orange"
android:layout_width="150dp"
android:layout_height="150dp"
android:layout_alignParentLeft="true"
android:layout_margin="10dp"
android:src="@drawable/circle_orange" />
<ImageView
android:id="@+id/circle_blue"
android:layout_width="150dp"
android:layout_height="150dp"
android:layout_alignParentRight="true"
android:layout_margin="10dp"
android:src="@drawable/circle_blue" />
<ImageView
android:id="@+id/circle_yellow"
android:layout_width="150dp"
android:layout_height="150dp"
android:layout_alignParentLeft="true"
android:layout_below="@+id/circle_orange"
android:layout_margin="10dp"
android:src="@drawable/circle_yellow" />
<ImageView
android:id="@+id/circle_red"
android:layout_width="150dp"
android:layout_height="150dp"
android:layout_alignParentRight="true"
android:layout_below="@id/circle_orange"
android:layout_margin="10dp"
android:src="@drawable/circle_red" />
</RelativeLayout>
sences_layout_2.xml
<?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"
android:orientation="vertical">
<ImageView
android:id="@+id/circle_blue"
android:layout_width="150dp"
android:layout_height="150dp"
android:layout_alignParentLeft="true"
android:layout_margin="10dp"
android:src="@drawable/circle_blue" />
<ImageView
android:id="@+id/circle_yellow"
android:layout_width="150dp"
android:layout_height="150dp"
android:layout_alignParentRight="true"
android:layout_margin="10dp"
android:src="@drawable/circle_yellow" />
<ImageView
android:id="@+id/circle_red"
android:layout_width="150dp"
android:layout_height="150dp"
android:layout_alignParentLeft="true"
android:layout_below="@id/circle_blue"
android:layout_margin="10dp"
android:src="@drawable/circle_red" />
<ImageView
android:id="@+id/circle_orange"
android:layout_width="150dp"
android:layout_height="150dp"
android:layout_alignParentRight="true"
android:layout_below="@id/circle_blue"
android:layout_margin="10dp"
android:src="@drawable/circle_orange" />
</RelativeLayout>
sences_layout_3.xml
<?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"
android:orientation="vertical">
<ImageView
android:id="@+id/circle_red"
android:layout_width="150dp"
android:layout_height="150dp"
android:layout_alignParentLeft="true"
android:layout_margin="10dp"
android:src="@drawable/circle_red" />
<ImageView
android:id="@+id/circle_orange"
android:layout_width="150dp"
android:layout_height="150dp"
android:layout_alignParentRight="true"
android:layout_margin="10dp"
android:src="@drawable/circle_orange" />
<ImageView
android:id="@+id/circle_blue"
android:layout_width="150dp"
android:layout_height="150dp"
android:layout_alignParentLeft="true"
android:layout_below="@+id/circle_red"
android:layout_margin="10dp"
android:src="@drawable/circle_blue" />
<ImageView
android:id="@+id/circle_yellow"
android:layout_width="150dp"
android:layout_height="150dp"
android:layout_alignParentRight="true"
android:layout_below="@+id/circle_red"
android:layout_margin="10dp"
android:src="@drawable/circle_yellow" />
</RelativeLayout>
sences_layout_4.xml
<?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"
android:orientation="vertical">
<ImageView
android:id="@+id/circle_blue"
android:layout_width="150dp"
android:layout_height="150dp"
android:layout_alignParentRight="true"
android:layout_below="@id/circle_yellow"
android:layout_margin="10dp"
android:src="@drawable/circle_blue" />
<ImageView
android:id="@+id/circle_yellow"
android:layout_width="150dp"
android:layout_height="150dp"
android:layout_alignParentLeft="true"
android:layout_margin="10dp"
android:src="@drawable/circle_yellow" />
<ImageView
android:id="@+id/circle_red"
android:layout_width="150dp"
android:layout_height="150dp"
android:layout_alignParentRight="true"
android:layout_margin="10dp"
android:src="@drawable/circle_red" />
<ImageView
android:id="@+id/circle_orange"
android:layout_width="150dp"
android:layout_height="150dp"
android:layout_alignParentLeft="true"
android:layout_below="@id/circle_yellow"
android:layout_margin="10dp"
android:src="@drawable/circle_orange" />
</RelativeLayout>
代码
SceneActivity.java
package demo.august1996.top.transitionanimationsdemo;
import android.transition.ChangeBounds;
import android.transition.Scene;
import android.transition.Slide;
import android.transition.TransitionManager;
import android.transition.TransitionSet;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.BounceInterpolator;
import java.util.ArrayList;
import java.util.List;
import demo.august1996.top.transitionanimationsdemo.Activity.ToolbarActivity;
public class SceneActivity extends ToolbarActivity {
/**
* 用于动画效果的按钮
*/
List<View> buttonList = new ArrayList<>();
/**
* 添加场景到该ViewGroup
*/
ViewGroup viewRoot;
/**
* 一共5个场景,0为默认场景
*/
Scene scene0;
Scene scene1;
Scene scene2;
Scene scene3;
Scene scene4;
@Override
protected void beforeInitView() {
super.beforeInitView();
}
@Override
protected String getToolbarTitle() {
return "场景动画";
}
@Override
protected void initView() {
/**
* 初始化按钮View
*/
buttonList.add(findViewById(R.id.one));
buttonList.add(findViewById(R.id.two));
buttonList.add(findViewById(R.id.three));
buttonList.add(findViewById(R.id.four));
viewRoot = (ViewGroup) findViewById(R.id.viewRoot);
/**
* 初始化场景
*/
scene0 = Scene.getSceneForLayout(viewRoot, R.layout.sences_layout_0, this);
scene1 = Scene.getSceneForLayout(viewRoot, R.layout.sences_layout_1, this);
scene2 = Scene.getSceneForLayout(viewRoot, R.layout.sences_layout_2, this);
scene3 = Scene.getSceneForLayout(viewRoot, R.layout.sences_layout_3, this);
scene4 = Scene.getSceneForLayout(viewRoot, R.layout.sences_layout_4, this);
/**
* 当场景1被切换的时候,按钮显示动画
*/
scene0.setEnterAction(new Runnable() {
@Override
public void run() {
for (int i = 0; i < buttonList.size(); i++) {
View view = buttonList.get(i);
view.animate()
.setDuration(1000)
.scaleX(1)
.scaleY(1)
.alpha(1)
.start();
}
}
});
/**
* 切换到场景0
*/
TransitionManager.go(scene0);
}
/**
* 使用简单动画切换场景1
* @param v
*/
public void one(View v) {
TransitionManager.go(scene1, new ChangeBounds());
}
/**
* 使用混合动画加载场景2
* @param v
*/
public void two(View v) {
TransitionSet transitionSet = new TransitionSet();
transitionSet.setDuration(1000);
Slide slide = new Slide();
ChangeBounds changeBounds = new ChangeBounds();
transitionSet.addTransition(slide);
transitionSet.addTransition(changeBounds);
TransitionManager.go(scene2, transitionSet);
}
/**
* 使用混合动画加载场景3,并设置动画顺序
* @param v
*/
public void three(View v) {
TransitionSet transitionSet = new TransitionSet();
transitionSet.setDuration(1000);
Slide slide = new Slide();
ChangeBounds changeBounds = new ChangeBounds();
transitionSet.addTransition(slide);
transitionSet.addTransition(changeBounds);
transitionSet.setOrdering(TransitionSet.ORDERING_SEQUENTIAL);
TransitionManager.go(scene3, transitionSet);
}
/**
* 使用混合动画加载场景4,并设置动画顺序和差时器
* @param v
*/
public void four(View v) {
TransitionSet transitionSet = new TransitionSet();
transitionSet.setDuration(1000);
transitionSet.setInterpolator(new BounceInterpolator());
Slide slide = new Slide();
ChangeBounds changeBounds = new ChangeBounds();
transitionSet.addTransition(slide);
transitionSet.addTransition(changeBounds);
transitionSet.setOrdering(TransitionSet.ORDERING_SEQUENTIAL);
TransitionManager.go(scene4, transitionSet);
}
@Override
protected int getContentViewID() {
return R.layout.activity_scene;
}
@Override
protected boolean canBack() {
return true;
}
}