Fragment介绍和运用

    |     2015年8月18日   |   Fragment碎片   |     0 条评论   |    2118

在 Android 上做动态、多窗口交互时,需要把 UI 和 Activity 操作封装成可切入切出的模块。Fragment 就像嵌套的 Activity:有自己的 layout,也管理自己的生命周期,可以在不同 Activity 里复用。

可以把 Fragment 想成 Activity 里一块模块化的部分:自己的生命周期、自己接收输入事件,Activity 运行中也能添加或移除(类似“子 Activity”)。还不熟的话可以先看 Android Fragment完全解析(创建、静态/动态添加、生命周期、相互通信)。学 FragmentTabHost 之前,也可对照站内 TabHost主界面实现。下面假设已掌握 RadioGroup / RadioButton 做底部 Tab。


一、FragmentTabHost 实现

布局 activity_raido_tab_fragment_layout.xml:底部 RadioGroup 当 Tab,内容区交给 FragmentTabHost。

<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.app.FragmentTabHost xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@android:id/tabhost"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent" >

        <TabWidget
            android:id="@android:id/tabs"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_alignParentBottom="true"
            android:background="@color/gray_bg"
            android:divider="@null"
            android:visibility="gone" >
        </TabWidget>

        <RadioGroup
            android:id="@+id/raidoGroupTab"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_alignParentBottom="true"
            android:background="@drawable/button_color_confirm_selector"
            android:orientation="horizontal" >

            <RadioButton
                android:id="@+id/raidoTab1"
                style="@style/main_tab_bottom"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:drawableTop="@drawable/btn_tab_main"
                android:text="首页" />

            <RadioButton
                android:id="@+id/raidoTab2"
                style="@style/main_tab_bottom"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:drawableTop="@drawable/btn_tab_meishi"
                android:text="美食" />

            <RadioButton
                android:id="@+id/raidoTab3"
                style="@style/main_tab_bottom"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:drawableTop="@drawable/btn_tab_yule"
                android:text="娱乐" />
        </RadioGroup>

        <FrameLayout
            android:id="@android:id/tabcontent"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_above="@id/raidoGroupTab" >
        </FrameLayout>
    </RelativeLayout>

</android.support.v4.app.FragmentTabHost>

样式 main_tab_bottom:

<style name="main_tab_bottom">
    <item name="android:textSize">@dimen/bottom_tab_font_size</item>
    <item name="android:gravity">center_horizontal|bottom</item>
    <item name="android:paddingTop">@dimen/bottom_tab_padding_up</item>
    <item name="android:background">@drawable/home_btn_bg</item>
    <item name="android:paddingBottom">2.0dip</item>
    <item name="android:layout_width">fill_parent</item>
    <item name="android:layout_height">wrap_content</item>
    <item name="android:button">@null</item>
    <item name="android:drawablePadding">@dimen/bottom_tab_padding_drawable</item>
    <item name="android:layout_weight">1.0</item>
</style>

Activity 里 setup FragmentTabHost,用 RadioGroup 切换 setCurrentTabByTag:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_raido_tab_fragment_layout);
    mRadioGroup = (RadioGroup) findViewById(R.id.raidoGroupTab);

    mTabHost = (FragmentTabHost) findViewById(android.R.id.tabhost);
    mTabHost.setup(this, getSupportFragmentManager(), android.R.id.tabcontent);

    mTabHost.addTab(mTabHost.newTabSpec("tab1").setIndicator("首页"), FragmentMain.class, null);
    mTabHost.addTab(mTabHost.newTabSpec("tab2").setIndicator("搜索"), FragmentSearch.class, null);
    mTabHost.addTab(mTabHost.newTabSpec("tab3").setIndicator("设置"), FragmentSetting.class, null);

    mRadioGroup.setOnCheckedChangeListener(new OnCheckedChangeListener() {
        @Override
        public void onCheckedChanged(RadioGroup group, int checkedId) {
            switch (checkedId) {
            case R.id.raidoTab1:
                mTabHost.setCurrentTabByTag("tab1");
                break;
            case R.id.raidoTab2:
                mTabHost.setCurrentTabByTag("tab2");
                break;
            case R.id.raidoTab3:
                mTabHost.setCurrentTabByTag("tab3");
                break;
            }
        }
    });
    ((RadioButton) mRadioGroup.getChildAt(0)).toggle(); // 默认选中第一项
}

二、replace 切换

