问题
在开发过程中遇到这样的一个问题 第一次进入详情页进入很快加载完网络数据后很快展示 而第二次直接加载缓存数据时却不是立即跳转?
问题分析
Activity的setContentView最终会调用phoneWindow的setContentView方法如下
@Override
public void setContentView(View view, ViewGroup.LayoutParams params) {
// Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window
// decor, when theme attributes and the like are crystalized. Do not check the feature
// before this happens.
if (mContentParent == null) {
installDecor();
} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
mContentParent.removeAllViews();
}
/*
* 执行跳转动画
**/
if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
view.setLayoutParams(params);
final Scene newScene = new Scene(mContentParent, view);
transitionTo(newScene);
} else {
mContentParent.addView(view, params);
}
mContentParent.requestApplyInsets();
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged();
}
}
接下来我们点点点
if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
view.setLayoutParams(params);
final Scene newScene = new Scene(mContentParent, view);
transitionTo(newScene);
}
private static void sceneChangeRunTransition(final ViewGroup sceneRoot,
final Transition transition) {
if (transition != null && sceneRoot != null) {
MultiListener listener = new MultiListener(transition, sceneRoot);
sceneRoot.addOnAttachStateChangeListener(listener);
//在这里添加了MultiListener 下面我们看一下MultiListener的实现
sceneRoot.getViewTreeObserver().addOnPreDrawListener(listener);
}
}
在这里添加了MultiListener 下面我们看一下MultiListener的实现
省去了一下无关代码
对下面的英文解释我们先翻一下
这个类是用来监听onpredraw和onattachstatechange事件。onpredraw事件主要是我们关心的,会触发过度动画。
This private utility class is used to listen for both OnPreDraw and
OnAttachStateChange events. OnPreDraw events are the main ones we care about since that's what triggers the transition to take place. OnAttachStateChange events are also important in case the view is removed from the hierarchy before the OnPreDraw event takes place; it's used to clean up things since the OnPreDraw listener didn't get called in time.
/**
* This private utility class is used to listen for both OnPreDraw and
* OnAttachStateChange events. OnPreDraw events are the main ones we care
* about since that's what triggers the transition to take place.
* OnAttachStateChange events are also important in case the view is removed
* from the hierarchy before the OnPreDraw event takes place; it's used to
* clean up things since the OnPreDraw listener didn't get called in time.
*/
private static class MultiListener implements ViewTreeObserver.OnPreDrawListener,
View.OnAttachStateChangeListener {
Transition mTransition;
ViewGroup mSceneRoot;
MultiListener(Transition transition, ViewGroup sceneRoot) {
mTransition = transition;
mSceneRoot = sceneRoot;
}
private void removeListeners() {
mSceneRoot.getViewTreeObserver().removeOnPreDrawListener(this);
mSceneRoot.removeOnAttachStateChangeListener(this);
}
@Override
public boolean onPreDraw() {
removeListeners();
// Don't start the transition if it's no longer pending.
if (!sPendingTransitions.remove(mSceneRoot)) {
return true;
}
// Add to running list, handle end to remove it
final ArrayMap<ViewGroup, ArrayList<Transition>> runningTransitions =
getRunningTransitions();
ArrayList<Transition> currentTransitions = runningTransitions.get(mSceneRoot);
ArrayList<Transition> previousRunningTransitions = null;
if (currentTransitions == null) {
currentTransitions = new ArrayList<Transition>();
runningTransitions.put(mSceneRoot, currentTransitions);
} else if (currentTransitions.size() > 0) {
previousRunningTransitions = new ArrayList<Transition>(currentTransitions);
}
currentTransitions.add(mTransition);
mTransition.addListener(new Transition.TransitionListenerAdapter() {
@Override
public void onTransitionEnd(Transition transition) {
ArrayList<Transition> currentTransitions =
runningTransitions.get(mSceneRoot);
currentTransitions.remove(transition);
}
});
mTransition.captureValues(mSceneRoot, false);
if (previousRunningTransitions != null) {
for (Transition runningTransition : previousRunningTransitions) {
runningTransition.resume(mSceneRoot);
}
}
mTransition.playTransition(mSceneRoot);
return true;
}
};
综上所述我们找到了触发activity过度动画的时机就是整个View树测量完成,在绘制之前触发predrawlistener的时候进行调整
未完待续