今天来说一说Transition
,这个我们肯定不陌生,实现共享元素就会用到这个API。Activities之间精美的动画也全靠这个来实现。从Kitkat版本就出现了Sence
与Transition
(场景和转换)的概念,那么今天我们先来说一说这两个概念的基本用法。
- Scene
定义了界面当前的状态信息,保存了布局中所有View的属性值 - Transition
当一个场景改变的时候,transition主要负责:捕获每个View在开始场景和结束场景时的状态(各种属性值);根据两个场景(开始和结束)之间的区别自动创建一个Animator。 TransitionManager
这个类将Sence
与Transition
关联了起来,大多数情况下的场景变化都会使用AutoTransition
。只有当应用程序需要不同的转换行为时,才需要指定其他的Transition来满足特定的场景需求。setTransition(Scene scene, Transition transition)
设置需要的场景与过渡动画setTransition(Scene fromScene, Scene toScene, Transition transition)
设置起始场景和结束场景与过渡动画go(Scene scene)
切换到指定的场景go(Scene scene, Transition transition)
切换到指定的场景以及所使用的过渡动画,transition
如果传了null,那么就没有了过渡动画。beginDelayedTransition(ViewGroup sceneRoot, Transition transition)
可以自定义场景过渡的方法,调用此方法时,TransitionManager会捕获sceneRoot中View的属性值,然后发出请求以在下一帧上进行转换。那时候,sceneRoot中的新值将被捕获,变化的过程将会以动画的形式展现出来。我们没有必要创建一个新的Scene,因为这个方法主要是用来展现当前场景到下一帧的过渡效果。void endTransitions (ViewGroup sceneRoot)
结束指定场景根目录在准备/正在进行的转换。
更详细的解释,请移步官方API。
AutoTransition
创建默认转场时使用的工具类,在场景更改期间实现自动淡化,移动视图并调整视图的大小的动画。例如:
我们为一个ViewGroup设置了背景,然后设置了一个Padding。
TransitionManager.beginDelayedTransition(content, new AutoTransition());
linearLayout.setPadding(100,100,100,100);
ChangeBounds
这种转换捕捉了场景变化之前和之后的目标视图的布局范围,并在转换过程中为这些变化提供动画。例如:
和上面那个动画一样,我设置一个时间,这样看的更明显。
ChangeBounds changeBounds=new ChangeBounds();
changeBounds.setDuration(2000);
TransitionManager.beginDelayedTransition(linearLayout,changeBounds);
linearLayout.setPadding(100,100,100,100);
ChangeClipBounds
ChangeClipBounds
捕捉View
的getClipBounds()
场景变化之前和之后的变化,并在变换过程中为这些变化提供动画。例如:
Rect rect = new Rect(50, 150, 200, 350);
ChangeClipBounds changeClipBounds = new ChangeClipBounds();
android.transition.TransitionManager.beginDelayedTransition(linearLayout, changeClipBounds);
if (pos % 2 == 0) {
ViewCompat.setClipBounds(imageView2,rect);
}else{
ViewCompat.setClipBounds(imageView2,null);
}
pos++;
这个裁剪的坐标给的真是太合适了!
ChangeImageTransform
这个Transition在场景变化之前和之后会捕获一个ImageView的矩阵,并在转换的过程中生成动画。可以与ChangeBounds
结合使用。例如:
if (v.getId() == R.id.button) {
ChangeImageTransform changeImageTransform = new ChangeImageTransform();
ChangeClipBounds changeClipBounds = new ChangeClipBounds();
android.transition.TransitionManager.
beginDelayedTransition(content,
new TransitionSet().addTransition(changeImageTransform)
.addTransition(changeClipBounds).setDuration(1000));
switch (pos) {
case 0:
Rect rect = new Rect(50, 150, 300, 450);
ViewCompat.setClipBounds(imageView2, rect);
imageView2.setScaleType(ImageView.ScaleType.CENTER);
break;
case 1:
ViewCompat.setClipBounds(imageView2, null);
imageView2.setScaleType(ImageView.ScaleType.CENTER_CROP);
break;
case 2:
Rect rect1 = new Rect(100, 200, 300, 400);
ViewCompat.setClipBounds(imageView2, rect1);
imageView2.setScaleType(ImageView.ScaleType.FIT_XY);
break;
case 3:
ViewCompat.setClipBounds(imageView2, null);
imageView2.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
break;
}
if (pos == 3) {
pos = -1;
}
pos++;
}
这里用到了TransitionSet()
,后面会讲到的。
ChangeScroll(API23中添加)
转换场景时,捕捉目标View更改之前与更改之后滚动的属性,然后对滚动的属性添加了动画处理。(API级别太高了,以后试一下)
ChangeTransform
在过渡场景更改之前和之后捕捉视图的缩放和旋转,并在过渡期间添加动画。例如:
ChangeTransform changeTransform = new ChangeTransform();
changeTransform.setDuration(500);
android.transition.TransitionManager.beginDelayedTransition(content, changeTransform);
imageView.setRotation(pos+90);
Explode(爆炸?)
此转换会捕捉目标视图从开始到结束的可见性,并且视图会从场景边缘移动出入。这里的可见性指的是setVisibility(int)
的状态以及当前视图是否在ViewGroup层次结构中(比如recyclerView的Item的消失与显示)。如果没有设置焦点中心的话,那么视图默认会从场景中心移出去(像爆炸一样?)例如:
默认的情况下:
final Rect viewRect = new Rect();
bt.getLocalVisibleRect(viewRect);
Transition transition = new Explode();
transition.setEpicenterCallback(new Transition.EpicenterCallback() {
@Override
public Rect onGetEpicenter(Transition transition) {
return null;
}
});
transition.setDuration(500);
TransitionManager.beginDelayedTransition(content, transition);
if(pos%2==0){
imageView2.setVisibility(View.GONE);
imageView.setVisibility(View.GONE);
imageView3.setVisibility(View.GONE);
imageView4.setVisibility(View.GONE);
}else{
imageView2.setVisibility(View.VISIBLE);
imageView.setVisibility(View.VISIBLE);
imageView3.setVisibility(View.VISIBLE);
imageView4.setVisibility(View.VISIBLE);
}
pos++;
当然了我们也可以自定义焦点,由Transition epicenter提供 (通过setEpicenterCallback方法来设置),例如:
final Rect viewRect = new Rect();
bt.getLocalVisibleRect(viewRect);
Transition transition = new Explode();
transition.setEpicenterCallback(new Transition.EpicenterCallback() {
@Override
public Rect onGetEpicenter(Transition transition) {
return viewRect;
}
});
transition.setDuration(500);
TransitionManager.beginDelayedTransition(content, transition);
if(pos%2==0){
imageView2.setVisibility(View.GONE);
imageView.setVisibility(View.GONE);
}else{
imageView2.setVisibility(View.VISIBLE);
imageView.setVisibility(View.VISIBLE);
}
pos++;
这里关于onGetEpicenter
方法,我也不清楚这个返回的Rect
是按照怎样的计算方式设置焦点的。
Fade
目标视图淡入淡出的过渡效果,视图可见性由设置了setVisibility(int)的状态以及是否在当前视图层次结构中确定。指定IN( MODE_IN)
或者OUT(MODE_OUT)
分别对应淡入和淡出。也可以通过fade.setMode
方法设置,若不指定默认为淡入淡出效果。例如:
Transition transition = new Fade();
transition.setDuration(500);
TransitionManager.beginDelayedTransition(content, transition);
Slide
就像前面那两个一样,目标视图滑动的过渡效果,视图可见性由设置了setVisibility(int)的状态以及是否在当前视图层次结构中确定。它帮助View从一测滑向另一测。默认是BOTTOM
,当然了也可以自己设置。例如:
Transition transition = new Slide(Gravity.RIGHT);
transition.setDuration(500);
TransitionManager.beginDelayedTransition(content, transition);
PathMotion
这个类有两个具体实现类ArcMotion和PatternPathMotion,这个基类可以在视图转换的时候沿着指定的路径运动。
- ArcMotion
Transition transition = new ChangeBounds();
transition.setDuration(600);
transition.setPathMotion(new ArcMotion());
TransitionManager.beginDelayedTransition(content, transition);
RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) imageView.getLayoutParams();
if (pos % 2 == 0) {
params.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
params.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
} else {
int[] rules = params.getRules();
for (int i = 0; i < rules.length; i++) {
params.removeRule(i);
}
}
imageView.setLayoutParams(params);
pos++;
- PatternPathMotion
可以使用Path定义两个坐标点之间的运动模式。而且必须具有与起点不同的终点。下面这个例子我随便写了一个坐标点,然后用Path连接起来,我们来看效果:
final Path path = new Path();
path.moveTo(0, 0);
path.quadTo(300, 0, 400, 450);
PathMotion pathMotion = new PathMotion() {
@Override
public Path getPath(float startX, float startY, float endX, float endY) {
return path;
}
};
ChangeBounds changeBounds = new ChangeBounds();
changeBounds.setPathMotion(pathMotion);
TransitionManager.beginDelayedTransition(content,changeBounds);
RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) imageView.getLayoutParams();
params.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
imageView.setLayoutParams(params);
TransitionSet
TransitionSets可以实现更复杂的转换效果。ORDERING_SEQUENTIAL
是一个一个的来 ORDERING_TOGETHER
同时开始
public void onClick(View v) {
mExpanded = !mExpanded;
TransitionManager.beginDelayedTransition(transitionsContainer, new TransitionSet()
.addTransition(new ChangeBounds())
.addTransition(new ChangeImageTransform()));
ViewGroup.LayoutParams params = imageView.getLayoutParams();
params.height = mExpanded ? ViewGroup.LayoutParams.MATCH_PARENT : ViewGroup.LayoutParams.WRAP_CONTENT;
imageView.setLayoutParams(params);
imageView.setScaleType(mExpanded ? ImageView.ScaleType.CENTER_CROP : ImageView.ScaleType.FIT_CENTER);
}
这个TransitionSet
其实就像AnimationSet
那样,把所有的转场效果集合在一起,然后按照规则来执行。
每次写博客,都不知道该怎么结尾。算了,吐槽一句:
食堂打饭的阿姨,总是给我一点点菜,搞得我吃不饱。