概述
当你想要从一个页面A转换到页面B,而且他们共享一个元素(比如是一个view),在这种场景下,最好的用户体验可能就是将共享的元素直接变换到最终的地方和大小,这会使用户专注于应用而且有一种连贯性的表达。这在Android5.0以上是可以很方便的实现的,接下来就说说不同场景下ShareElements的使用。
Fragment/Activity跳转到Activity
跳转Activity相对简单一些,官方封装好了使用方法:
//这是ContextCompat里的方法,参数中多了一个bundle,可配置共享元素相关设置
public static void startActivity(@NonNull Context context, @NonNull Intent intent,
@Nullable Bundle options) {
if (Build.VERSION.SDK_INT >= 16) {
context.startActivity(intent, options);
} else {
context.startActivity(intent);
}
}
//这是ActivityOptionsCompat里的方法,方便我们直接生成对应的bundle
@NonNull
public static ActivityOptionsCompat makeSceneTransitionAnimation(@NonNull Activity activity,
@NonNull View sharedElement, @NonNull String sharedElementName) {
if (Build.VERSION.SDK_INT >= 21) {
return createImpl(ActivityOptions.makeSceneTransitionAnimation(
activity, sharedElement, sharedElementName));
}
return new ActivityOptionsCompat();
}
//如果传递多个共享元素,则要使用到Pair
public static ActivityOptionsCompat makeSceneTransitionAnimation(@NonNull Activity activity,
Pair<View, String>... sharedElements)
看完support包提供的方法,来看具体使用吧:
Intent intent = new Intent(getContext(), ImageViewActivity.class);
ActivityCompat.startActivity(getContext(), intent,
ActivityOptionsCompat.makeSceneTransitionAnimation(
getActivity(), ivUserInfoHead, ViewCompat.getTransitionName(ivUserInfoHead)).toBundle());
注意点:
- 基本要求,转场涉及的两个Fragment的共享元素的transitionName要一致,可以在xml中设置,也可以通过ViewCompat.setTransitionName(view, "name")在代码中设置。
- 如果使用了自定义按钮返回,非系统返回,则结束Activity时不能直接finish(),需要使用supportFinishAfterTransition()替代,这样返回也会有相应的转场动画了。
Fragment跳转到Fragment
直接上代码:
//首先在Fragment中加载一个小图标Fragment
getChildFragmentManager().beginTransaction()
.replace(R.id.view_holder, new ImageViewSmallFragment())
.commit();
//小图标Fragment中监听点击事件跳转到大图标Fragment
view.findViewById(R.id.test_img).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
ImageViewFragment newFragment = new ImageViewFragment();
//注意:这里要设置共享元素的转场动画,也可以把这段代码放在newFragment的onCreate里面,看个人喜欢,我更倾向于放跳转这里,这样转场的逻辑在一个地方,更方便阅读和理解
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
newFragment.setSharedElementEnterTransition(
TransitionInflater.from(getContext()).inflateTransition(android.R.transition.move));
}
//注意:这里要用getFragmentManager(),因为要替换掉自己
getFragmentManager().beginTransaction()
//设置指定的共享元素和共享元素名字
.addSharedElement(view, ViewCompat.getTransitionName(view))
//注意:这里只能用replace,用add无效
.replace(R.id.view_holder, newFragment)
//注意:这里要压栈,返回时才能也有转场动画
.addToBackStack("")
.commit();
}
});
//新Fragment中返回时使用popBackStack方法回退,这样返回也能有相应的转场动画了,注意,这里要使用getFragmentManager()
getFragmentManager().popBackStack();
注意点:
- 基本要求,转场涉及的两个Fragment的共享元素的transitionName要一致,可以在xml中设置,也可以通过ViewCompat.setTransitionName(view, "name")在代码中设置。
- 跳转新Fragment只能使用replace,使用add无效,因为replace会触发原来Fragment生命周期的onPause,而add不会,所以使用add无法触发转场动画。
- 既然只能使用replace,则表示跳转的Fragment和原来的Fragment要在同一个布局容器里,否则也是不会触发原来Fragment的onPause的。
- 是在同一个布局容器里,说明两个Fragment是同级的,在旧Fragment中点击跳转新Fragment时,不能使用getChildFragmentManager,在Activity中就使用getSupportFragmentManager,如果是Fragment里则使用getFragmentManager()。
- 新Fragment需要设置指定的转场动画。
- 跳转时要添加addToBackStack,这样返回时才能正常pop,pop要使用getFragmentManager(),返回才能有转场动画效果。
- 要使用27.0.0以上的support包,因为新的support转场动画使用的是support包里的transition,可以参考官方support的releaseNote描述:Fragment can use support library versions of Transition for fragment transitions, including shared-element transitions.
共享元素延时加载情况
经常会有如下场景,下一个页面的共享元素需要等网络接口返回后才能确定具体的位置和大小,这样才能完成转场动画,此时,就不能直接执行转场,需要做一些处理:
//首先在onCreate方法里暂停转场
supportPostponeEnterTransition();
//然后在布局完成后,也就是合适的时机恢复转场
supportStartPostponedEnterTransition();
总结
在适当场景下添加转场动画,可以提高用户体验。但不要在一个转场里添加过多的共享元素,这样反面影响了用户的注意点,感觉乱。官方提供的转场动画,快点用到项目里吧~