在 Android 应用中使用底部导航栏的方式相信大家都已经十分熟悉了,包括微信、手机QQ、大众点评等大家耳熟能详的 app 都都使用这种形式。底部导航栏能有效的突出重点、热点功能,减少应用的层级结构,令使用者一目了然,这里介绍一下使用 Fragment 来实现底部导航栏的方式。

首先定义主活动 MainActivity 作为容器,底部导航栏即在 MainActivity 活动的下方。 MainActivity中间为 FrameLayout,所有底部导航栏对应的子页面即依附在此 FrameLayout 内。当点击下方导航时,FrameLayout 中的内容就自动切换到子页面对应的 Fragment,实现了导航的过程。

首先看看 MainActivity 内相关的内容:

public class MainActivity extends Activity  {
    private FragmentManager fragmentManager;

    private HomeFragment homeFragment;
    private WorkFragment workFragment;
    private WarnFragment warnFragment;
    private SettingFragment settingFragment;

    private View homeTab;
    private View workTab;
    private View warnTab;
    private View settingTab;

    private ImageView homeImage;
    private ImageView workImage;
    private ImageView warnImage;
    private ImageView settingImage;

    private TextView homeText;
    private TextView workText;
    private TextView warnText;
    private TextView settingText;
     ...
}



以上是相关的元素的定义,接下来看一下相关的界面配置:


<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/main_layout"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

    <FrameLayout
        android:id="@+id/main_content"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"></FrameLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="60dp"
        android:background="@drawable/tab_bg">

        <RelativeLayout
            android:id="@+id/home_tab"
            android:layout_width="0dp"
            android:layout_weight="1"
            android:layout_height="match_parent">

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:orientation="vertical">
                <ImageView
                    android:id="@+id/home_image"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_gravity="center_horizontal"
                    android:background="@drawable/tab_home_unselected"/>

                <TextView
                    android:id="@+id/home_text"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_gravity="center_horizontal"
                    android:text="@string/main_home"
                    android:textColor="#82858b"/>
            </LinearLayout>

        </RelativeLayout>

        <RelativeLayout
            android:id="@+id/work_tab"
            android:layout_width="0dp"
            android:layout_weight="1"
            android:layout_height="match_parent">

            <LinearLayout
                android:id="@+id/work_layout"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:orientation="vertical">

                <ImageView
                    android:id="@+id/work_image"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_gravity="center_horizontal"
                    android:background="@drawable/tab_work_unselected"/>

                <TextView
                    android:id="@+id/work_text"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_gravity="center_horizontal"
                    android:text="@string/main_work"
                    android:textColor="#82858b"/>
            </LinearLayout>
        </RelativeLayout>

        <RelativeLayout
            android:id="@+id/warn_tab"
            android:layout_width="0dp"
            android:layout_weight="1"
            android:layout_height="match_parent">

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:orientation="vertical">
            <ImageView
                android:id="@+id/warn_image"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center_horizontal"
                android:background="@drawable/tab_warn_unselected"/>

            <TextView
                android:id="@+id/warn_text"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center_horizontal"
                android:text="@string/main_warn"
                android:textColor="#82858b"/>
            </LinearLayout>
        </RelativeLayout>

        <RelativeLayout
            android:id="@+id/setting_tab"
            android:layout_width="0dp"
            android:layout_weight="1"
            android:layout_height="match_parent">

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:orientation="vertical">
            <ImageView
                android:id="@+id/setting_image"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center_horizontal"
                android:background="@drawable/tab_setting_unselected"/>

            <TextView
                android:id="@+id/setting_text"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center_horizontal"
                android:text="@string/main_setting"
                android:textColor="#82858b"/>
            </LinearLayout>
        </RelativeLayout>

    </LinearLayout>
</LinearLayout>




最先定义的FrameLayout即上面提到的放置子页面的容器。后续的 LinearLayout 即主界面下方的导航栏。导航栏本身是一个 LinearLayout,里面的四个导航选项均为 RelativeLayout 子布局,由图片和相关文字组成。每个导航栏选项的图片与文字均有两套,分别对应被选中时的状态与未被选中时的状态。

