Material Design系列之BottomNavigationView詳解

Material Design系列之BottomNavigationView詳解

Material Design官方文檔Bottom navigation的介紹

BottomNavigationView官方API文檔

簡介

BottomNavigationView實(shí)現(xiàn)的效果就是常見的app底部導(dǎo)航欄的效果。

Bottom navigation bars make it easy for users to explore and switch between top-level views in a single tap. It should be used when application has three to five top-level destinations.

適用于3到5個tab的情況下。

使用

compile 'com.android.support:design:26.0.0-alpha1'

<android.support.design.widget.BottomNavigationView
        android:id="@+id/bottomnavigationview"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:itemIconTint="@drawable/selector_tab_color"
        app:itemTextColor="@drawable/selector_tab_color"
        app:menu="@menu/bottom_navigation_tab">

    </android.support.design.widget.BottomNavigationView>

屬性:

itemIconTint:icon圖片的顏色

itemTextColor:文本的顏色

menu:tab的布局

selector_tab_color:

<selector xmlns:android="http://schemas.android.com/apk/res/android">

    <item android:state_checked="true" android:color="@color/colorAccent"/>
    <item android:state_checked="false" android:color="@color/colorPrimary"/>

</selector>

一般來說圖片的顏色是和文字的顏色是一致的。

菜單的布局:

<menu xmlns:android="http://schemas.android.com/apk/res/android">

    <item
        android:id="@+id/tab_one"
        android:icon="@drawable/icon_one_selected"
        android:title="首頁"/>

    <item
        android:id="@+id/tab_two"
        android:icon="@drawable/icon_two_selected"
        android:title="消息"/>

    <item
        android:id="@+id/tab_three"
        android:icon="@drawable/icon_three_selected"
        android:title="訂單"/>

    <item
        android:id="@+id/tab_four"
        android:icon="@drawable/icon_four_selected"
        android:title="我的"/>
</menu>

這種情況只適用于圖片是純色且選中和未選中時的圖片是一樣的。如果是不同的圖片需要新建一個selector文件,
設(shè)置選中時的圖片和未選中時的圖片,并且不設(shè)置itemIconTint屬性。

完整的activity代碼:

public class BottomNavigationViewActivity extends AppCompatActivity implements BottomNavigationView.OnNavigationItemSelectedListener, ViewPager.OnPageChangeListener {
    ViewPager viewPager;
    BottomNavigationView bottomNavigationView;
    private MenuItem menuItem;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_bottom_navigation_view);

        viewPager = (ViewPager) findViewById(R.id.viewpager);
        bottomNavigationView = (BottomNavigationView) findViewById(R.id.bottomnavigationview);
        disableShiftMode(bottomNavigationView);
        bottomNavigationView.setOnNavigationItemSelectedListener(this);
        viewPager.addOnPageChangeListener(this);
        bottomNavigationView.setSelectedItemId(R.id.tab_two);
        viewPager.setAdapter(new ViewPagerAdapter(getSupportFragmentManager()));
    }

    @Override
    public boolean onNavigationItemSelected(@NonNull MenuItem item) {
        int itemId = item.getItemId();
        switch (itemId){
            case R.id.tab_one:
                viewPager.setCurrentItem(0);
                break;
            case R.id.tab_two:
                viewPager.setCurrentItem(1);
                break;
            case R.id.tab_three:
                viewPager.setCurrentItem(2);
                break;
            case R.id.tab_four:
                viewPager.setCurrentItem(3);
                break;
        }
        return false;
    }

    @Override
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

    }

    @Override
    public void onPageSelected(int position) {
        menuItem = bottomNavigationView.getMenu().getItem(position);
        menuItem.setChecked(true);
    }

    @Override
    public void onPageScrollStateChanged(int state) {

    }


    public void disableShiftMode(BottomNavigationView navigationView) {

        BottomNavigationMenuView menuView = (BottomNavigationMenuView) navigationView.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 itemView = (BottomNavigationItemView) menuView.getChildAt(i);
                itemView.setShiftingMode(false);
                itemView.setChecked(itemView.getItemData().isChecked());
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    class ViewPagerAdapter extends FragmentPagerAdapter {
        private Fragment[] mFragments = new Fragment[]{new OneFragment(), new TwoFragment(), new ThreeFragment(),new FourFragment()};

        public ViewPagerAdapter(FragmentManager fm) {
            super(fm);
        }

        @Override
        public Fragment getItem(int position) {
            return mFragments[position];
        }

        @Override
        public int getCount() {
            return 4;
        }
    }
}
注意

1、BottomNavigationView只適用于3到5個的導(dǎo)航欄;

2、當(dāng)tab個數(shù)大余3個時,BottomNavigationView不會均分寬度,一般來說我們都是需要均分寬度。


圖片1

解決方案:disableShiftMode(bottomNavigationView);

