下面介绍的便是RecyclerView 到 ViewPager的Shared Element transition动画,如朋友圈那样的。
SourceActivity(RecyclerView) ---> DestinationActivity(ViewPager)
先阅读官方的Shared Element Transitions文档
总共5个步骤:
- Enable window content transitions in your theme.
- Specify a shared elements transition in your style.
- Define your transition as an XML resource.
- Assign a common name to the shared elements in both layouts with the android:transitionName attribute.
- Use the ActivityOptions.makeSceneTransitionAnimation() method.
在主题中启动 window content transitions动画, 设置Shared Element transition 动画, 路径
res/values-v21/styles.xml
< item name="android:windowContentTransitions">true < item name="android:windowSharedElementEnterTransition">@transition/change_image_transform < item name="android:windowSharedElementExitTransition">@transition/change_image_transform
定义 Shared Element Transition 动画, 路径
res/transition/change_image_transform.xml
< ?xml version="1.0" encoding="utf-8"?> < transitionSet xmlns:android="http://schemas.android.com/apk/res/android"> < changeBounds/> < changeImageTransform/> < /transitionSet>
RecyclerView, ViewPager都用到Adpter,自然不能在xml布局中设置 transitionName 了。
利用 View.setTransitionName()动态设置,在Adapter中绑定视图时设置transitionName。
做了初步了解后,应该知道Shared Element transition动画是需要一个一对一的关系来确定动画的。
下面的步骤也不可少:
一、 每个View应该有唯一 transitionName,不做详解。
二、 需要等待 ViewPager 开始显示图片后才启用转场动画。
//简化代码
public class DestinationActivity extends AppCompatActivity {
protected void onCreate(Bundle savedInstanceState) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
//延迟动画
postponeEnterTransition();
}
}
public class PhotoAdapter extends PagerAdapter {
public Object instantiateItem(ViewGroup container, int position) {
ImageView imageView = new ImageView(container.getContext());
//....省略
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
String name = container.getContext()
.getString(R.string.transition_name, adapterPosition, position);
imageView.setTransitionName(name);
if (position == current) {
//初始化当前显示的ImageView时,设置一个callback开启转场动画
setStartPostTransition(imageView);
}
}
}
}
// @TargetApi(21)
private void setStartPostTransition(final View sharedView) {
sharedView.getViewTreeObserver().addOnPreDrawListener(
new ViewTreeObserver.OnPreDrawListener() {
// @Override
public boolean onPreDraw() {
sharedView.getViewTreeObserver().removeOnPreDrawListener(this);
startPostponedEnterTransition();
return false;
}
});
}
}
三、 ViewPager 中左右滑动后返回时要通知 SourceActivity.class。
SourceActivity.onActivityReenter();
startActivityForResult();
setResult(RESULT_OK, intent);
这三个函数配合即可在 SourceActivity.class 中获取到变动通知。
// @Override
public void onActivityReenter(int resultCode, Intent data) {
super.onActivityReenter(resultCode, data);
if (resultCode == RESULT_OK && data != null) {
exitPosition = data.getIntExtra("exit_position", enterPosition);
}
}
四、 既然有所变动,那么原先一对一的关系有所变动了,下面两个函数可处理这种情况:
public class DestinationActivity extends AppCompatActivity {
//只有在ViewPager左右滑动后才需要在关闭时设置callback
//view 为转场对象 ImageView
// @TargetApi(21)
private void setSharedElementCallback(final View view) {
setEnterSharedElementCallback(new SharedElementCallback() {
// @Override
public void onMapSharedElements(List names, Map sharedElements) {
names.clear();
sharedElements.clear();
names.add(view.getTransitionName());
sharedElements.put(view.getTransitionName(), view);
}
});
}
}
//exitPosition 是在 SourceActivity.onActivityReenter() 取得具体值的。
public class SourceActivity extends AppCompatActivity {
// @TargetApi(21)
private void setCallback(final int enterPosition) {
this.enterPosition = enterPosition;
setExitSharedElementCallback(new SharedElementCallback() {
// @Override
public void onMapSharedElements(List names, Map sharedElements) {
if (exitPosition != enterPosition && names.size() > 0 && exitPosition < sharedViews.length) {
names.clear();
sharedElements.clear();
View view = sharedViews[exitPosition];
names.add(view.getTransitionName());
sharedElements.put(view.getTransitionName(), view);
}
setExitSharedElementCallback((SharedElementCallback) null);
sharedViews = null;
}
});
}
}
滑动返回实现
- 为 DestinationActivity.class 设置透明背景主题, 路径
/res/values/styles.xml
< style name="AppTheme.Transparent" parent="AppTheme">
< item name="android:windowBackground">@android:color/transparent
< item name="android:windowIsTranslucent">true
< /style>
- 定义一个 DismissFrameLayout 用来包裹 ImageView, 实现手势检测, 动态更新 ImageView 位置, 动态设置 DestinationActivity.class 背景透明度。