public class MainActivity extends Activity  {
    ...
    protected void onCreate(Bundle savedInstanceState) {
        ...
        fragmentManager = getFragmentManager();

        homeTab = findViewById(R.id.home_tab);
        workTab = findViewById(R.id.work_tab);
        warnTab = findViewById(R.id.warn_tab);
        settingTab = findViewById(R.id.setting_tab);

        homeImage = (ImageView) findViewById(R.id.home_image);
        workImage = (ImageView) findViewById(R.id.work_image);
        warnImage = (ImageView) findViewById(R.id.warn_image);
        settingImage = (ImageView) findViewById(R.id.setting_image);

        homeText = (TextView) findViewById(R.id.home_text);
        workText = (TextView) findViewById(R.id.work_text);
        warnText = (TextView) findViewById(R.id.warn_text);
        settingText = (TextView) findViewById(R.id.setting_text);

        homeTab.setOnClickListener(this);
        workTab.setOnClickListener(this);
        warnTab.setOnClickListener(this);
        settingTab.setOnClickListener(this);

        setTabSelection(0);
}




接下来是在 onCreate 中初始化各元素。fragmentManager 是用来管理各个 Fragment 的类,可直接通过 Activity 类的 getFragmentManager() 函数获得。初始化各元素后后通过 setTabSelection(0) 函数指定初始化时选择第一个导航元素。

private void setTabSelection(int index) {
        // 每次选中之前先清除掉上次的选中状态
        clearSelection();
        // 开启一个Fragment事务
        FragmentTransaction transaction = fragmentManager.beginTransaction();
        // 先隐藏掉所有的Fragment,以防止有多个Fragment显示在界面上的情况
        hideFragments(transaction);
        switch (index) {
            case 0:
                homeImage.setImageResource(R.drawable.tab_home_selected);
                homeText.setTextColor(Color.WHITE);
                if (homeFragment == null) {
                    homeFragment = new HomeFragment();
                    transaction.add(R.id.main_content, homeFragment);
                } else {
                    transaction.show(homeFragment);
                }
                break;
            case 1:
                workImage.setImageResource(R.drawable.tab_work_selected);
                workText.setTextColor(Color.WHITE);
                if (workFragment == null) {
                    workFragment = new WorkFragment();
                    transaction.add(R.id.main_content, workFragment);
                } else {
                    transaction.show(workFragment);
                }
                break;
            case 2:
                warnImage.setImageResource(R.drawable.tab_warn_selected);
                warnText.setTextColor(Color.WHITE);
                if (warnFragment == null) {
                    warnFragment = new WarnFragment();
                    transaction.add(R.id.main_content, warnFragment);
                } else {
                    transaction.show(warnFragment);
                }
                break;
            case 3:
            default:
                settingImage.setImageResource(R.drawable.tab_setting_selected);
                settingText.setTextColor(Color.WHITE);
                if (settingFragment == null) {
                    settingFragment = new SettingFragment();
                    transaction.add(R.id.main_content, settingFragment);
                } else {
                    transaction.show(settingFragment);
                }
                break;
        }
        transaction.commit();
    }




这里来分析一下 setTabSelection() 函数。首先清除掉所有导航元素的被选中状态。即将各导航元素的图片置为未被选中状态的图片,文字颜色置为未被选中状态的颜色:

private void clearSelection() {
        homeImage.setImageResource(R.drawable.tab_home_unselected);
        homeText.setTextColor(Color.parseColor("#82858b"));
        workImage.setImageResource(R.drawable.tab_work_unselected);
        workText.setTextColor(Color.parseColor("#82858b"));
        warnImage.setImageResource(R.drawable.tab_warn_unselected);
        warnText.setTextColor(Color.parseColor("#82858b"));
        settingImage.setImageResource(R.drawable.tab_setting_unselected);
        settingText.setTextColor(Color.parseColor("#82858b"));
    }



然后开启 fragmentManager 的事务,并将所有的子页面 fragment 隐藏:


private void hideFragments(FragmentTransaction transaction) {
        if (homeFragment != null) {
            transaction.hide(homeFragment);
        }
        if (workFragment != null) {
            transaction.hide(workFragment);
        }
        if (warnFragment != null) {
            transaction.hide(warnFragment);
        }
        if (settingFragment != null) {
            transaction.hide(settingFragment);
        }
    }



最后,根据传入的序号参数,来决定显示哪个子页面 fragment。对于被选中的子页面,如果对应的 fragment 为null,则初始化之后通过 transaction.add() 函数将其加入到 frameLayout;如果已初始化,则通过 transaction.show() 函数显示即可。

这样,进入主界面之后默认就可以展示第一个子页面了,但是如何让应用在我们点击导航栏元素时进行响应,切换到对应的页面?只需要让 MainActivity 实现 View.OnClickListener 接口并实现 onClick(View) 方法就可以了:


public void onClick(View v) {
        switch (v.getId()) {
            case R.id.home_tab:
                // 当点击了消息tab时,选中第1个tab
                setTabSelection(0);
                break;
            case R.id.work_tab:
                // 当点击了联系人tab时,选中第2个tab
                setTabSelection(1);
                break;
            case R.id.warn_tab:
                // 当点击了动态tab时,选中第3个tab
                setTabSelection(2);
                break;
            case R.id.setting_tab:
                // 当点击了设置tab时,选中第4个tab
                setTabSelection(3);
                break;
            default:
                break;
        }
    }