Material Design系列之BottomNavigationView詳解

Material Design系列之BottomNavigationView詳解

Material Design官方文檔Bottom navigation的介紹

BottomNavigationView官方API文檔

簡介

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

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文件答姥,
設置選中時的圖片和未選中時的圖片什燕,并且不設置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個的導航欄柱蟀;

2、當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();
        }
    }

來看下關于mShiftingMode這個變量的源碼昼牛,在BottomNavigationMenuView中:

mShiftingMode = mMenu.size() > 3;//當大于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不一致早抠。
當mShiftingMode為false執(zhí)行的代碼很容易看出寬度是均分計算的霎烙。

其他

源碼里面的各個屬性的設置:

<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>//未選中時的字體大小

如果要修改這些屬性值,在自己項目的dimens定義相同的名字蕊连,重新賦值

Github示例代碼

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末悬垃,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子甘苍,更是在濱河造成了極大的恐慌尝蠕,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,509評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件载庭,死亡現(xiàn)場離奇詭異看彼,居然都是意外死亡,警方通過查閱死者的電腦和手機囚聚,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,806評論 3 394
  • 文/潘曉璐 我一進店門靖榕,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人顽铸,你說我怎么就攤上這事茁计。” “怎么了谓松?”我有些...
    開封第一講書人閱讀 163,875評論 0 354
  • 文/不壞的土叔 我叫張陵星压,是天一觀的道長践剂。 經(jīng)常有香客問我,道長娜膘,這世上最難降的妖魔是什么逊脯? 我笑而不...
    開封第一講書人閱讀 58,441評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮劲绪,結(jié)果婚禮上男窟,老公的妹妹穿的比我還像新娘。我一直安慰自己贾富,他們只是感情好歉眷,可當我...
    茶點故事閱讀 67,488評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著颤枪,像睡著了一般汗捡。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上畏纲,一...
    開封第一講書人閱讀 51,365評論 1 302
  • 那天扇住,我揣著相機與錄音,去河邊找鬼盗胀。 笑死艘蹋,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的票灰。 我是一名探鬼主播女阀,決...
    沈念sama閱讀 40,190評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼屑迂!你這毒婦竟也來了浸策?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,062評論 0 276
  • 序言:老撾萬榮一對情侶失蹤惹盼,失蹤者是張志新(化名)和其女友劉穎庸汗,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體手报,經(jīng)...
    沈念sama閱讀 45,500評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡蚯舱,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,706評論 3 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了掩蛤。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片枉昏。...
    茶點故事閱讀 39,834評論 1 347
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖盏档,靈堂內(nèi)的尸體忽然破棺而出凶掰,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 35,559評論 5 345
  • 正文 年R本政府宣布懦窘,位于F島的核電站前翎,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏畅涂。R本人自食惡果不足惜港华,卻給世界環(huán)境...
    茶點故事閱讀 41,167評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望午衰。 院中可真熱鬧立宜,春花似錦、人聲如沸臊岸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,779評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽帅戒。三九已至灯帮,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間逻住,已是汗流浹背钟哥。 一陣腳步聲響...
    開封第一講書人閱讀 32,912評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留瞎访,地道東北人腻贰。 一個月前我還...
    沈念sama閱讀 47,958評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像扒秸,于是被迫代替她去往敵國和親播演。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,779評論 2 354

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,110評論 25 707
  • 1鸦采、通過CocoaPods安裝項目名稱項目信息 AFNetworking網(wǎng)絡請求組件 FMDB本地數(shù)據(jù)庫組件 SD...
    陽明先生_X自主閱讀 15,980評論 3 119
  • 這第一條文章是來治療我的懶癌的宾巍,標題是我用0.01秒想的咕幻。從來都不喜歡用文字表達自己渔伯,近來越來越覺得寫作是一個理清...
    耳東佳閱讀 192評論 0 0
  • 作者/米唐曉 愛上一個陰天蓝厌,偶遇一個晴天玄叠,我始終可以在撕扯過的痛再抬頭看著天空。我始終可以在別人看不到的地方矯情寫...
    米唐曉閱讀 807評論 3 2
  • “ 如果你想起我拓提,你會想到什么”
    酥孞閱讀 200評論 0 0