public void disableShiftMode(BottomNavigationView navigationView) {

        BottomNavigationMenuView menuView = (BottomNavigationMenuView) navigationView.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 itemView = (BottomNavigationItemView) menuView.getChildAt(i);
                itemView.setShiftingMode(false);
                itemView.setChecked(itemView.getItemData().isChecked());
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

來看下關(guān)于mShiftingMode這個變量的源碼,在BottomNavigationMenuView中:

mShiftingMode = mMenu.size() > 3;//當(dāng)大于3時為true
        for (int i = 0; i < mMenu.size(); i++) {
            mPresenter.setUpdateSuspended(true);
            mMenu.getItem(i).setCheckable(true);
            mPresenter.setUpdateSuspended(false);
            BottomNavigationItemView child = getNewItem();
            mButtons[i] = child;
            child.setIconTintList(mItemIconTint);
            child.setTextColor(mItemTextColor);
            child.setItemBackground(mItemBackgroundRes);
            child.setShiftingMode(mShiftingMode);
            child.initialize((MenuItemImpl) mMenu.getItem(i), 0);
            child.setItemPosition(i);
            child.setOnClickListener(mOnClickListener);
            addView(child);
        }

在執(zhí)行onMeasure方法時:

if (mShiftingMode) {
            final int inactiveCount = count - 1;
            final int activeMaxAvailable = width - inactiveCount * mInactiveItemMinWidth;
            final int activeWidth = Math.min(activeMaxAvailable, mActiveItemMaxWidth);
            final int inactiveMaxAvailable = (width - activeWidth) / inactiveCount;
            final int inactiveWidth = Math.min(inactiveMaxAvailable, mInactiveItemMaxWidth);
            int extra = width - activeWidth - inactiveWidth * inactiveCount;
            for (int i = 0; i < count; i++) {
                mTempChildWidths[i] = (i == mSelectedItemPosition) ? activeWidth : inactiveWidth;
                if (extra > 0) {
                    mTempChildWidths[i]++;
                    extra--;
                }
            }
        } else {
            final int maxAvailable = width / (count == 0 ? 1 : count);
            final int childWidth = Math.min(maxAvailable, mActiveItemMaxWidth);
            int extra = width - childWidth * count;
            for (int i = 0; i < count; i++) {
                mTempChildWidths[i] = childWidth;
                if (extra > 0) {
                    mTempChildWidths[i]++;
                    extra--;
                }
            }
        }

下圖是mShiftingMode為true的情況下debug拿到的數(shù)據(jù),再結(jié)合效果圖,即可分析出:


圖片2

inactiveCount為閑置的個數(shù),即沒有被選中的menuItem的個數(shù),選中的寬度activeWidth和未選中的寬度inactiveWidth不一致。
當(dāng)mShiftingMode為false執(zhí)行的代碼很容易看出寬度是均分計(jì)算的。

其他

源碼里面的各個屬性的設(shè)置:

<dimen name="design_bottom_navigation_active_item_max_width">168dp</dimen>//選中時的最大寬度
    <dimen name="design_bottom_navigation_active_text_size">14sp</dimen>//選中時的字體大小
    <dimen name="design_bottom_navigation_elevation">8dp</dimen>//陰影的大小
    <dimen name="design_bottom_navigation_height">56dp</dimen>//高度
    <dimen name="design_bottom_navigation_item_max_width">96dp</dimen>//未選中的最大寬度
    <dimen name="design_bottom_navigation_item_min_width">56dp</dimen>//未選中的最小的寬度
    <dimen name="design_bottom_navigation_margin">8dp</dimen>//icon與文本之間的間距
    <dimen name="design_bottom_navigation_shadow_height">1dp</dimen>//陰影高度
    <dimen name="design_bottom_navigation_text_size">12sp</dimen>//未選中時的字體大小

如果要修改這些屬性值,在自己項(xiàng)目的dimens定義相同的名字,重新賦值

Github示例代碼

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,064評論 25 709
  • 1、通過CocoaPods安裝項(xiàng)目名稱項(xiàng)目信息 AFNetworking網(wǎng)絡(luò)請求組件 FMDB本地?cái)?shù)據(jù)庫組件 SD...
    陽明AI閱讀 16,208評論 3 119
  • 這第一條文章是來治療我的懶癌的,標(biāo)題是我用0.01秒想的。從來都不喜歡用文字表達(dá)自己,近來越來越覺得寫作是一個理清...
    耳東佳閱讀 243評論 0 0
  • 作者/米唐曉 愛上一個陰天,偶遇一個晴天,我始終可以在撕扯過的痛再抬頭看著天空。我始終可以在別人看不到的地方矯情寫...
    米唐曉閱讀 887評論 3 2
  • “ 如果你想起我,你會想到什么”
    酥孞閱讀 217評論 0 0

友情鏈接更多精彩內(nèi)容