上篇播客从源码和注释说了下fragment的生命周期,这里还是接着说fragment并实现Tab切换效果,先看下运行的效果;
实现这个效果其实很简单,通过getSupportFragmentManager()返回一个FragmentManager对象,调用FragmentManager中的beginTransaction()方法返回一个FragmentTransaction对象,调用FragmentTransaction中的add方法将fragment添加到FragmentActivity中,调用FragmentTransaction中的commit()方法就实现了;
/**
* 添加Fragment
* @param fragment
*/
public void add(Fragment fragment){
FragmentTransaction fragmentTransaction = mFragmentManager.beginTransaction();
//添加fragment
fragmentTransaction.add(mComtainViewId, fragment);
fragmentTransaction.commit();
}
在进行fragment切换的时候调用FragmentTransaction 中的replace()方法和commit()方法就可以了;
/**
* 替换Fragment
* @param fragment
*/
public void replace(Fragment fragment){
FragmentTransaction fragmentTransaction = mFragmentManager.beginTransaction();
//添加fragment
fragmentTransaction.replace(mComtainViewId, fragment);
fragmentTransaction.commit();
}
就这些代码就实现了,接下来看看我们在调用这些方法的时候,源码里面干了哪些事情;
我们在调用getSupportFragmentManager获取FragmentManager对象的时候,涉及到了FragmentController、FragmentManagerImpl、FragmentHostCallback这些类;
FragmentManagerImpl继承自FragmentManager;
一开始调用的是FragmentActivity这个方法,
final FragmentController mFragments = FragmentController.createController(new HostCallbacks());
/**
* Return the FragmentManager for interacting with fragments associated
* with this activity.
*/
public FragmentManager getSupportFragmentManager() {
return mFragments.getSupportFragmentManager();
}
这里的mFragments就是FragmentController集合,接着又调用了FragmentController中的
/**
* Returns a {@link FragmentManager} for this controller.
*/
public FragmentManager getSupportFragmentManager() {
return mHost.getFragmentManagerImpl();
}
这里的mHost就是FragmentHostCallback类,最后调用了FragmentHostCallback中的
FragmentManagerImpl getFragmentManagerImpl() {
return mFragmentManager;
}
返回一个FragmentManagerImpl对象;
FragmentManager在调用beginTransaction()方法应该是调用了子类FragmentManagerImpl中的beginTransaction()方法,返回一个BackStackRecord对象,而BackStackRecord是FragmentTransaction的子类,
@Override
public FragmentTransaction beginTransaction() {
return new BackStackRecord(this);
}
拿到FragmentTransaction对象后,调用add等方法时,一看源码FragmentTransaction中的这些方法都是抽象方法,都是在BackStackRecord子类中做了相应的动作;
@Override
public FragmentTransaction add(int containerViewId, Fragment fragment, String tag) {
doAddOp(containerViewId, fragment, tag, OP_ADD);
return this;
}
调用add最终是调用了doAddOp将fragment添加进来;
private void doAddOp(int containerViewId, Fragment fragment, String tag, int opcmd) {
final Class fragmentClass = fragment.getClass();
final int modifiers = fragmentClass.getModifiers();
if (fragmentClass.isAnonymousClass() || !Modifier.isPublic(modifiers)
|| (fragmentClass.isMemberClass() && !Modifier.isStatic(modifiers))) {
throw new IllegalStateException("Fragment " + fragmentClass.getCanonicalName()
+ " must be a public static class to be properly recreated from"
+ " instance state.");
}
fragment.mFragmentManager = mManager;
if (tag != null) {
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) {
if (containerViewId == View.NO_ID) {
throw new IllegalArgumentException("Can't add fragment "
+ fragment + " with tag " + tag + " to container view with no 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);
}
调用commit()方法提交事务的时候,调用的是FragmentManager的scheduleCommit()方法,再通过FragmentHostCallBack中的getHandler返回一个Handler对象去执行,
/**
* Schedules the execution when one hasn't been scheduled already. This should happen
* the first time {@link #enqueueAction(OpGenerator, boolean)} is called or when
* a postponed transaction has been started with
* {@link Fragment#startPostponedEnterTransition()}
*/
private void scheduleCommit() {
synchronized (this) {
boolean postponeReady =
mPostponedTransactions != null && !mPostponedTransactions.isEmpty();
boolean pendingReady = mPendingActions != null && mPendingActions.size() == 1;
if (postponeReady || pendingReady) {
mHost.getHandler().removeCallbacks(mExecCommit);
mHost.getHandler().post(mExecCommit);
}
}
}
这里的mHost是FragmentHostCallBack对象,在fragment切换的时候调用replace就可以了,其实还是调用了添加的方法来实现的,
@Override
public FragmentTransaction replace(int containerViewId, Fragment fragment, String tag) {
if (containerViewId == 0) {
throw new IllegalArgumentException("Must use non-zero containerViewId");
}
doAddOp(containerViewId, fragment, tag, OP_REPLACE);
return this;
}
细心下就会发现,调用replace方法后,再切换回来的时候,页面又重新加载一遍了,
是不是感觉对于不想重新加载页面来说是不是太坑了,其实如果不想重新加载页面的话可以调用FragmentTransaction中的hide和show方法就可以了,
/**
* 切换显示Fragment
* @param fragment
*/
public void switchFragment(Fragment fragment){
FragmentTransaction fragmentTransaction = mFragmentManager.beginTransaction();
//获取当前所有的fragment
List<Fragment> childFragments = mFragmentManager.getFragments();
//先隐藏所有的fragment
for(Fragment childFragment:childFragments){
fragmentTransaction.hide(childFragment);
}
//没有的话就添加,有就显示
if(!childFragments.contains(fragment)){
//添加
fragmentTransaction.add(mComtainViewId,fragment);
}else{
fragmentTransaction.show(fragment);
}
fragmentTransaction.commit();
}
想重新加载页面的话就调用replace,不想的话就调用hide和show就可以了,最后说下相关类的关系及包含什么东西;
FragmentManagerImpl是FragmentManager的子类,提供了beginTransaction、getFragment、getFragments、putFragment等方法,
BackStackRecord是FragmentTransaction的子类,提供了add、replace、hide、show、remove等方法,
FragmentController提供了getSupportFragmentManager等方法
FragmentHostCallback类,其实fragment中很多方法的调用都走到这里,就那getActivity来说,
/**
* Return the {@link FragmentActivity} this fragment is currently associated with.
* May return {@code null} if the fragment is associated with a {@link Context}
* instead.
*/
final public FragmentActivity getActivity() {
return mHost == null ? null : (FragmentActivity) mHost.getActivity();
}
这里的mHost 其实就FragmentHostCallback,调的就是FragmentHostCallback中的getActivity返回一个activity对象,
Activity getActivity() {
return mActivity;
}
getContext()、startActivity()、startActivityForResult()等都是这样调用的;
startActivity通过调用FragmentHostCallback中的onStartActivityFromFragment方法,再由Context调用startActivity来实现的;
fragment中startActivity
/**
* Call {@link Activity#startActivity(Intent, Bundle)} from the fragment's
* containing Activity.
*/
public void startActivity(Intent intent, @Nullable Bundle options) {
if (mHost == null) {
throw new IllegalStateException("Fragment " + this + " not attached to Activity");
}
mHost.onStartActivityFromFragment(this /*fragment*/, intent, -1, options);
}
FragmentHostCallback中onStartActivityFromFragment
/**
* Starts a new {@link Activity} from the given fragment.
* See {@link FragmentActivity#startActivityForResult(Intent, int, Bundle)}.
*/
public void onStartActivityFromFragment(
Fragment fragment, Intent intent, int requestCode, @Nullable Bundle options) {
if (requestCode != -1) {
throw new IllegalStateException(
"Starting activity with a requestCode requires a FragmentActivity host");
}
mContext.startActivity(intent);
}
其他的一些东西有时间可以慢慢研究,上面这些如有写的不对欢迎交流。
源码地址:http://pan.baidu.com/s/1miDkUEG