谷歌Demo效果展示
改进后效果展示
官方调用
这里不分析源码是怎么实现的,因为我还没彻底搞懂……
所以只简单贴一下调用方法
从ActivityA启动ActivityB时设置共享元素imageview_item和textview_name
ActivityA.java
Intent intent = new Intent(MainActivity.this, DetailActivity.class);
intent.putExtra(DetailActivity.EXTRA_PARAM_ID, item.getId());
ActivityOptionsCompat activityOptions = ActivityOptionsCompat.makeSceneTransitionAnimation(
MainActivity.this,
new Pair<>(view.findViewById(R.id.imageview_item),
DetailActivity.VIEW_NAME_HEADER_IMAGE),
new Pair<>(view.findViewById(R.id.textview_name),
DetailActivity.VIEW_NAME_HEADER_TITLE)
);
ActivityCompat.startActivity(MainActivity.this, intent, activityOptions.toBundle());
同样在ActivityB中也设置这两个共享元素
private ImageView mHeaderImageView;
private TextView mHeaderTitle;
@Override
protected void onCreated(Bundle saveInstanceState){
...
HeaderImageView = findViewById(R.id.imageview_header);
mHeaderTitle = findViewById(R.id.textview_title);
ViewCompat.setTransitionName(mHeaderImageView, VIEW_NAME_HEADER_IMAGE);
ViewCompat.setTransitionName(mHeaderTitle, VIEW_NAME_HEADER_TITLE);
...
}
就这么简单。具体代码去看谷歌Demo
但是因为Demo中的textview这个共享元素在两个页面的字体不一样,所以过度很突兀
页面跳转共享元素动画实际上是先加载好ActivityB的textview,然后开始动画,所以看到在进入动画的时候,textview的字体样式突然变成了ActivityB的样式
自定义Transition
ActivityA的代码不变
ActivityB的代码略作修改
private ImageView mHeaderImageView;
private TextView mHeaderTitle;
@Override
protected void onCreate(Bundle savedInstanceState) {
...
mHeaderImageView = findViewById(R.id.imageview_header);
mHeaderTitle = findViewById(R.id.textview_title);
ViewCompat.setTransitionName(mHeaderImageView, VIEW_NAME_HEADER_IMAGE);
ViewCompat.setTransitionName(mHeaderTitle, VIEW_NAME_HEADER_TITLE);
// 配置启动动画的属性
configEnterExitAnimation(true);
// 推迟动画启动
postponeEnterTransition();
... // 这里加载所需的数据
// 数据加载好后启动动画
startPostponedEnterTransition();
}
配置enter和exit的动画属性
private void configEnterExitAnimation(boolean isEnter) {
// 文字大小和颜色动画
TextviewTransition textviewTransition = new TextviewTransition(isEnter);
textviewTransition.addTarget(R.id.textview_title);
// 文字位置动画
ChangeBounds textviewChangeBounds = new ChangeBounds();
textviewChangeBounds.addTarget(R.id.textview_title);
// ImageView位置动画
ChangeBounds imageChangeBounds = new ChangeBounds();
imageChangeBounds.addTarget(VIEW_NAME_HEADER_IMAGE);
TransitionSet set = new TransitionSet()
.addTransition(textviewTransition)
.addTransition(textviewChangeBounds)
.addTransition(imageChangeBounds)
.setDuration(1500); // 这里把动画时长调成1.5s,过度效果更明显
this.getWindow().setSharedElementEnterTransition(set);
}
自定义的TextView的Transition类,用来管理文字颜色和大小的动画过度
class TextviewTransition extends Transition {
private boolean mIsEnter = true;
public TextviewTransition(boolean isEnter) {
mIsEnter = isEnter;
}
@Override
public void captureStartValues(TransitionValues transitionValues) {
}
@Override
public void captureEndValues(TransitionValues transitionValues) {
}
@Override
public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues, TransitionValues endValues) {
if (startValues == null || endValues == null) {
return null;
}
return createEnterAnimator(startValues, endValues);
}
private Animator createEnterAnimator(TransitionValues startValues, TransitionValues endValues) {
// 文字大小过度动画
ObjectAnimator textSizeAnimator = ObjectAnimator.ofFloat((TextView) startValues.view,
new Property<TextView, Float>(Float.class, "textSize") {
@Override
public void set(TextView object, Float value) {
object.setTextSize(TypedValue.COMPLEX_UNIT_PX, value);
}
@Override
public Float get(TextView object) {
return object.getTextSize();
}
},
mIsEnter ? getResources().getDimensionPixelOffset(R.dimen.text_size_start) :
getResources().getDimensionPixelOffset(R.dimen.text_size_end),
mIsEnter ? getResources().getDimensionPixelOffset(R.dimen.text_size_end) :
getResources().getDimensionPixelOffset(R.dimen.text_size_start)
);
// 文字颜色过度动画
ObjectAnimator textColorAnimator = ObjectAnimator.ofArgb((TextView) startValues.view,
new Property<TextView, Integer>(Integer.class, "textColor") {
@Override
public void set(TextView object, Integer value) {
object.setTextColor(value);
}
@Override
public Integer get(TextView object) {
return object.getCurrentTextColor();
}
},
mIsEnter ? getResources().getColor(R.color.start_color, null) :
getResources().getColor(R.color.end_color, null),
mIsEnter ? getResources().getColor(R.color.end_color, null) :
getResources().getColor(R.color.start_color, null)
);
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.playTogether(textSizeAnimator, textColorAnimator);
return animatorSet;
}
}
简单分析下就是TextviewTransition类中包含了两个动画,一个是TextView的大小,另一个是TextView的颜色
然后整体的页面过度动画TransitionSet又包含三个:TextView大小颜色动画(TextviewTransition)、TextView大小和位置动画,ImageView大小和位置动画。这样就能在谷歌Demo的基础上让文字过度更加自然。(这里没有处理字重过度的问题 ,感兴趣可以去拓展)
ChangeBounds: View的大小与位置动画
ChangeTransform: View的缩放与旋转动画
ChangeClipBounds: View的裁剪区域(View.getClipBounds())动画
ChangeScroll: 处理View的scrollX与scrollY属性
ChangeImageTransform: 处理ImageView的ScaleType属性效果不是很完美(包括字重过度、动画启动结束时TextView内容换行问题),但已经实现了TextView大小和动画的过度。