今天做项目的时候用到了Transition动画。也就是android5.0中出现的。基本的使用方式我们都知道是用TransitionName在两个activity中进行绑定。现在的场景是activityA中有个recyclerview,点击item的时候跳转到activityB。B中是一个类似全屏的viewpager。item中的图片元素共享的viewpager指定的Item上。并且viewpager可以动态删除item,返回的A的时候item也要同步数量。

想想实现功能应该不难。难的地方在于两个页面的item数量不一样,共享元素view的位置可能也不一样。如果B当中已经改变了item的数量和选中的viewpager的position的位置,A那边的共享元素该如何改变呢。现在开始展现真正的轮子了。

1、基本使用

设置两个activity的style中 android:windowContentTransitions 为true

false
true
true
false
true

共享元素关键点是绑定的view和view的TransitionName,那么我们就要给itemView设置transitionName了。这里我们使用url给item设置TransitionName,如果item的url一样那么可以自己想想,保持唯一就行了。

ViewCompat.setTransitionName(holder.itemView, ims.get(position).url);

ViewCompat是support兼容类。不然就要写一堆判断了很是烦。

启动activity绑定transitionView,将list和选中的item传入

ActivityOptionsCompat options =
ActivityOptionsCompat.makeSceneTransitionAnimation(A.this,
holder.itemView, ViewCompat.getTransitionName(holder.itemView));
Intent intent = new Intent(A.this, B.class);
intent.putExtra(B.INTENT_SELECT_POSITION, imgs.get(holder.getAdapterPosition()));
intent.putParcelableArrayListExtra(B.INTENT_IMGS, imgs);
ActivityCompat.startActivityForResult(A.this, intent, REQUEST_CODE_PREVIEW, options.toBundle());

之后我们到了B的Activity,这里我们不要为viewpager的每个itemview设置transitionName,我们直接给viewpager设置transitionName,以为viewpager选中页的两边都会加载view,处理起来稍微有点麻烦,直接设置viewpager的transitionName反而方便连贯。

//currentItem是我从上个页面传进来的Item
ViewCompat.setTransitionName(viewPager, currentItem.url);
....
//设置选中的item
viewPager.setCurrentItem(i);
@Override
public void onBackPressed() {
checkFinish(RESULT_OK);
}
/**
* 通过转场动画finish
*/
private void checkFinish(int resultCode) {
Intent intent = new Intent();
intent.putParcelableArrayListExtra(INTENT_IMGS, imgs);
setResult(resultCode, intent);
supportFinishAfterTransition();
}

2、进阶使用

基本的点击返回效果我们就搞好了,可是还是会出现动画不连贯的效果。一头雾水的搜了一下

//如果共享元素需要通过AsyncTask,一个Loader,或其他类似的进行数据加载在被调用的
//Activity决定它们最终的表现,数据被分发返回到主线程之前框架可以开始过渡。
supportPostponeEnterTransition()//暂时阻止共享元素过渡
supportStartPostponedEnterTransition//确认元素都已经摆放完成恢复过度

因为viewpager.setCurrentItem之后UI毕竟是在默认300ms完成绘制。所以我们要等待viewpager完全展示完成才能进行过渡动画的渲染。

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
supportPostponeEnterTransition();
....//更新ui
//监听viewpager布局树已经绘制完成
viewPager.getViewTreeObserver().addOnGlobalLayoutListener(new
ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
viewPager.getViewTreeObserver().removeOnGlobalLayoutListener(this);
//释放
supportStartPostponedEnterTransition();
}
});
}

这样我们的连贯效果就实现了。

3、完成实现

因为我们知道B页面是带有删除和翻页功能的,所以我们就要进行页面之间切换时候的ui数据整理。我试着在onActivityResult中用supportPostponeEnterTransition和supportStartPostponedEnterTransition实现数据的变换,发现效果并没有实现,onActivityResult在过渡动画后才调用。所以还是搜索一下

