Fragmentaion框架是一个很优秀的框架,我们有2个项目都使用过,目前无不良反应,BUG呢还是有一些,不过不妨碍它的优秀。
项目地址:https://github.com/YoKeyword/Fragmentation 点击打开链接
关注好几千,群众的眼光是雪亮的。
它有啥作用呢?
Fragment是可以让你的app纵享丝滑的设计,如果你的app想在现在基础上性能大幅度提高,并且占用内存降低,同样的界面Activity占用内存比Fragment要多,响应速度Fragment比Activty在中低端手机上快了很多,甚至能达到好几倍!如果你的app当前或以后有移植平板等平台时,可以让你节省大量时间和精力。
为"单Activity + 多Fragment的架构", "多模块Activity + 多Fragment的架构"而生,帮你简化使用过程,轻松解决各种复杂嵌套等问题,修复了官方Fragment库存在的一些BUG。
这句话是作者原话,意思就是说不用这个框架也可以实现它的效果,由于这个框架都封装好了我们想用的方法,可以让代码更简单,使用的感受呢就是纵享丝滑。
想详细了解,可以看看作者的全面解析博客
http://www.jianshu.com/p/d9143a92ad94 点击打开链接
下面开始进入正文
很久没更新这个框架了,我看了下,最新的和老版本核心代码都一样的,下图结构是老版本的,不过后面的代码解析都用的目前最新的版本
导入源码可以看到总共分为4个部分,最外层有SupportActivity、SupportFraggment 等,里面有3个部分:anim、debug、
helper。
最外层的SupportActivity、SupportFragment是我们需要直接继承的基类,
animi 用于SupportFragment 直接的转场动画,
debug 顾名思义用于帮助查找框架中的bug,
helper 一些辅助类。
下面我们看下ISupport,
interface ISupport {
/**
* 加载根Fragment, 即Activity内的第一个Fragment 或 Fragment内的第一个子Fragment
*
* @param containerId 容器id
* @param toFragment 目标Fragment
*/
void loadRootFragment(int containerId, SupportFragment toFragment);
/**
* 以replace方式加载根Fragment
*/
void replaceLoadRootFragment(int containerId, SupportFragment toFragment, boolean addToBack);
/**
* 加载多个根Fragment
*
* @param containerId 容器id
* @param toFragments 目标Fragments
*/
void loadMultipleRootFragment(int containerId, int showPosition, SupportFragment... toFragments);
一个定义了各种以后会直接使用到的常用方法,
ISupportFragment,继承ISupport的一个接口
interface ISupportFragment extends ISupport {
/**
* replace目标Fragment, 主要用于Fragment之间的replace
*
* @param toFragment 目标Fragment
* @param addToBack 是否添加到回退栈
*/
void replaceFragment(SupportFragment toFragment, boolean addToBack);
/**
* @return 位于栈顶的子Fragment
*/
SupportFragment getTopChildFragment();
/**
* @return 当前Fragment的前一个Fragment
*/
SupportFragment getPreFragment();
在ISupport的基础上又定义了一些方法,因为定义的这些方法是在实现它的SupportFragment 中使用的。
public class SupportFragment extends Fragment implements ISupportFragment {
// LaunchMode
public static final int STANDARD = 0;
public static final int SINGLETOP = 1;
public static final int SINGLETASK = 2;
而SupportActivity是直接实现ISupport的
public class SupportActivity extends AppCompatActivity implements ISupport, SensorEventListener {
private FragmentationDelegate mFragmentationDelegate;
private LifecycleHelper mLifecycleHelper;
private ArrayList mFragmentLifecycleCallbacks;
private FragmentAnimator mFragmentAnimator;
ISupport及ISupportFragment里面定义的方法就是我们以后需要直接使用的主要方法了,
先来看下SupportActivity,
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mFragmentationDelegate = getFragmentationDelegate();
mFragmentAnimator = onCreateFragmentAnimator();
}
初始化中创建了FragmentationDelegate对象和转场动画对象,FragmentationDelegate是干嘛的呢?
SupportFragment和SupportActivity中核心方法都会用到FragmentaionDelegate,
说白了整个框架的核心业务都在FragmentaionDelegate里面,主要包含了这些事务:
加载根Fragment、加载多个根Fragment、替换根Fragment、子Fragment之间的替换、隐藏显示Fragment、启动目标Fragment、
得到位于栈顶Fragment、获取栈内的Fragment、Fragment出栈、出栈到目标fragment、获取栈顶的子Fragment、
获取当前Fragment的前一个Fragment、子栈内Fragment出栈
现在开始一个一个方法突破吧!
一、加载根Fragment
void loadRootTransaction(FragmentManager fragmentManager, int containerId, ISupportFragment to, boolean addToBackStack, boolean allowAnimation) {
fragmentManager = checkFragmentManager(fragmentManager, null);
if (fragmentManager == null) return;
bindContainerId(containerId, to);
start(fragmentManager, null, to, to.getClass().getName(), !addToBackStack, null, allowAnimation, TYPE_REPLACE);
}
首先检查了下fragmentManager存不存在,不存在就没后面的,所以一般情况下是存在的,
private void bindContainerId(int containerId, ISupportFragment to) {
Bundle args = getArguments((Fragment) to);
args.putInt(FRAGMENTATION_ARG_CONTAINER, containerId);
}
bindContainerId() 把视图容器id存在目标Fragment的Arguments中,
private void start(FragmentManager fragmentManager, final ISupportFragment from, ISupportFragment to, String toFragmentTag,
boolean dontAddToBackStack, ArrayList sharedElementList, boolean allowRootFragmentAnim, int type) {
FragmentTransaction ft = fragmentManager.beginTransaction();
boolean addMode = (type == TYPE_ADD || type == TYPE_ADD_RESULT || type == TYPE_ADD_WITHOUT_HIDE);
Fragment fromF = (Fragment) from;
Fragment toF = (Fragment) to;
Bundle args = getArguments(toF);
args.putBoolean(FRAGMENTATION_ARG_REPLACE, !addMode);
if (sharedElementList == null) {
if (addMode) { // Replace mode forbidden animation, the replace animations exist overlapping Bug on support-v4.
TransactionRecord record = to.getSupportDelegate().mTransactionRecord;
if (record != null && record.targetFragmentEnter != Integer.MIN_VALUE) {
ft.setCustomAnimations(record.targetFragmentEnter, record.currentFragmentPopExit,
record.currentFragmentPopEnter, record.targetFragmentExit);
args.putInt(FRAGMENTATION_ARG_CUSTOM_END_ANIM, record.targetFragmentEnter);
} else {
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
}
} else {
args.putInt(FRAGMENTATION_ARG_ROOT_STATUS, SupportFragmentDelegate.STATUS_ROOT_ANIM_DISABLE);
}
} else {
args.putBoolean(FRAGMENTATION_ARG_IS_SHARED_ELEMENT, true);
for (TransactionRecord.SharedElement item : sharedElementList) {
ft.addSharedElement(item.sharedElement, item.sharedName);
}
}
if (from == null) {
ft.replace(args.getInt(FRAGMENTATION_ARG_CONTAINER), toF, toFragmentTag);
if (!addMode) {
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
args.putInt(FRAGMENTATION_ARG_ROOT_STATUS, allowRootFragmentAnim ?
SupportFragmentDelegate.STATUS_ROOT_ANIM_ENABLE : SupportFragmentDelegate.STATUS_ROOT_ANIM_DISABLE);
}
} else {
if (addMode) {
ft.add(from.getSupportDelegate().mContainerId, toF, toFragmentTag);
if (type != TYPE_ADD_WITHOUT_HIDE) {
ft.hide(fromF);
}
} else {
ft.replace(from.getSupportDelegate().mContainerId, toF, toFragmentTag);
}
}
if (!dontAddToBackStack && type != TYPE_REPLACE_DONT_BACK) {
ft.addToBackStack(toFragmentTag);
}
supportCommit(fragmentManager, ft);
}
接下来这个start()就稍微复杂些了,主要业务都在里面,也是一个很公用的方法,包含了fragment的添加、替换,
参数也比较多,
FragmentManager fragmentManager:Fragment管理器
ISupportFragment from:当前Fragment
ISupportFragment to: 目标Fragment,即需要开启的Fragment,方式可能是add或replace
String toFragmentTag:目标Fragment的tag,通过这个tag可以从栈中找到它
boolean dontAddToBackStack:是否不允许添加入栈,感觉有点别扭,直接名字叫是否允许入栈多好
ArrayList
boolean allowRootFragmentAnim:是否允许根Fragment动画
int type:添加Fragment的类型
这个方法主要分为3块,
第一块,判断有无过渡动画元素,添加相应的动画
第二块,当前fragment是否为空,然后做出add或replace fragment
第三块,是否允许把目标fragment添加入栈
然后提交事务。
二、加载多个根Fragment
void loadMultipleRootTransaction(FragmentManager fragmentManager, int containerId, int showPosition, ISupportFragment... tos) {
fragmentManager = checkFragmentManager(fragmentManager, null);
if (fragmentManager == null) return;
FragmentTransaction ft = fragmentManager.beginTransaction();
for (int i = 0; i < tos.length; i++) {
Fragment to = (Fragment) tos[i];
Bundle args = getArguments(to);
args.putInt(FRAGMENTATION_ARG_ROOT_STATUS, SupportFragmentDelegate.STATUS_ROOT_ANIM_DISABLE);
bindContainerId(containerId, tos[i]);
String toName = to.getClass().getName();
ft.add(containerId, to, toName);
if (i != showPosition) {
ft.hide(to);
}
}
supportCommit(fragmentManager, ft);
}
这个方法比start()逻辑少点,因为这个方法没它公用的地方多,整个方法主要就要一个for循环,然后一个一个的添加,显示指定位置的Fragment,其他添加的都隐藏。
三、启动一个Fragment
public void start(ISupportFragment toFragment) {
start(toFragment, ISupportFragment.STANDARD);
}
/**
* @param launchMode Similar to Activity's LaunchMode.
*/
public void start(final ISupportFragment toFragment, @ISupportFragment.LaunchMode int launchMode) {
mTransactionDelegate.dispatchStartTransaction(mFragment.getFragmentManager(), mSupportF, toFragment, 0, launchMode, TransactionDelegate.TYPE_ADD);
}
void dispatchStartTransaction(FragmentManager fragmentManager, ISupportFragment from, ISupportFragment to, int requestCode, int launchMode, int type) {
fragmentManager = checkFragmentManager(fragmentManager, from);
if (fragmentManager == null) return;
checkNotNull(to, "toFragment == null");
if (from != null) {
if (from.getSupportDelegate().mContainerId == 0) {
Fragment fromF = (Fragment) from;
if (fromF.getTag() != null && !fromF.getTag().startsWith("android:switcher:")) {
throw new RuntimeException("Can't find container, please call loadRootFragment() first!");
}
}
bindContainerId(from.getSupportDelegate().mContainerId, to);
from = SupportHelper.getTopFragment(fragmentManager, from.getSupportDelegate().mContainerId);
}
// process SupportTransaction
String toFragmentTag = to.getClass().getName();
boolean dontAddToBackStack = false;
ArrayList sharedElementList = null;
TransactionRecord transactionRecord = to.getSupportDelegate().mTransactionRecord;
if (transactionRecord != null) {
if (transactionRecord.tag != null) {
toFragmentTag = transactionRecord.tag;
}
dontAddToBackStack = transactionRecord.dontAddToBackStack;
if (transactionRecord.sharedElementList != null) {
sharedElementList = transactionRecord.sharedElementList;
// Compat SharedElement
FragmentationHack.reorderIndices(fragmentManager);
}
}
if (type == TYPE_ADD_RESULT || type == TYPE_ADD_RESULT_WITHOUT_HIDE) {
saveRequestCode((Fragment) to, requestCode);
}
if (handleLaunchMode(fragmentManager, from, to, toFragmentTag, launchMode)) return;
if (type == TYPE_ADD_WITH_POP) {
startWithPop(fragmentManager, from, to);
} else {
start(fragmentManager, from, to, toFragmentTag, dontAddToBackStack, sharedElementList, false, type);
}
}
这也是非常核心的一个方法,将会是用得最多的方法,
如果fromFragment为空的则用加载根Fragment的方式开启,不为空就把视图容器id传给目标fragment,
接下来取出目标Fragment中的过渡动画相关的参数,然后如果添加类型为带requestCode,就调用
saveRequestCode((Fragment) to, requestCode)把这个requestCode保存起来,
if (handleLaunchMode(fragmentManager, from, to, toFragmentTag, launchMode)) return;
如果启动模式为SINGLETOP 或 SINGLETASK,就return掉,跟Activity的启动模式一样,
接下来根据条件复用startWithPop()或start()
四、出栈当前Fragment开启新Fragment
/**
* Launch a fragment while poping self.
*/
public void startWithPop(ISupportFragment toFragment) {
mDelegate.startWithPop(toFragment);
}
/**
* Launch a fragment while poping self.
*/
public void startWithPop(ISupportFragment toFragment) {
mTransactionDelegate.dispatchStartTransaction(mFragment.getFragmentManager(), mSupportF, toFragment, 0, ISupportFragment.STANDARD, TransactionDelegate.TYPE_ADD_WITH_POP);
}
if (type == TYPE_ADD_WITH_POP) {
startWithPop(fragmentManager, from, to);
} else {
start(fragmentManager, from, to, toFragmentTag, dontAddToBackStack, sharedElementList, false, type);
}
下面好好看下startWithPop(),如果FragmentManager正在执行某事务,那么把executeStartWithPop()加入消息队列,否则
直接调用executeStartWithPop()
private void startWithPop(final FragmentManager fragmentManager, final ISupportFragment from, final ISupportFragment to) {
if (FragmentationHack.isExecutingActions(fragmentManager)) {
mHandler.post(new Runnable() {
@Override
public void run() {
executeStartWithPop(fragmentManager, from, to);
}
});
return;
}
executeStartWithPop(fragmentManager, from, to);
}
在看下executeStartWithPop()
private void executeStartWithPop(final FragmentManager fragmentManager, final ISupportFragment from, final ISupportFragment to) {
fragmentManager.executePendingTransactions();
final ISupportFragment preFragment = getPreFragment((Fragment) from);
final int fromContainerId = from.getSupportDelegate().mContainerId;
mockStartWithPopAnim(from, to, from.getSupportDelegate().mAnimHelper.popExitAnim);
fragmentManager.popBackStackImmediate();
mHandler.post(new Runnable() {
@Override
public void run() {
FragmentationHack.reorderIndices(fragmentManager);
if (preFragment != null && preFragment.getSupportDelegate().mContainerId == fromContainerId) {
preFragment.getSupportDelegate().start(to);
} else {
dispatchStartTransaction(fragmentManager, from, to, 0, ISupportFragment.STANDARD, TYPE_ADD);
}
}
});
}
首先获取到fromFragment即当前Fragment的前一个Fragment和容器id,mockStartWithPopAnim()执行fromFragment的退出动画,并立即从栈中移除,接着后面的业务都加入消息队列,先整理下栈的顺序,如果前一个fragment不为null且它的容器id等于当前Fragment的,就开启目标Fragment,否则目标Fragment被作为根Fragment加入栈中,因为当前Fragment被移除栈了,所以如果还有前一个Fragment就相当于是从前一个开始开启目标Fragment。
五、出栈到目标Fragment
/**
* Pop the last fragment transition from the manager's fragment
* back stack.
*
* 出栈到目标fragment
*
* @param targetFragmentClass 目标fragment
* @param includeTargetFragment 是否包含该fragment
*/
public void popTo(Class> targetFragmentClass, boolean includeTargetFragment) {
mDelegate.popTo(targetFragmentClass, includeTargetFragment);
}
/**
* Pop the last fragment transition from the manager's fragment back stack.
*
* @param targetFragmentTag Tag
* @param includeTargetFragment Whether it includes targetFragment
*/
void popTo(final String targetFragmentTag, final boolean includeTargetFragment, final Runnable afterPopTransactionRunnable, FragmentManager fragmentManager, final int popAnim) {
fragmentManager = checkFragmentManager(fragmentManager, null);
if (fragmentManager == null) return;
if (FragmentationHack.isExecutingActions(fragmentManager)) {
final FragmentManager finalFragmentManager = fragmentManager;
mHandler.post(new Runnable() {
@Override
public void run() {
executePopTo(targetFragmentTag, includeTargetFragment, afterPopTransactionRunnable, finalFragmentManager, popAnim);
}
});
return;
}
executePopTo(targetFragmentTag, includeTargetFragment, afterPopTransactionRunnable, fragmentManager, popAnim);
}
发现其实调用的是executePopTo()
private void executePopTo(final String targetFragmentTag, boolean includeTargetFragment, final Runnable afterPopTransactionRunnable, FragmentManager fragmentManager, int popAnim) {
fragmentManager.executePendingTransactions();
Fragment targetFragment = fragmentManager.findFragmentByTag(targetFragmentTag);
if (targetFragment == null) {
Log.e(TAG, "Pop failure! Can't find FragmentTag:" + targetFragmentTag + " in the FragmentManager's Stack.");
return;
}
int flag = 0;
if (includeTargetFragment) {
flag = FragmentManager.POP_BACK_STACK_INCLUSIVE;
targetFragment = (Fragment) getPreFragment(targetFragment);
}
ISupportFragment fromFragment = getTopFragment(fragmentManager);
Animation popAnimation;
if (afterPopTransactionRunnable == null && popAnim == TransactionDelegate.DEFAULT_POPTO_ANIM) {
popAnimation = fromFragment.getSupportDelegate().mAnimHelper.exitAnim;
} else {
if (popAnim == TransactionDelegate.DEFAULT_POPTO_ANIM) {
popAnimation = new Animation() {
};
popAnimation.setDuration(fromFragment.getSupportDelegate().mAnimHelper.exitAnim.getDuration());
} else if (popAnim == 0) {
popAnimation = new Animation() {
};
} else {
popAnimation = AnimationUtils.loadAnimation(mActivity, popAnim);
}
}
final int finalFlag = flag;
final FragmentManager finalFragmentManager = fragmentManager;
mockPopAnim(fromFragment, (ISupportFragment) targetFragment, popAnimation, afterPopTransactionRunnable != null, new Callback() {
@Override
public void call() {
popToFix(targetFragmentTag, finalFlag, finalFragmentManager);
if (afterPopTransactionRunnable != null) {
mHandler.post(new Runnable() {
@Override
public void run() {
mPopToTempFragmentManager = finalFragmentManager;
afterPopTransactionRunnable.run();
mPopToTempFragmentManager = null;
}
});
}
}
});
}
这个方法较长,includeTargetFragment是否包含目标fragment,这里的作用是如果包含,到时就把栈内目标fragment及以上的fragment都移除,
如果不包含,就把栈内目标fragment以上的fragment都移除;
当移除栈的事务完成后是否还有业务需要处理,就通过afterPopTransactionRunnable来判断;
最后,mockPopAnim(),用于处理出栈后的相关动画,即先出栈再播放动画,如果还有afterPopTransactionRunnable业务,随便把这个业务处理了再播放动画。
以上应该就是最核心的代码了,这个框架还有一个很重要就是对于转场动画的操作,不自定义动画也不会影响使用,这个下篇再分析吧