上篇很重要,请先阅读上篇。
FragmentTransaction
最常见的调用
getSupportFragmentManager().beginTransaction().add(xxFragment, xxTag).commit();
追踪FragmentManager
@Override
public FragmentTransaction beginTransaction() {
return new BackStackRecord(this);
}
再看BackStackRecord
public BackStackRecord(FragmentManagerImpl manager) {
mManager = manager;
}
每次初始化Transaction会创建一个后台堆栈来保存操作(可以不止一个操作)。但最常见的用法还是每次只进行一次操作,所以还是以单个操作为例。
看最常用的添加操作FragmentTransaction.add
public FragmentTransaction add(int containerViewId, Fragment fragment, String tag) {
doAddOp(containerViewId, fragment, tag, OP_ADD);
return this;
}
private void doAddOp(int containerViewId, Fragment fragment, String tag, int opcmd) {
fragment.mFragmentManager = mManager;
if (tag != null) {
// tag不允许重复
if (fragment.mTag != null && !tag.equals(fragment.mTag)) {
throw new IllegalStateException("Can't change tag of fragment "
+ fragment + ": was " + fragment.mTag
+ " now " + tag);
}
fragment.mTag = tag;
}
if (containerViewId != 0) {
// 容器id也不允许重复
if (fragment.mFragmentId != 0 && fragment.mFragmentId != containerViewId) {
throw new IllegalStateException("Can't change container ID of fragment "
+ fragment + ": was " + fragment.mFragmentId
+ " now " + containerViewId);
}
fragment.mContainerId = fragment.mFragmentId = containerViewId;
}
Op op = new Op();
op.cmd = opcmd;
op.fragment = fragment;
addOp(op);
}
回头看一下Op的结构
static final class Op {
Op next;
Op prev;
int cmd;
Fragment fragment;
int enterAnim;
int exitAnim;
int popEnterAnim;
int popExitAnim;
ArrayList removed;
}
原来是一个双端链表结构,head的prev和tail的next都是null。
添加链表就是在尾部插入新的节点。一个Transaction只能指定一个进出动画。
void addOp(Op op) {
if (mHead == null) {
mHead = mTail = op;
} else {
op.prev = mTail;
mTail.next = op;
mTail = op;
}
op.enterAnim = mEnterAnim;
op.exitAnim = mExitAnim;
op.popEnterAnim = mPopEnterAnim;
op.popExitAnim = mPopExitAnim;
mNumOp++;
}
添加add操作结束后到commit方法
public int commit() {
return commitInternal(false);
}
public int commitAllowingStateLoss() {
return commitInternal(true);
}
int commitInternal(boolean allowStateLoss) {
if (mCommitted) throw new IllegalStateException("commit already called");
if (FragmentManagerImpl.DEBUG) {
Log.v(TAG, "Commit: " + this);
LogWriter logw = new LogWriter(TAG);
PrintWriter pw = new PrintWriter(logw);
dump(" ", null, pw, null);
}
mCommitted = true;
if (mAddToBackStack) {
mIndex = mManager.allocBackStackIndex(this);
} else {
mIndex = -1;
}
mManager.enqueueAction(this, allowStateLoss);
return mIndex;
}
BackStackRecord本身是一个Runnable,commit等于执行其run方法,看add操作的case。
public void run() {
//....省略
bumpBackStackNesting(1);//计算堆栈中操作的数量
Op op = mHead;
while (op != null) {
int enterAnim = state != null ? 0 : op.enterAnim;
int exitAnim = state != null ? 0 : op.exitAnim;
switch (op.cmd) {
case OP_ADD: {
Fragment f = op.fragment;
f.mNextAnim = enterAnim;
mManager.addFragment(f, false);
} break; op = op.next;
}
mManager.moveToState(mManager.mCurState, transition, transitionStyle, true);
if (mAddToBackStack) {
mManager.addBackStackState(this);
}
}
回到FragmentManger.addFragment和moveToState,此时mManager.mCurState为Fragment.INITIALIZING,只有setRetainInstance(true)时mAddToBackStack才为true,其他的上篇已经说过不再赘述。
至此,一个add操作已经结束了。
接着看替换replace操作
case OP_REPLACE: {
Fragment f = op.fragment;
int containerId = f.mContainerId;
if (mManager.mAdded != null) {
for (int i = mManager.mAdded.size() - 1; i >= 0; i--) {
Fragment old = mManager.mAdded.get(i);
if (FragmentManagerImpl.DEBUG) Log.v(TAG,
"OP_REPLACE: adding=" + f + " old=" + old);
if (old.mContainerId == containerId) {
if (old == f) {
//新旧fragment相同,则无需再次添加
op.fragment = f = null;
} else {
//把旧的删除
if (op.removed == null) {
op.removed = new ArrayList();
}
op.removed.add(old);
old.mNextAnim = exitAnim;
if (mAddToBackStack) {
old.mBackStackNesting += 1;
if (FragmentManagerImpl.DEBUG) Log.v(TAG, "Bump nesting of "
+ old + " to " + old.mBackStackNesting);
}
mManager.removeFragment(old, transition, transitionStyle);
}
}
}
}
//添加新的
if (f != null) {
f.mNextAnim = enterAnim;
mManager.addFragment(f, false);
}
} break;
再看看hide
public void hideFragment(Fragment fragment, int transition, int transitionStyle) {
if (DEBUG) Log.v(TAG, "hide: " + fragment);
if (!fragment.mHidden) {
fragment.mHidden = true;
if (fragment.mView != null) {
Animation anim = loadAnimation(fragment, transition, false,
transitionStyle);
if (anim != null) {
setHWLayerAnimListenerIfAlpha(fragment.mView, anim);
fragment.mView.startAnimation(anim);
}
//就是把里面的视图指控
fragment.mView.setVisibility(View.GONE);
}
if (fragment.mAdded && fragment.mHasMenu && fragment.mMenuVisible) {
mNeedMenuInvalidate = true;
}
//调用change方法
fragment.onHiddenChanged(true);
}
}
show方法
public void showFragment(Fragment fragment, int transition, int transitionStyle) {
if (DEBUG) Log.v(TAG, "show: " + fragment);
if (fragment.mHidden) {
fragment.mHidden = false;
if (fragment.mView != null) {
Animation anim = loadAnimation(fragment, transition, true,
transitionStyle);
if (anim != null) {
setHWLayerAnimListenerIfAlpha(fragment.mView, anim);
fragment.mView.startAnimation(anim);
}
//show就是把view设置可见
fragment.mView.setVisibility(View.VISIBLE);
}
if (fragment.mAdded && fragment.mHasMenu && fragment.mMenuVisible) {
mNeedMenuInvalidate = true;
}
//调一下方法
fragment.onHiddenChanged(false);
}
}
过渡动画
动画是很令人头痛的东西。在FragmentManager.moveToState中有不小的影响
if (f.mAnimatingAway != null) {
// The fragment is currently being animated... but! Now we
// want to move our state back up. Give up on waiting for the
// animation, move to whatever the final state should be once
// the animation is done, and then we can proceed from there.
f.mAnimatingAway = null;
moveToState(f, f.mStateAfterAnimating, 0, 0, true);
}
这个是最开始就会判断Fragment退出时是否有动画。
再回顾一下退出的过程
case Fragment.ACTIVITY_CREATED:
if (newState < Fragment.ACTIVITY_CREATED) {
if (f.mView != null && f.mContainer != null) {
Animation anim = null;
if (mCurState > Fragment.INITIALIZING && !mDestroyed) {
anim = loadAnimation(f, transit, false,
transitionStyle);
}
//这个创建的是Transaction中添加的动画
if (anim != null) {
final Fragment fragment = f;
f.mAnimatingAway = f.mView;
f.mStateAfterAnimating = newState;
final View viewToAnimate = f.mView;
anim.setAnimationListener(new AnimateOnHWLayerIfNeededListener(
viewToAnimate, anim) {
@Override
public void onAnimationEnd(Animation animation) {
super.onAnimationEnd(animation);
if (fragment.mAnimatingAway != null) {
fragment.mAnimatingAway = null;
//这个mStateAfterAnimating是Fragment.INITIALIZING,动画结束之后又调用了moveToState
moveToState(fragment, fragment.mStateAfterAnimating,
0, 0, false);
}
}
});
f.mView.startAnimation(anim);
}
f.mContainer.removeView(f.mView);
}
f.mContainer = null;
f.mView = null;
f.mInnerView = null;
}
重新又走moveToState方法,newState为Fragment.INITIALIZING,f.mAnimatingAway != null
if (f.mAnimatingAway != null) {
//强制结束
f.mAnimatingAway = null;
moveToState(f, f.mStateAfterAnimating, 0, 0, true);
}
//设置true
if (!keepActive) {
if (!f.mRetaining) {
makeInactive(f);
} else {
//没有setReenterTransition(true),会将host置空了,注意引起no host的问题
f.mHost = null;
f.mParentFragment = null;
f.mFragmentManager = null;
}
}
最后走销毁过程。过渡动画未结束就按返回键会导致no host的异常,在support v24上已经修复过了,遇到这个问题的可以详细交流。
LoaderManager
主要与Fragment的生命周期绑定了,无需再额外管理,只要把任务和回调接口搞定其他都不用再担心。
版本兼容
实践是检验真理的唯一标准。Android系统本身的碎片话,再加上国内某些大厂深度定制,解决版本兼容问题会是繁琐且耗时的工程,需要技巧和剖析问题的耐心。
总结
大体过了一遍Fragment相关的知识,细枝末节的地方不可能只看一两篇文章就说弄懂,归根结底需要了解和调试源码。全篇也没有提到任何具体的问题和解决方案,只要熟悉了源码,回过头再去运用和调试就变得轻而易举。
所以,感谢阅读。