【Android】Material Design 之二 BottomNavigationView使用
上午記錄了TabLayout的使用,簡單實現了一個頂部可滑動的導航效果,突然想到Material Design的另一個控制元件BottomNavigationView,可以實現類似淘寶、微信、QQ、京東的底部導航欄的效果,下面就來介紹一下使用BottomNavigationView來實現底部導航欄的效果。
使用該控制元件同樣需要新增Material Design的依賴:(執行環境是在Android Studio 3.0)
implementation 'com.android.support:design:28.0.0-alpha1'
因為BottomNavigationView控制元件是通過app:menu屬性,使用Menu的形式為底部導航欄指定元素的,所以第一步就要新建一個選單xml檔案,在menu資料夾下新建bottom_navigation_view.xml,佈局內容如下:
<?xml version="1.0" encoding="utf-8"?> <menu xmlns:android="http://schemas.android.com/apk/res/android"> <item android:id="@+id/item_home" android:icon="@drawable/ic_home_grey" android:title="首頁"/> <item android:id="@+id/item_music" android:icon="@drawable/ic_music_grey" android:title="音樂"/> <item android:id="@+id/item_find" android:icon="@drawable/ic_find_grey" android:title="發現"/> </menu>
BottomNavigationView一般也是和ViewPager+Fragment搭配使用,所以第二步就寫下佈局檔案,
activity_bottom_navigation_view.xml佈局內容如下:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context=".BottomNavigationViewActivity"> <android.support.v4.view.ViewPager android:id="@+id/viewpager" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1"> </android.support.v4.view.ViewPager> <View android:id="@+id/view" android:layout_width="match_parent" android:layout_height="1dp" android:background="#6b6b6b"/> <android.support.design.widget.BottomNavigationView android:id="@+id/bottom_navigation_view" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="bottom" app:menu="@menu/bottom_navigation_view"> </android.support.design.widget.BottomNavigationView> </LinearLayout>
建立BottonNaviFragment繼承自Fragment,其佈局檔案fragment_bottom_navi.xml檔案如下:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:gravity="center"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="30sp" android:textColor="#000000" android:id="@+id/tv_content"/> </LinearLayout>
BottonNaviFragment.java檔案如下:
@SuppressLint("ValidFragment") public class BottonNaviFragment extends Fragment { private TextView textView; private String title; public BottonNaviFragment(String title) { this.title = title; } @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View view=inflater.inflate(R.layout.fragment_bottom_navi,container,false); textView=view.findViewById(R.id.tv_content); return view; } @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); textView.setText(title); } }
建立Fragment介面卡檔案FragmentAdapter.java檔案如下:
public class FragmentAdapter extends FragmentPagerAdapter { private List<Fragment> list; //存放ViewPager中要填充的Fragment public FragmentAdapter(FragmentManager fm,List<Fragment> list) { super(fm); this.list=list; } @Override public Fragment getItem(int i) { return list.get(i); } @Override public int getCount() { return list.size(); } }
BottomNavigationViewActivity.java檔案如下:
public class BottomNavigationViewActivity extends AppCompatActivity { private ViewPager viewPager; private BottomNavigationView bottomNavigationView; private MenuItem menuItem; //選單子項 private List<Fragment> list; private FragmentAdapter fragmentAdapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_bottom_navigation_view); initView(); initData(); } private void initView() { viewPager=findViewById(R.id.viewpager); bottomNavigationView=findViewById(R.id.bottom_navigation_view); //viewPager滑動監聽 viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { } @Override public void onPageSelected(int position) { if(menuItem!=null){ menuItem.setChecked(false); }else{ bottomNavigationView.getMenu().getItem(0).setChecked(false); } menuItem=bottomNavigationView.getMenu().getItem(position); menuItem.setChecked(true); } @Override public void onPageScrollStateChanged(int state) { } }); //bottmNavigationView選單選擇監聽 bottomNavigationView.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() { @Override public boolean onNavigationItemSelected(@NonNull MenuItem item) { switch(item.getItemId()){ case R.id.item_home: viewPager.setCurrentItem(0); break; case R.id.item_music: viewPager.setCurrentItem(1); break; case R.id.item_find: viewPager.setCurrentItem(2); break; } return false; } }); } private void initData() { list=new ArrayList<>(); list.add(new BottonNaviFragment("首頁")); list.add(new BottonNaviFragment("音樂")); list.add(new BottonNaviFragment("發現")); fragmentAdapter=new FragmentAdapter(getSupportFragmentManager(),null,list); viewPager.setAdapter(fragmentAdapter); } }
到此執行下專案,效果如圖:
預設元素選中時圖示、文字的顏色為@color/colorPrimary,如果我們想改變導航欄中圖示、文字在選中和未選中時的顏色,可以通過BottomNavigationView控制元件的兩個屬性去實現,分別是
app:itemTextColor="" app:itemIconTint=""
為了方便效果展示,在這裡我們設定圖示、文字在選中時顏色為紅色,未選中時為黑色,涉及到顏色選擇,需要在color資料夾下新建一個顏色選擇器bottomnavigation_select.xml檔案,內容如下:
<?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:color="#ff2200" android:state_checked="true"/> <item android:color="#000000"/> </selector>
然後在activity_bottom_navigation_view.xml中的BottomNavigationView控制元件下,增加屬性:
app:itemTextColor="@color/bottomnavigation_select" app:itemIconTint="@color/bottomnavigation_select"
執行一下,效果如圖:
以上是導航欄只有3個元素時效果,下面將元素增加到4個,執行效果如圖:
從圖中可以發現,當導航欄中元素增加到4個時,效果就不一樣了,只有當元素選中以及滑動到對應元素時,文字才會出現,未選中時,文字是隱藏的。 這是因為官方的BottomNavigationView預設有個放大的ShiftingMode效果,但是尚未支援程式碼層級的切換。在3個元素及以下時是預設關閉的,而到了4個及以上時就會開啟ShiftingMode效果,並且沒有任何屬性和方法去修改ShiftingMode,此時我們只能通過反射來修改:
新建一個BottomNavigationView的幫助者類BottomNavigationViewHelper.java,程式碼如下:
import android.support.design.internal.BottomNavigationItemView; import android.support.design.internal.BottomNavigationMenuView; import android.support.design.widget.BottomNavigationView; import android.util.Log; import java.lang.reflect.Field; /** * 新建一個BottomNavigationview幫助者類, * 通過反射來修改ShiftingMode */ public class BottomNavigationViewHelper { public static void disableShiftMode(BottomNavigationView view) { BottomNavigationMenuView menuView = (BottomNavigationMenuView) view.getChildAt(0); try { Field shiftingMode = menuView.getClass().getDeclaredField("mShiftingMode"); shiftingMode.setAccessible(true); shiftingMode.setBoolean(menuView, false); shiftingMode.setAccessible(false); for (int i = 0; i < menuView.getChildCount(); i++) { BottomNavigationItemView item = (BottomNavigationItemView) menuView.getChildAt(i); //noinspection RestrictedApi item.setShiftingMode(false); // set once again checked value, so view will be updated //noinspection RestrictedApi item.setChecked(item.getItemData().isChecked()); } } catch (NoSuchFieldException e) { Log.e("BNVHelper", "Unable to get shift mode field", e); } catch (IllegalAccessException e) { Log.e("BNVHelper", "Unable to change value of shift mode", e); } } }
然後在 BottomNavigationViewActivity.java中呼叫BottomNavigationViewHelper的靜態方法disableShiftMode()即可。
bottomNavigationView=findViewById(R.id.bottom_navigation_view); BottomNavigationViewHelper.disableShiftMode(bottomNavigationView);
當你的build.gradle中 依賴庫 'com.android.support:appcompat-v7:X.0.0-rc02' 中X小於28時,以上程式碼是沒有問題的,當X等於28時(我使用的是Android Studio 3.0,依賴包是implementation 'com.android.support:appcompat-v7:28.0.0-rc02'),item.setShiftingMode(false)就會報Cannot resolve method 'setShiftingMode(Boolean)'的錯誤,借鑑了
public class BottomNavigationViewHelper { @SuppressLint("RestrictedApi") public static void removeNavigationShiftMode(BottomNavigationView view) { BottomNavigationMenuView menuView = (BottomNavigationMenuView) view.getChildAt(0); menuView.setLabelVisibilityMode(LabelVisibilityMode.LABEL_VISIBILITY_LABELED); menuView.buildMenuView(); } }
同樣的,然後在 BottomNavigationViewActivity.java中呼叫BottomNavigationViewHelper的靜態方法removeNavigationShiftMode()
bottomNavigationView=findViewById(R.id.bottom_navigation_view); BottomNavigationViewHelper.removeNavigationShiftMode(bottomNavigationView);
一番修改之後,再執行下專案,看下效果:
此時,4個元素的效果和3個元素的效果就一樣啦。
最後關於BottomNavigationView控制元件作點補充:
1、如果要設定底部導航欄的背景顏色,可以通過BottomNavigationView的屬性app:itemBackground來設定,預設是當前主題的背景色,白色or黑色。
2、官方建議導航欄元素item個數為3-5個 ,最多5個,如果設定6個會直接報錯。設定2個則不會報錯,但是如果是2個的話不建議使用該控制元件。
3、如果想實現元素是不帶文字的圖示,可以不設定選單的title值,例如,不設定“發現”的元素文字,執行後效果如下方:
好啦,關於BottomNavigationView實現底部導航欄的使用就介紹到這裡啦。