在 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;
}
}