布局 fragment_tabs_replace_act.xml:上面 FrameLayout 放内容,底部 RadioGroup 做菜单。

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@drawable/bottom_bg"
    android:orientation="vertical" >

    <!-- 上边主页面 -->
    <FrameLayout
        android:id="@+id/fragments_content"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1" />

    <!-- 底部菜单页面 -->
    <RadioGroup
        android:id="@+id/bottomRg"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom"
        android:background="@drawable/maintab_toolbar_bg"
        android:orientation="horizontal" >

        <RadioButton
            android:id="@+id/rbOne"
            style="@style/main_tab_bottom"
            android:layout_marginTop="2.0dip"
            android:drawableTop="@drawable/icon_pinglun"
            android:text="首页" />

        <RadioButton
            android:id="@+id/rbTwo"
            style="@style/main_tab_bottom"
            android:layout_marginTop="2.0dip"
            android:drawableTop="@drawable/icon_xianshi"
            android:text="搜索" />

        <RadioButton
            android:id="@+id/rbThree"
            style="@style/main_tab_bottom"
            android:layout_marginTop="2.0dip"
            android:drawableTop="@drawable/icon_nongjiale"
            android:text="设置" />
    </RadioGroup>

</LinearLayout>

每次 checked 变化都 replace:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    requestWindowFeature(Window.FEATURE_NO_TITLE);
    setContentView(R.layout.fragment_tabs_replace_act);
    mRadioGroup = (RadioGroup) findViewById(R.id.bottomRg);

    mainFragment = (FragmentMain) FragmentMain.newInstance();
    searchFragment = (FragmentSearch) FragmentSearch.newInstance();
    settingFragment = (FragmentSetting) FragmentSetting.newInstance();

    mRadioGroup.setOnCheckedChangeListener(new OnCheckedChangeListener() {
        @Override
        public void onCheckedChanged(RadioGroup group, int checkedId) {
            FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
            switch (checkedId) {
            case R.id.rbOne:
                if (mainFragment == null) {
                    mainFragment = (FragmentMain) FragmentMain.newInstance();
                }
                fragmentTransaction.replace(R.id.fragments_content, mainFragment);
                break;
            case R.id.rbTwo:
                if (searchFragment == null) {
                    searchFragment = (FragmentSearch) FragmentSearch.newInstance();
                }
                fragmentTransaction.replace(R.id.fragments_content, searchFragment);
                break;
            case R.id.rbThree:
                if (settingFragment == null) {
                    settingFragment = (FragmentSetting) FragmentSetting.newInstance();
                }
                fragmentTransaction.replace(R.id.fragments_content, settingFragment);
                break;
            }
            fragmentTransaction.commit();
        }
    });
    ((RadioButton) mRadioGroup.getChildAt(0)).toggle(); // 默认选中第一项
}

replace 本质是先 remove 再 add,生命周期会从 onAttach 走到 onDetach,视图状态保不住。FragmentSearch 里 EditText 输入的内容一切换就没了。

life

fragment_tab1


三、show / hide 保存状态

布局与上一节相同。先 hide 全部,再 show 当前;尚未创建则 add:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    requestWindowFeature(Window.FEATURE_NO_TITLE);
    setContentView(R.layout.fragment_tabs_replace_act);
    mRadioGroup = (RadioGroup) findViewById(R.id.bottomRg);

    mRadioGroup.setOnCheckedChangeListener(new OnCheckedChangeListener() {
        @Override
        public void onCheckedChanged(RadioGroup group, int checkedId) {
            FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
            hideFragments(fragmentTransaction);
            switch (checkedId) {
            case R.id.rbOne:
                if (mainFragment == null) {
                    mainFragment = new FragmentMain();
                    fragmentTransaction.add(R.id.fragments_content, mainFragment);
                } else {
                    fragmentTransaction.show(mainFragment);
                }
                break;
            case R.id.rbTwo:
                if (searchFragment == null) {
                    searchFragment = new FragmentSearch();
                    fragmentTransaction.add(R.id.fragments_content, searchFragment);
                } else {
                    fragmentTransaction.show(searchFragment);
                }
                break;
            case R.id.rbThree:
                if (settingFragment == null) {
                    settingFragment = new FragmentSetting();
                    fragmentTransaction.add(R.id.fragments_content, settingFragment);
                } else {
                    fragmentTransaction.show(settingFragment);
                }
                break;
            }
            fragmentTransaction.commit();
        }
    });
    ((RadioButton) mRadioGroup.getChildAt(0)).toggle(); // 默认选中第一项
}

/**
 * 将所有的 Fragment 都置为隐藏状态。
 */
private void hideFragments(FragmentTransaction transaction) {
    if (mainFragment != null) {
        transaction.hide(mainFragment);
    }
    if (searchFragment != null) {
        transaction.hide(searchFragment);
    }
    if (settingFragment != null) {
        transaction.hide(settingFragment);
    }
}

fragment_tab2


四、三种切 Tab 对照

做法 切换方式 生命周期 视图状态
FragmentTabHost setCurrentTabByTag 由 TabHost 托管 一般可保留
replace remove + add onAttach → onDetach 不能保存,EditText 会丢
show / hide add 一次后 hide/show 不会走完销毁流程 可以保存输入

一句话总结:底部 Tab 用 Fragment 模块化即可;要保住输入状态,优先 show/hide,不要每次 replace。

转载请注明来源:Fragment介绍和运用
本文链接地址:https://ai.zhousir.top/?p=976

上一篇:

下一篇:

回复 取消