問題
如何實現(xiàn)常見應用中的底部導航欄的功能?

回答
可以借助 BottomNavigationView 來實現(xiàn)這一效果。
效果

簡介
BottomNavigationView 是應用程序的標準底部導航欄,是 Material Design 3 導航規(guī)范、Material Design 2 底部導航規(guī)范 的實現(xiàn)。
應用于頂級頁面的導航,適用于 3 - 5 個菜單項的場景。
本文的說明不涉及角標 ( 圖標右上角顯示的數(shù)字 )。如有需要,BottomNavigation 可能會有所幫助。
步驟
- 添加
BottomNavigationView所在庫的依賴。
dependencies {
...
implementation 'com.google.android.material:material:1.4.0'
...
}
如果 build 失敗,可能和項目與 material 庫的版本不匹配有關。
1.5.0 版本對應 Material Design 3, 要求 compileSdkVersion 和 targetSdkVersion 不低于 31 。其他版本的項目要求可以查閱 material-components-android/releases。
- 在 Activity 的 layout 文件中使用
BottomNavigationView控件。
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/main_navigation_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@android:color/white"
app:layout_constraintBottom_toBottomOf="parent"
app:menu="@menu/navigation_view_menu_items" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
- 第二步中,
BottomNavigationView的app:menu屬性需要引用一個菜單資源,該菜單資源用于定義導航欄中菜單項的數(shù)量和樣式。創(chuàng)建res/menu目錄,在目錄下創(chuàng)建菜單文件。
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/navigation_menu_item_home"
android:icon="@drawable/navigation_home"
android:title="主頁" />
<item
android:id="@+id/navigation_menu_item_contact"
android:icon="@drawable/navigation_contact"
android:title="聯(lián)系人" />
<item
android:id="@+id/navigation_menu_item_mine"
android:icon="@drawable/navigation_mine"
android:title="我的" />
</menu>
上面的菜單文件中,定義了 3 個菜單項,每個菜單項包含 id, icon(圖標) 和title (標題) 三個屬性。
- 第三步中的
icon屬性需要引用 drawable 資源,以“主頁”為例進行說明。創(chuàng)建 drawable 文件, 為選中和非選中狀態(tài)添加對應的圖標。
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/navigation_home_selected" android:state_selected="true" />
<item android:drawable="@drawable/navigation_home_default" android:state_selected="false" />
</selector>
item 的 drawable屬性引用 png 之類的圖片即可。


至此,底部導航欄的樣式就實現(xiàn)了。
創(chuàng)建“主頁”、“聯(lián)系人”和“我的” Fragment 和 layout 文件。當某個菜單項選中時,切換到對應的 Fragment。
在 Activity 的 layout 文件中使用
ViewPager2控件。作為 3 個 Fragment 的容器,并控制它們的切換。
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/main_fragment_container"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="@id/main_navigation_bar"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_weight="1" />
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/main_navigation_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@android:color/white"
app:layout_constraintBottom_toBottomOf="parent"
app:menu="@menu/navigation_view_menu_items" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
創(chuàng)建 ViewPager2 的 Fragment 適配器。
public class FragmentAdapter extends FragmentStateAdapter {
private final ArrayList<Fragment> fragments;
public FragmentAdapter(@NonNull FragmentActivity fragmentActivity) {
super(fragmentActivity);
fragments = new ArrayList<>();
fragments.add(new HomeFragment());
fragments.add(new ContactFragment());
fragments.add(new MineFragment());
}
@NonNull
@Override
public Fragment createFragment(int position) {
return fragments.get(position);
}
@Override
public int getItemCount() {
return fragments.size();
}
}
在 Activity 中為 ViewPager2 設置適配器。
public class BottomNavigationBarActivity extends AppCompatActivity {
private NavigationActivityBottomNavigationBarBinding binding;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
int layoutId = R.layout.navigation_activity_bottom_navigation_bar;
binding = DataBindingUtil.setContentView(this, layoutId);
// 為 ViewPager2 設置適配器
binding.mainFragmentContainer.setAdapter(new FragmentAdapter(this));
}
}
- 在 Activity 中為
BottomNavigationView控件添加菜單項選中事件監(jiān)聽器,當某一項選中后,ViewPager2切換為對應的 Fragment。
private void setNavigationItemSelectedListener() {
binding.mainNavigationBar.setOnItemSelectedListener(new NavigationBarView.OnItemSelectedListener() {
@Override
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
String itemTitle = (String) item.getTitle();
switch (itemTitle) {
case "主頁": {
binding.mainFragmentContainer.setCurrentItem(0);
}
break;
case "聯(lián)系人": {
binding.mainFragmentContainer.setCurrentItem(1);
}
break;
case "我的": {
binding.mainFragmentContainer.setCurrentItem(2);
}
break;
}
return true;
}
});
}
禁用 ViewPager2 控件的滑動切換頁面功能,僅支持點擊底部導航欄的菜單項切換頁面。( 監(jiān)聽 ViewPager2 的頁面切換,設置 BottomNavigationView 的選中項亦可。類似于微信的效果,支持點擊和滑動兩種切換方式 )
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
int layoutId = R.layout.navigation_activity_bottom_navigation_bar;
binding = DataBindingUtil.setContentView(this, layoutId);
binding.mainFragmentContainer.setAdapter(new FragmentAdapter(this));
// 禁用滑動切換頁面功能
binding.mainFragmentContainer.setUserInputEnabled(false);
setNavigationItemSelectedListener();
}