今天来说一说Transition
,这个我们肯定不陌生,实现共享元素就会用到这个API。Activities之间精美的动画也全靠这个来实现。从Kitkat版本就出现了Sence
与Transition
(场景和转换)的概念,那么今天我们先来说一说这两个概念的基本用法。
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。
创建默认转场时使用的工具类,在场景更改期间实现自动淡化,移动视图并调整视图的大小的动画。例如:
我们为一个ViewGroup设置了背景,然后设置了一个Padding。
TransitionManager.beginDelayedTransition(content, new AutoTransition());
linearLayout.setPadding(100,100,100,100);
这种转换捕捉了场景变化之前和之后的目标视图的布局范围,并在转换过程中为这些变化提供动画。例如:
和上面那个动画一样,我设置一个时间,这样看的更明显。
ChangeBounds changeBounds=new ChangeBounds();
changeBounds.setDuration(2000);
TransitionManager.beginDelayedTransition(linearLayout,changeBounds);
linearLayout.setPadding(100,100,100,100);
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++;
这个裁剪的坐标给的真是太合适了!
这个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()
,后面会讲到的。
转换场景时,捕捉目标View更改之前与更改之后滚动的属性,然后对滚动的属性添加了动画处理。(API级别太高了,以后试一下)
在过渡场景更改之前和之后捕捉视图的缩放和旋转,并在过渡期间添加动画。例如:
ChangeTransform changeTransform = new ChangeTransform();
changeTransform.setDuration(500);
android.transition.TransitionManager.beginDelayedTransition(content, changeTransform);
imageView.setRotation(pos+90);
此转换会捕捉目标视图从开始到结束的可见性,并且视图会从场景边缘移动出入。这里的可见性指的是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
是按照怎样的计算方式设置焦点的。
目标视图淡入淡出的过渡效果,视图可见性由设置了setVisibility(int)的状态以及是否在当前视图层次结构中确定。指定IN( MODE_IN)
或者OUT(MODE_OUT)
分别对应淡入和淡出。也可以通过fade.setMode
方法设置,若不指定默认为淡入淡出效果。例如:
Transition transition = new Fade();
transition.setDuration(500);
TransitionManager.beginDelayedTransition(content, transition);
就像前面那两个一样,目标视图滑动的过渡效果,视图可见性由设置了setVisibility(int)的状态以及是否在当前视图层次结构中确定。它帮助View从一测滑向另一测。默认是BOTTOM
,当然了也可以自己设置。例如:
Transition transition = new Slide(Gravity.RIGHT);
transition.setDuration(500);
TransitionManager.beginDelayedTransition(content, transition);
这个类有两个具体实现类ArcMotion和PatternPathMotion,这个基类可以在视图转换的时候沿着指定的路径运动。
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++;
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);
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
那样,把所有的转场效果集合在一起,然后按照规则来执行。
每次写博客,都不知道该怎么结尾。算了,吐槽一句:
食堂打饭的阿姨,总是给我一点点菜,搞得我吃不饱。