/**
* Called when an activity you launched with an activity transition exposes this
* Activity through a returning activity transition, giving you the resultCode
* and any additional data from it. This method will only be called if the activity
* set a result code other than {@link #RESULT_CANCELED} and it supports activity
* transitions with {@link Window#FEATURE_ACTIVITY_TRANSITIONS}.
*
* 
The purpose of this function is to let the called Activity send a hint about
* its state so that this underlying Activity can prepare to be exposed. A call to
* this method does not guarantee that the called Activity has or will be exiting soon.
* It only indicates that it will expose this Activity's Window and it has
* some data to pass to prepare it.
*
* @param resultCode The integer result code returned by the child activity
* through its setResult().
* @param data An Intent, which can return result data to the caller
* (various data can be attached to Intent "extras").
*/
public void onActivityReenter(int resultCode, Intent data) {
//当你使用了过度动画返回时候就会调用此方法,通过supportPostponeEnterTransition和supportStartPostponedEnterTransition可以延缓动画的发生
//只要resultCode不是RESULT_CANCELED并且 Window#FEATURE_ACTIVITY_TRANSITIONS这个已经在theme里设置了
}
@Override
public void onActivityReenter(int resultCode, Intent data) {
if (resultCode == RESULT_OK && data != null) {
//针对classloader回收 我这里出现了这个问题,如果没有请忽略
data.setExtrasClassLoader(getClass().getClassLoader());
ArrayList limgs = data.getParcelableArrayListExtra(B.INTENT_IMGS);
imgs = limgs;
adapter.setIms(imgs);
adapter.notifyDataSetChanged();
supportPostponeEnterTransition();
binding.recyclerview.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
recyclerview.getViewTreeObserver().removeOnGlobalLayoutListener(this);
supportStartPostponedEnterTransition();
}
});
}
super.onActivityReenter(resultCode, data);
}

满怀希望的试一试。结果懵逼,切换了还是没效果。。。==。B页面的共享viewpager的TransitionName还是原来的。我们要在返回的时候设置viewpager的TransitionName为当前选中的item的url。

B中

/**
* 通过转场动画finish
*/
private void checkFinish(int resultCode) {
Intent intent = new Intent();
setEnterShareCallback(currentItem);
intent.putParcelableArrayListExtra(INTENT_IMGS, imgs);
setResult(resultCode, intent);
supportFinishAfterTransition();
}
private void setEnterShareCallback(String url) {
//设置共享元素们的回调
setEnterSharedElementCallback(new SharedElementCallback() {
@Override
public void onMapSharedElements(List names, Map sharedElements) {
sharedElements.clear();
names.clear();
if (currentItem==null){
return;
}
names.add(url);
sharedElements.put(url,viewPager);
}
});
}

哈哈 。可以了。不过最后一步中,感觉只需要设置viewpager的TransitionName就行了,没试过。有时间再试试吧。

4、遇到的问题

项目加载网络图片会出现一些问题。如果使用glide的话要禁用动画加载dontAnimate。

还有部分时候会出现viewpager翻页会出现返回动画失效的问题,我们在返回的时候需要进行view的替换

@Override
protected void onCreate(Bundle savedInstanceState) {
setExitSharedElementCallback(new SharedElementCallback() {
@Override
public void onMapSharedElements(List names, Map sharedElements) {
//isBack为成员变量 如果启动B页面设置为false,onActivityReenter后为true
if (isBack) {
names.clear();
names.add(shareName);
sharedElements.clear();
sharedElements.put(shareName, shareView);
}
}
});
}
public void onActivityReenter(){
//更新adapter
.......
recyclerview.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
notifyCallView(data);
recyclerview.getViewTreeObserver().removeOnGlobalLayoutListener(this);
supportStartPostponedEnterTransition();
}
});
}
private void notifyCallView(Intent data) {
//遍历recyclerview.getchildCount 从B页面返回的url值
.....
shareName = url;
shareView = viewHolder.itemView;
}

over

点击数:735