重写FragmentTabHost,避免导航栏切换过程中再次创建内容页面Fragment

来源:互联网 发布:android安装 mysql 编辑:程序博客网 时间:2024/05/17 00:58

问题描述: 上一篇文章我们在导航栏对应的每个Fragment的onActivityCreated(…)方法中打印一个toast,在反复切换页面页面时,都显示了toast对话框。这说明,切换的时候,每个内容页面fragment再次重新创建了。预期是,第一次进入创建,随后切换进入不再创建。

解决方案: 如果之前使用radioGroup+FrameLayout实现导航栏的,想必当时也遇到过上述情况,当时我的解决思想是。新建内容页面fragment时,我就把它保存起来,给它一个标记(tag),下次再切换到这个页面时,通过这个tag字段搜索一下是否已经创建,如果已经创建就直接拿出来并显示,没有则创建并添加tag。其实,重写FragmentTabHost实现的思想也是如此,因为FragmentTabHost+FrameLayout的实现原理和radioGroup+FrameLayout的差不多。下面看FragmentTabHost的一个方法,此方法是解决问题的关键,代码如下:

private FragmentTransaction doTabChanged(String tabId, FragmentTransaction ft) {        TabInfo newTab = null;        for (int i=0; i<mTabs.size(); i++) {            TabInfo tab = mTabs.get(i);            if (tab.tag.equals(tabId)) {                newTab = tab;            }        }        if (newTab == null) {            throw new IllegalStateException("No tab known for tag " + tabId);        }        if (mLastTab != newTab) {            if (ft == null) {                ft = mFragmentManager.beginTransaction();            }            if (mLastTab != null) {                if (mLastTab.fragment != null) {                    ft.detach(mLastTab.fragment);                }            }            if (newTab != null) {                if (newTab.fragment == null) {                    newTab.fragment = Fragment.instantiate(mContext,                            newTab.clss.getName(), newTab.args);                    ft.add(mContainerId, newTab.fragment, newTab.tag);                } else {                    ft.attach(newTab.fragment);                }            }            mLastTab = newTab;        }        return ft;    }

重点看ft.detach(mLastTab.fragment)和ft.attach(newTab.fragment),先来解释几个FragmentTransaction的方法:
(1)attach(Fragment fragment)方法, 主要意思是:如果之前的Fragment通过detach(Fragment fragment)从UI分离,则会重新创建并显示。
(2)detach(Fragment fragment) 主要意思是:从UI分离(就是被切换了),就会从UI中移除,进而销毁。
(3)hide(Fragment fragment) 主要意思:隐藏Fragment,不销毁。
(4)show(Fragment fragment) 主要意思:显示隐藏的Fragment。

看了上面的四个方法后,结合doTabChanged()的内容应该可以明白,什么FragmentTabHost在切换时重新创建了Fragment。主要原因就是使用了ft.detach(mLastTab.fragment)和ft.attach(newTab.fragment)。如果我们把这两个方法用hide和show替换掉,这样就可以解决问题了。操作流程,把android.support.v4.app包下的FragmentTabHost拷贝到我们自定义控件的文件夹下,例如:我放到我项目的widget文件夹下,然后修改doTabChanged()方法。修改代码如下:

private FragmentTransaction doTabChanged(String tabId, FragmentTransaction ft) {        TabInfo newTab = null;        for (int i=0; i<mTabs.size(); i++) {            TabInfo tab = mTabs.get(i);            if (tab.tag.equals(tabId)) {                newTab = tab;            }        }        if (newTab == null) {            throw new IllegalStateException("No tab known for tag " + tabId);        }        if (mLastTab != newTab) {            if (ft == null) {                ft = mFragmentManager.beginTransaction();            }            if (mLastTab != null) {                if (mLastTab.fragment != null) {                    // 将detach替换为hide,隐藏Fragment                    // ft.detach(mLastTab.fragment);                    ft.hide(mLastTab.fragment);                }            }            if (newTab != null) {                if (newTab.fragment == null) {                    newTab.fragment = Fragment.instantiate(mContext,                            newTab.clss.getName(), newTab.args);                    ft.add(mContainerId, newTab.fragment, newTab.tag);                } else {                    // 将attach替换为show,显示Fragment                    // ft.attach(newTab.fragment);                    ft.show(newTab.fragment);                }            }            mLastTab = newTab;        }        return ft;    }

最后, 把布局中使用的android.support.v4.app.FragmentTabHost改为自己重写的FragmentTabHost。例如我的修改为com.lyl.weeklearning.widget.FragmentTabHost。还有在使用FragmentTabHost的java文件中,修改导入包,例如我的修改为:import com.lyl.weeklearning.widget.FragmentTabHost; 到此,文章结束了,有写的不对的地方,欢迎评论指正^_^

1 0