Fragment我想大家肯定定不会陌生的,也是我们经常会在项目中经常用到的,我们也知道如何将fragment添加到Activity中。
首先我们先看一下我们的布局:很简单,FrameLayout+(一个第三方的bottomBar用法很简单)。
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="moocollege.cn.fragmentanalysis.MainActivity">
<FrameLayout
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="@+id/bottom_bar" />
<com.ashokvarma.bottomnavigation.BottomNavigationBar
android:id="@id/bottom_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:bnbActiveColor="#0eb5c5"
android:layout_alignParentBottom="true" />
</RelativeLayout>
设置bottombar
mBottomBar = (BottomNavigationBar) findViewById(R.id.bottom_bar);
//mBottomBar一些基础配置 模式 添加内容 默认选中 添加监听
mBottomBar.setMode(BottomNavigationBar.MODE_FIXED);
mBottomBar.setBackgroundStyle(BottomNavigationBar.BACKGROUND_STYLE_STATIC);
mBottomBar.addItem(new BottomNavigationItem(R.mipmap.tab_ic_home_pressed, "首页").setInactiveIconResource(R.mipmap.tab_ic_home_default))
.addItem(new BottomNavigationItem(R.mipmap.tab_ic_dynamic_pressed, "动态").setInactiveIconResource(R.mipmap.tab_ic_dynamic_default))
.addItem(new BottomNavigationItem(R.mipmap.tab_ic_practice_pressed, "实习").setInactiveIconResource(R.mipmap.tab_ic_practice_default))
.addItem(new BottomNavigationItem(R.mipmap.tab_ic_message_pressed, "消息").setInactiveIconResource(R.mipmap.tab_ic_message_default))
.setFirstSelectedPosition(0)
.initialise();
mBottomBar.setTabSelectedListener(mBottomBarSelectedListener);
监听:
private BottomNavigationBar.OnTabSelectedListener mBottomBarSelectedListener = new BottomNavigationBar.OnTabSelectedListener() {
@Override
public void onTabSelected(int position) {
switchTo(position);
}
@Override
public void onTabUnselected(int position) {
}
@Override
public void onTabReselected(int position) {
}
};
Fragment切换之replace方法:
/**
* fragment的切换之replace方法
*
* @param position
*/
private void switchTo(int position) {
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
switch (position) {
case 0:
if (mFragmentHome == null) {
mFragmentHome = new FragmentHome();
}
fragmentTransaction.replace(R.id.container, mFragmentHome);
fragmentTransaction.commit();
break;
case 1:
if (mFragmentDynamic == null) {
mFragmentDynamic = new FragmentDynamic();
}
fragmentTransaction.replace(R.id.container, mFragmentDynamic);
fragmentTransaction.commit();
break;
case 2:
if (mFragmentPractice == null) {
mFragmentPractice = new FragmentPractice();
}
fragmentTransaction.replace(R.id.container, mFragmentPractice);
fragmentTransaction.commit();
break;
case 3:
if (mFragmentMessage == null) {
mFragmentMessage = new FragmentMessage();
}
fragmentTransaction.replace(R.id.container, mFragmentMessage);
fragmentTransaction.commit();
break;
}
}
我们来看一下效果:
Fragment添加(初始化第一个fragment)
//加载第一个fragment
//getSupportFragmentManager()可以兼容11以下的版本
FragmentManager fragmentManager = getSupportFragmentManager();
//开启事物
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
//把第一个fragment加进来
mFragmentHome = new FragmentHome();
fragmentTransaction.add(R.id.container, mFragmentHome);
//提交
fragmentTransaction.commit();
那我们的add方法干了些什么呢?
我们进入到源码发现add方法是一个抽象方法,FragmentTransaction是一个抽象类,FragmentManager同样是一个抽象类,接着我们来看看
getSupportFragmentManager()干了些什么。
return mHost.getFragmentManagerImpl();
看到这里我们猜一猜FragmentManagerImpl是不是FragmentManager的子类呢?不出意外应该是的吧,点进去果然是的:
FragmentManagerImpl中实现了beginTransaction方法,该方法返回一个
return new BackStackRecord(this);
在BackStackRecord中add返回一个FragmentTransaction
@Override
public FragmentTransaction add(int containerViewId, Fragment fragment) {
doAddOp(containerViewId, fragment, null, OP_ADD);
return this;
}
doAppOp()干了些什么呢?这其实只是设置一些参数
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方法:
@Override
public int commit() {
return commitInternal(false);
}
经过commint的后面一系列的方法之后,这中间的源码比较多,我就贴出一些方法,愿意看的可以在源码中去找,不愿意看的看主要步骤就可以了
commitInternal-->mManager.enqueueAction--> scheduleCommit()->mHost.getHandler().post(mExecCommit)->
execPendingActions()-->doPendingDeferredStart()-->startPendingDeferredFragments()-->performPendingDeferredStart
-->moveToState(f, mCurState, 0, 0, false)
最终会来FragmentManager的moveToState(f, mCurState, 0, 0, false);
void moveToState(Fragment f, int newState, int transit,
int transitionStyle, boolean keepActive){
// ... 省略部分代码
f.onAttach(mHost.getContext());
// 这个方法一调用就会执行Fragment的onAttach(Activity activity)这个生命周期方法
if (f.mParentFragment == null) {
mHost.onAttachFragment(f);
}
if (!f.mRetaining) {
f.performCreate(f.mSavedFragmentState);
// 执行生命周期onCreate(savedInstanceState);
}
f.mRetaining = false;
if (f.mFromLayout) {
ViewGroup container = null;
if (f.mContainerId != 0) {
//从activity中找到我们需要存放Fragment的ViewGroup布局
container = (ViewGroup)mContainer.onFindViewById(f.mContainerId);
if (container == null && !f.mRestored) {
throwException(new IllegalArgumentException(
"No view found for id 0x"
+ Integer.toHexString(f.mContainerId) + " ("
+ f.getResources().getResourceName(f.mContainerId)
+ ") for fragment " + f));
}
}
f.mView = f.performCreateView(f.getLayoutInflater(
f.mSavedFragmentState), null, f.mSavedFragmentState);
// 这个方法过后会执行onCreateView()生命周期且f.mView就是我们自己覆盖Fragment返回的View
if (f.mView != null) {
f.mInnerView = f.mView;
// v4包兼容11以下的版本
if (Build.VERSION.SDK_INT >= 11) {
ViewCompat.setSaveFromParentEnabled(f.mView, false);
} else {
f.mView = NoSaveStateFrameLayout.wrap(f.mView);
}
if (container != null) {
Animation anim = loadAnimation(f, transit, true,
transitionStyle);
if (anim != null) {
setHWLayerAnimListenerIfAlpha(f.mView, anim);
f.mView.startAnimation(anim);
}
// 如果ViewGroup不等于null就把从onCreateView()生命周期中获得的View添加到该布局中
// 最主要的就是这个方法,其实我们可以把Fragment理解成一个自定义的类
// 通过onCreateView()获取的到View添加到一个FragmentActivity的一个ViewGroup中
// 只不过它有自己的生命周期而已......
container.addView(f.mView);
}
// 如果是隐藏那就设置为不可见
if (f.mHidden) f.mView.setVisibility(View.GONE);
// 执行onViewCreated()生命周期方法
f.onViewCreated(f.mView, f.mSavedFragmentState);
} else {
f.mInnerView = null;
}
f.performActivityCreated(f.mSavedFragmentState);
if (f.mView != null) {
f.restoreViewState(f.mSavedFragmentState);
}
f.mSavedFragmentState = null;
}
// 代码省略......
}
}
在这里会去回调Fragment的一些生命周期
onAttach
onCreate
onCreateView会返回一个View,就是我们Fragment的布局
然后会去调用container.addView把我们刚才返回的View添加进去到我们的Activity中。
下面看看我们的replace方法干了些什么?
在在BackStackRecord中replace方法干了些什么?@Override
public FragmentTransaction replace(int containerViewId, Fragment fragment) {
return replace(containerViewId, fragment, null);
}
和add一样返回一个FragmentTransaction
add方法中:
doAddOp(containerViewId, fragment, tag, OP_ADD);
replace方法:
doAddOp(containerViewId, fragment, tag, OP_REPLACE);
两个都是调用先沟通的方法,只是我们的opcmd不同,一个是OP_ADD,一个是OP_REPLACE
private void doAddOp(int containerViewId, Fragment fragment, String tag, int opcmd) {
当我们的opcmd = OP_REPLACE的时候
case OP_REPLACE: {
Fragment f = op.fragment;
int containerId = f.mContainerId;
boolean alreadyAdded = false;
for (int i = added.size() - 1; i >= 0; i--) {
Fragment old = added.get(i);
if (old.mContainerId == containerId) {
if (old == f) {
alreadyAdded = true;
} else {
Op removeOp = new Op();
removeOp.cmd = OP_REMOVE;
removeOp.fragment = old;
removeOp.enterAnim = op.enterAnim;
removeOp.popEnterAnim = op.popEnterAnim;
removeOp.exitAnim = op.exitAnim;
removeOp.popExitAnim = op.popExitAnim;
mOps.add(opNum, removeOp);
added.remove(old);
opNum++;
}
}
}
if (alreadyAdded) {
mOps.remove(opNum);
opNum--;
} else {
op.cmd = OP_ADD;
added.add(f);
}
}
break;
遍历我们的一个fragment集合,把我们老的fragment,之前的fragment移除掉,然后就会把我们新的fragment添加进来,所以就会再次去走我们上面的add方法,fragment的生命周期就会重新再走一遍,这也就解释了我们图里面的现象,切换回来的时候,我们的页面又回到最上面。当然这样的方法体验是很不好的,需要频繁的去创建和销毁fragment,浪费内存,如果有网络请求的话,还会频繁的去请求网络数据,约会消耗流量,也是耗时的操作
所以replace源码看出,就是把之前的移除,会重新执行Fragment生命周期, 会重新绘制界面
那么我们如何解决呢?
/**
* fragment的切换之hide和show
*
* @param position
*/
private void switchTo(int position) {
FragmentManager fragmentManager = getSupportFragmentManager();
// 开启事物
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
// 获得所有的Fragment
List<Fragment> childFragments = fragmentManager.getFragments();
//隐藏所有的Fragment
for (Fragment childFragment : childFragments) {
fragmentTransaction.hide(childFragment);
}
switch (position) {
case 0:
if (mFragmentHome == null) {
mFragmentHome = new FragmentHome();
}
//如果容器里面没有我们就添加,否则显示
if (!childFragments.contains(mFragmentHome)) {
fragmentTransaction.add(R.id.container, mFragmentHome);
} else {
fragmentTransaction.show(mFragmentHome);
}
break;
case 1:
if (mFragmentDynamic == null) {
mFragmentDynamic = new FragmentDynamic();
}
if (!childFragments.contains(mFragmentDynamic)) {
fragmentTransaction.add(R.id.container, mFragmentDynamic);
} else {
fragmentTransaction.show(mFragmentDynamic);
}
break;
case 2:
if (mFragmentPractice == null) {
mFragmentPractice = new FragmentPractice();
}
if (!childFragments.contains(mFragmentPractice)) {
fragmentTransaction.add(R.id.container, mFragmentPractice);
} else {
fragmentTransaction.show(mFragmentPractice);
}
break;
case 3:
if (mFragmentMessage == null) {
mFragmentMessage = new FragmentMessage();
}
if (!childFragments.contains(mFragmentMessage)) {
fragmentTransaction.add(R.id.container, mFragmentMessage);
} else {
fragmentTransaction.show(mFragmentMessage);
}
break;
}
fragmentTransaction.commit();
}
如下:
public class FragmentManagerOperator {
private FragmentManager mFragmentManager;
private int mContainerId;
/**
* 构造函数
* @param fragmentManager 管理类FragmentManager
* @param containerId 容器布局ID
*/
public FragmentManagerOperator(@Nullable FragmentManager fragmentManager, @IdRes int containerId) {
this.mFragmentManager = fragmentManager;
this.mContainerId = containerId;
}
/**
* 添加fragment
* @param fragment
*/
public void add(Fragment fragment){
//开启事物
FragmentTransaction fragmentTransaction = mFragmentManager.beginTransaction();
fragmentTransaction.add(mContainerId, fragment);
//提交
fragmentTransaction.commit();
}
public void changeFragment(Fragment fragment){
// 开启事物
FragmentTransaction fragmentTransaction = mFragmentManager.beginTransaction();
// 先隐藏当前所有的Fragment
List<Fragment> childFragments = mFragmentManager.getFragments();
for (Fragment childFragment : childFragments) {
fragmentTransaction.hide(childFragment);
}
// 2.如果容器里面没有我们就添加,否则显示
if(!childFragments.contains(fragment)){
fragmentTransaction.add(mContainerId,fragment);
}else{
fragmentTransaction.show(fragment);
}
// 一定要commit
fragmentTransaction.commit();
}
}
使用Fragment的操作类后:
private void initData() {
mFragmentManagerOperator = new FragmentManagerOperator(getSupportFragmentManager(), R.id.container);
mFragmentHome = new FragmentHome();
mFragmentManagerOperator.add(mFragmentHome);
}
切换fragment的时候:
/**
* 封装后
*
* @param position
*/
private void switchTo(int position) {
switch (position) {
case 0:
if (mFragmentHome == null) {
mFragmentHome = new FragmentHome();
}
mFragmentManagerOperator.changeFragment(mFragmentHome);
break;
case 1:
if (mFragmentDynamic == null) {
mFragmentDynamic = new FragmentDynamic();
}
mFragmentManagerOperator.changeFragment(mFragmentDynamic);
break;
case 2:
if (mFragmentPractice == null) {
mFragmentPractice = new FragmentPractice();
}
mFragmentManagerOperator.changeFragment(mFragmentPractice);
break;
case 3:
if (mFragmentMessage == null) {
mFragmentMessage = new FragmentMessage();
}
mFragmentManagerOperator.changeFragment(mFragmentMessage);
break;
}
}
我们在别的项目中如果要操作frament直接把该类拷过去就是,很方便。