Fragment源码解读

Fragment 源码解读和使用。

中间部分: FrameLayout(ViewGroup) +Fragment, 选择某个tab再加载其里面的数据。需要左右滑动,也可以给Fragment切换添加动画。
而非ViewPager+Fragment。 要设置Adapter,除非设置setOffscreenPageLimit(4) 预加载4个页面。适合左右滑动场景。
TabHostFragment

简介--- Fragment片段。

Android 3.0以上引入了。
自己的生命周期:onAttach()/ onCreateView() 传递资源xml设置UI /onDestroyView() ...

通过代码方式添加到某个ViewGroup中。

FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
FindFragment fragment = new FindFragment();
// 参数1:要放置片段的位置,有资源id指定。 替换replace()
fragmentTransaction.add(R.id.fragment_container, fragment);  
fragmentTransaction.commit();

add方法分析。

add(containerId, fragment); 只是设置了一些参数。调用commit方法,才会执行其逻辑。
-- doAddOp(containerViewId, fragment, null, OP_ADD);

// FragmentTransaction.java
void doAddOp(int containerViewId, Fragment fragment, @Nullable String tag, int opcmd) {
    final Class<?> fragmentClass = fragment.getClass(); // 反射
    final int modifiers = fragmentClass.getModifiers();
    if(tag!=null) {
        fragment.mTag = tag;
    }
    if (containerViewId != 0) {
        fragment.mContainerId = fragment.mFragmentId = containerViewId;
    }
    addOp(new Op(opcmd, fragment)); // 添加到集合中。
}
// ArrayList<Op> mOps = new ArrayList<>();
void addOp(Op op) { 
    mOps.add(op);
    op.mEnterAnim = mEnterAnim;
    op.mExitAnim = mExitAnim;
    op.mPopEnterAnim = mPopEnterAnim;
    op.mPopExitAnim = mPopExitAnim;
}

commit() 提交方法
-- commitInternal(false)
-- mManager.enqueueAction(this, allowStateLoss)
-- mHost.getHandler().post(mExecCommit); // 使用异步
-- execPendingActions()
-- startPendingDeferredFragments() //
-- performPendingDeferredStart(fragment)
-- moveToState(f, mCurState, 0, 0, false) // 移动到当前状态。

case Fragment.INITIALIZING:
-- f.onAttach(mHost.getContext())-->onAttach() // 调用Fragment的attach生命周期函数。
-- mHost.onAttachFragment(f)
-- f.performCreate(f.mSavedFragmentState) // 执行onCreate生命周期。
-- f.mView = f.performCreateView()--> 回调onCreateView()方法,返回布局。
-- f.onViewCreated(f.mView, f.mSavedFragmentState)

case Fragment.CREATED:
-- container = (ViewGroup)mContainer.onFindViewById(f.mContainerId)
-- f.mView = f.performCreateView() // 拿到首页的界面。???调用两次?
-- container.addView(f.mView)
-- f.onViewCreated(f.mView, f.mSavedFragmentState)
-- f.performActivityCreated(f.mSavedFragmentState)


replace 替换方法分析。 support-V4:23.4.0

总结:把之前的Fragment移除,重新执行Fragment的生命周期(会重新绘制UI界面)。
所以之前的状态就不存在了。

-- replace(viewId, fragment)
-- replace(viewId, fragment, null)
-- doAddOp(containerViewId, fragment, tag, OP_REPLACE);

BackStackRecord.java
-- case OP_REPLACE:
-- Fragment old = mManager.mAdded.get(i) // 遍历拿到老的,最后一个Fragment。
-- op.removed.add(old);
mManager.removeFragment(old, transition, transitionStyle) // 会把上一个Fragment被移除。执行其onDestroy
mManager.addFragment(f, false) // 把新的Fragment加进来。

case OP_REPLACE: {
        final Fragment f = op.fragment;
        final int containerId = f.mContainerId;
        boolean alreadyAdded = false;
        for (int i = added.size() - 1; i >= 0; i--) {
            final Fragment old = added.get(i); // 拿到最后一个
            if (old.mContainerId == containerId) {
                if (old == f) {
                    alreadyAdded = true;
                } else {
                    // This is duplicated from above since we only make
                    // a single pass for expanding ops. Unset any outgoing primary nav.
                    if (old == oldPrimaryNav) {
                        mOps.add(opNum, new Op(OP_UNSET_PRIMARY_NAV, old));
                        opNum++;
                        oldPrimaryNav = null;
                    }
                    final Op removeOp = new Op(OP_REMOVE, 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;

6. 解决思路

如果容器中没有就去add添加;否则,就去显示(显示之前需要把所有已经添加的Fragment不显示(隐藏hide))
replace?
先隐藏当前所有 Fragment,fragmentManager.getFragments() 拿到容器所有的。

7.代码封装

package com.tom.joke.fragment;
import java.util.List;
import androidx.annotation.IdRes;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;

/**
 * 封装代码
 */
public class FragmentManagerHelper {
    private FragmentManager mFragmentManager; // 管理类
    private int mContainerViewId; // 容器布局 viewId

    public FragmentManagerHelper(@Nullable FragmentManager fragmentManager,
                                 @IdRes int containerViewId) {
        this.mFragmentManager = fragmentManager;
        this.mContainerViewId = containerViewId;
    }

    // 初始化加载Fragment
    public void add(Fragment fragment) {
        FragmentTransaction fTransaction = mFragmentManager.beginTransaction();
        fTransaction.add(mContainerViewId, fragment);
        fTransaction.commit();
    }

    // 切换显示Fragment。
    public void switchFragment(Fragment fragment) {
        FragmentTransaction fragmentTransaction = mFragmentManager.beginTransaction();
        // 先隐藏已添加的所有Fragment
        List<Fragment> fragments = mFragmentManager.getFragments();
        for(Fragment f: fragments) {
            fragmentTransaction.hide(f);
        }
        // 2.容器中没有,就添加add;否则显示show。
        if (!fragments.contains(fragment)) {
            fragmentTransaction.add(mContainerViewId, fragment);
        }else {
            fragmentTransaction.show(fragment);
        }
        // fragmentTransaction.replace(R.id.main_tab_fl, mHomeFragment); // 替换Fragment
        fragmentTransaction.commit();
    }

}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,607评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,047评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,496评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,405评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,400评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,479评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,883评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,535评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,743评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,544评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,612评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,309评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,881评论 3 306
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,891评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,136评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,783评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,316评论 2 342

推荐阅读更多精彩内容