Android導(dǎo)航菜單選項(xiàng)卡總結(jié)

前言

如果你之前沒(méi)接觸過(guò)的話搬泥,建議先下載demo,對(duì)比本文進(jìn)行學(xué)習(xí)小腊;
如果你向我一樣,總是忘記久窟,那就動(dòng)動(dòng)小手收藏一下吧~~~
demo: https://github.com/liuleshuai/NavigationMenuBar

愛你

干貨

現(xiàn)在的App底部一般都有一個(gè)可切換的菜單秩冈,通常使用有以下5種方式實(shí)現(xiàn):

  • TabHost(過(guò)時(shí))
  • Radiobutton(自定義)
  • FragmentTabHost
  • TabLayout(5.0新增)
  • BottomNavigationView(5.0新增)

1.TabHost

它添加的是Activity而不像上面那些全部使用Fragment來(lái)顯示內(nèi)容。

過(guò)時(shí)斥扛,不推薦使用漩仙,略...

2.Radiobutton

原理就是通過(guò)設(shè)置button的樣式來(lái)模擬菜單操作,可以參考這篇文章:
https://blog.csdn.net/duolaimila/article/details/51315623

不推薦犹赖,略...

3.FragmentTabHost

依賴

布局

  • 底部菜單欄的布局一般如下所示:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"              
              xmlns:tools="http://schemas.android.com/tools"              
              android:layout_width="match_parent"              
              android:layout_height="match_parent"              
              android:orientation="vertical"              
              tools:context=".MainActivity">    

   <FrameLayout        
              android:id="@+id/realcontent"        
              android:layout_width="match_parent"        
              android:layout_height="0dp"        
              android:layout_weight="1">    
    </FrameLayout>    

    <!--<android.support.v4.view.ViewPager
        android:id="@+id/pager"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1" />-->

    <android.support.v4.app.FragmentTabHost        
              android:id="@android:id/tabhost"          
              android:layout_width="match_parent"        
              android:layout_height="wrap_content"        
              android:background="#ffffff" >      
 
         <FrameLayout         
               android:id="@android:id/tabcontent"            
               android:layout_width="0dp"            
               android:layout_height="0dp"            
               android:layout_weight="0" >
         </FrameLayout>    
     </android.support.v4.app.FragmentTabHost></LinearLayout>

注意:

  1. FragmentTabHost的id是需要使用安卓自帶的id队他,必須設(shè)置為@android:id/tabhost
  2. FragmentTabHost中的FrameLayout也需要使用安卓自帶的id峻村,@android:id/tabcontent麸折。

另外,需要添加一個(gè)菜單Item的布局粘昨。

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:orientation="vertical">

    <TextView
        android:id="@+id/tab_textview"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text=""
        android:gravity="center"
        android:textColor="#ffffff"
        android:textSize="12sp" />
</LinearLayout>

代碼

  • 綁定
   mTabHost = (FragmentTabHost) findViewById(android.R.id.tabhost);
   //綁定Fragment
   mTabHost.setup(this, getSupportFragmentManager(), R.id.realcontent);

   //綁定viewpager
//   mTabHost.setup(this, getSupportFragmentManager(), R.id.pager);
  • 設(shè)置菜單選項(xiàng)卡
for (int i = 0; i < count; i++) {
   // 給每個(gè)Tab按鈕設(shè)置標(biāo)簽垢啼、圖標(biāo)和文字
   TabHost.TabSpec tabSpec = mTabHost.newTabSpec(textViewArray[i])
                    .setIndicator(getItemView(i));
   // 將Tab按鈕添加進(jìn)Tab選項(xiàng)卡中,并綁定Fragment
   mTabHost.addTab(tabSpec, fragmentArray[i], null);
   //設(shè)置Tab被選中的時(shí)候顏色改變
   mTabHost.getTabWidget().getChildAt(i)
          .setBackgroundResource(R.drawable.selector_tab_background);
 }

注意:
上述代碼中的.newTabSpec(textViewArray[i])是為菜單設(shè)置String類型的Tag標(biāo)識(shí)张肾,不一定要設(shè)置為菜單標(biāo)題芭析,但是一定不能重復(fù),推薦設(shè)置成菜單的名稱吞瞪,肯定沒(méi)人會(huì)設(shè)置兩個(gè)一樣的菜單吧 :)

    private View getItemView(int i) {
        View view = LayoutInflater.from(this).inflate(R.layout.fragment_tab_host_item, null, false);
        TextView tv = view.findViewById(R.id.tab_textview);
        tv.setText(textViewArray[i]);
        Drawable drawable = ContextCompat.getDrawable(this,R.mipmap.ic_launcher);
        drawable.setBounds(0, 0, drawable.getMinimumWidth(), drawable.getMinimumHeight());
        tv.setCompoundDrawables(null, drawable, null, null);
        return view;
    }
  • 其它設(shè)置
 //去掉分割線 null 或 LinearLayout.SHOW_DIVIDER_NONE
 mTabHost.getTabWidget().setDividerDrawable(null);

 //設(shè)置默認(rèn)第一個(gè)
 mTabhost.setCurrentTab(0);

如果綁定的是ViewPager馁启,還需要對(duì)頁(yè)面切換進(jìn)行處理

  /*為了當(dāng)點(diǎn)擊下面菜單時(shí),上面的ViewPager能滑動(dòng)到對(duì)應(yīng)的Fragment*/
  mTabHost.setOnTabChangedListener(new TabHost.OnTabChangeListener() {
            @Override
            public void onTabChanged(String tabId) {
                int position = mTabHost.getCurrentTab();
                vp.setCurrentItem(position);
            }
  });

  //設(shè)置頁(yè)面切換時(shí)的監(jiān)聽器
  vp.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
        @Override
        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
        }

        @Override
        public void onPageSelected(int position) {
            mTabHost.setCurrentTab(arg0);
        }

        @Override
        public void onPageScrollStateChanged(int state) {      
        }
    });

不用擔(dān)心上述二者的監(jiān)聽造成循環(huán)調(diào)用,VP和FragmentTabHost內(nèi)部已經(jīng)為我們做好了處理芍秆,即如果目標(biāo)位置和當(dāng)前位置相同惯疙,不會(huì)觸發(fā)OnPageChangeListenerOnTabChangeListener回調(diào)

源碼如下:


VP部分源碼
TabHost部分源碼

4.TabLayout

依賴

compile 'com.android.support:support-v4:xx.x.x'
compile 'com.android.support:design:xx.x.x'

布局

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <android.support.design.widget.TabLayout
        android:id="@+id/tabLayout"
        android:layout_width="match_parent"
        android:layout_height="100p"
        //導(dǎo)航欄背景顏色
        android:background="#ffff00"
        //指示器顏色
        app:tabIndicatorColor="#66ff33"
        //指示器高度
        app:tabIndicatorHeight="20p"
        //普通狀態(tài)下文字的顏色
        app:tabTextColor="@color/colorPrimary"
        //選中時(shí)文字的顏色
        app:tabSelectedTextColor="#CC33FF"
        //是否可滑動(dòng):fixed:固定妖啥;scrollable:可滑動(dòng)
        app:tabMode="fixed"
        //居中模式:fill:填充; center:居中
        app:tabGravity="fill"
        //設(shè)置選項(xiàng)卡的背景
        app:tabBackground="@drawable/selected"
        //設(shè)置字體大忻沟摺:此處要寫一個(gè)style) 
        app:tabTextAppearance="@style/MyTabLayoutTextAppearance"/>

    <android.support.v4.view.ViewPager
        android:id="@+id/viewPager"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"/>
</LinearLayout>

注意:
上述TabLayout布局中,android:layout_height="100p"這里需要注意荆虱,不能設(shè)置成wrap_content蒿偎,必須設(shè)置具體數(shù)值,否則會(huì)出現(xiàn)無(wú)法顯示菜單標(biāo)題的問(wèn)題怀读。切記K呶弧!愿吹!切記2淮印!犁跪!切記4幌ⅰ4踉!

style.xml

<style name="MyTabLayoutTextAppearance" parent="TextAppearance.AppCompat.Widget.ActionBar.Title">
    <item name="android:textSize">6sp</item>    
    <item name="android:textColor">@color/gary</item>
</style>
tabGravity&tabMode

代碼

//綁定VP
tabHost.setupWithViewPager(pager);
  • 通過(guò)指定Tab的方式設(shè)置菜單
//指定Tab的位置
Tab one = mTabLayout.getTabAt(0);
Tab two = mTabLayout.getTabAt(1);
Tab three = mTabLayout.getTabAt(2);
Tab four = mTabLayout.getTabAt(3);
//設(shè)置Tab的圖標(biāo)寝优,假如不需要?jiǎng)t把下面的代碼刪去
one.setIcon(R.mipmap.ic_launcher);
two.setIcon(R.mipmap.ic_launcher);
three.setIcon(R.mipmap.ic_launcher);
four.setIcon(R.mipmap.ic_launcher);

綁定好VP和TabLayout后条舔,菜單的tv內(nèi)容默認(rèn)是從適配器中getPageTitle獲得。

public class MyFragmentPagerAdapter extends FragmentPagerAdapter {

    ... ...

    //PageTitle就是Tab的Text
    @Override
    public CharSequence getPageTitle(int position) {
        return mTitles[position];
    }
}
  • 你還可以通過(guò)setCustomView的方式對(duì)菜單進(jìn)行設(shè)置:
  //自定義菜單
  for (int i = 0; i < adapter.getCount(); i++) {
       TabLayout.Tab tab = tabHost.getTabAt(i);
       if (tab != null) {
           tab.setCustomView(adapter.getTabView(i));
       }
   }

   //在適配器中乏矾,返回自定義View
   public View getTabView(int position) {
        View view = LayoutInflater.from(context).inflate(R.layout.tab_layout, null);
        ImageView iv = view.findViewById(R.id.iv);
        TextView tv = view.findViewById(R.id.tv);
        iv.setImageResource(images.get(position));
        tv.setText(title.get(position));
        return view;
    }

通過(guò)這種方式設(shè)置的好處是孟抗,不用關(guān)心適配器中getPageTitle的返回值啦!所有的設(shè)置都在view內(nèi)钻心。

5.BottomNavigationView

依賴

本文用的是 官方凄硼!官方!官方捷沸!

布局

<android.support.design.widget.CoordinatorLayout 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:id="@+id/activity_main2"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".ui.activity.MainActivity">

    <android.support.v4.view.ViewPager
        android:id="@+id/view_pager"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    <android.support.design.widget.BottomNavigationView
        android:id="@+id/navigation_view"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom"
        android:background="@drawable/black_line_white_inner"
        app:itemIconTint="@drawable/nav_item_color_state"
        app:itemTextColor="@drawable/nav_item_color_state"
        app:menu="@menu/bottom_navigation_view" />
</android.support.design.widget.CoordinatorLayout>

注意:app:menu="@menu/bottom_navigation_view"是必須設(shè)置的摊沉,這個(gè)布局里面是導(dǎo)航菜單的內(nèi)容。

bottom_navigation_view.xml

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

   <item
        android:id="@+id/wechat"
        android:icon="@mipmap/icon_project_not_selected"
        android:title="微信"
        app:showAsAction="always" />
    <item
        android:id="@+id/communcation"
        android:icon="@mipmap/icon_knowledge_hierarchy_not_selected"
        android:title="通訊錄"
        app:showAsAction="always" />
    <item
        android:id="@+id/find"
        android:icon="@mipmap/icon_navigation_not_selected"
        android:title="發(fā)現(xiàn)"
        app:showAsAction="always" />
    <item
        android:id="@+id/mine"
        android:icon="@mipmap/icon_home_pager_not_selected"
        android:title="我"
        app:showAsAction="always" />
</menu>

代碼

//監(jiān)聽VP痒给,觸發(fā)BNV
mViewPager.addOnPageChangeListener(onPageListener);
private ViewPager.OnPageChangeListener onPageListener = new ViewPager.OnPageChangeListener() {
        @Override
        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
        }

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

        @Override
        public void onPageScrollStateChanged(int state) {
        }
    };

//監(jiān)聽BNV说墨,觸發(fā)VP
bottomNavigationView.setOnNavigationItemSelectedListener(onItemSelectedListener);
private BottomNavigationView.OnNavigationItemSelectedListener onItemSelectedListener
            = new BottomNavigationView.OnNavigationItemSelectedListener() {
        @Override
        public boolean onNavigationItemSelected(@NonNull MenuItem item) {
            resetToDefaultIcon();//重置到默認(rèn)不選中圖片
            switch (item.getItemId()) {
                case R.id.wechat:
                    mViewPager.setCurrentItem(Constants.FIRST);
                    item.setIcon(R.mipmap.icon_project_not_selected);
                    break;
                case R.id.communcation:
                    mViewPager.setCurrentItem(Constants.SECOND);
                    break;
                case R.id.find:
                    mViewPager.setCurrentItem(Constants.THIRD);
                    break;
                case R.id.mine:
                    mViewPager.setCurrentItem(Constants.FOURTH);
                    break;
                default:
                    break;
            }
            return true;
        }
    };

//解決 BottomNavigationView 圖標(biāo)位移非等分問(wèn)題(使用的時(shí)候 item 數(shù)大于 3 個(gè)時(shí)會(huì)有位移)
BottomNavigationViewHelper.disableShiftMode(bottomNavigationView);

BottomNavigationViewHelper .java

public class BottomNavigationViewHelper {

    @SuppressLint("RestrictedApi")
    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);
        }
    }
}

補(bǔ)充

如果想禁用選中菜單時(shí)放大的動(dòng)畫效果,只需要在資源文件dimen.xml中苍柏,如下設(shè)置:

<!--BottomNavigationView 有文字-->
<dimen name="design_bottom_navigation_active_text_size">10sp</dimen>
<dimen name="design_bottom_navigation_text_size">10sp</dimen>


<!--BottomNavigationView 只放圖標(biāo)尼斧,無(wú)文字-->
<dimen name="design_bottom_navigation_active_text_size">0sp</dimen>
<dimen name="design_bottom_navigation_text_size">0sp</dimen>
<dimen name="design_bottom_navigation_margin">16dp</dimen>

Demo

https://github.com/liuleshuai/NavigationMenuBar

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市试吁,隨后出現(xiàn)的幾起案子棺棵,更是在濱河造成了極大的恐慌,老刑警劉巖潘悼,帶你破解...
    沈念sama閱讀 206,482評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件律秃,死亡現(xiàn)場(chǎng)離奇詭異爬橡,居然都是意外死亡治唤,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門糙申,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)宾添,“玉大人,你說(shuō)我怎么就攤上這事柜裸÷粕拢” “怎么了?”我有些...
    開封第一講書人閱讀 152,762評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵疙挺,是天一觀的道長(zhǎng)扛邑。 經(jīng)常有香客問(wèn)我,道長(zhǎng)铐然,這世上最難降的妖魔是什么蔬崩? 我笑而不...
    開封第一講書人閱讀 55,273評(píng)論 1 279
  • 正文 為了忘掉前任恶座,我火速辦了婚禮,結(jié)果婚禮上沥阳,老公的妹妹穿的比我還像新娘跨琳。我一直安慰自己,他們只是感情好桐罕,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,289評(píng)論 5 373
  • 文/花漫 我一把揭開白布脉让。 她就那樣靜靜地躺著,像睡著了一般功炮。 火紅的嫁衣襯著肌膚如雪溅潜。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,046評(píng)論 1 285
  • 那天薪伏,我揣著相機(jī)與錄音伟恶,去河邊找鬼。 笑死毅该,一個(gè)胖子當(dāng)著我的面吹牛博秫,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播眶掌,決...
    沈念sama閱讀 38,351評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼挡育,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了朴爬?” 一聲冷哼從身側(cè)響起即寒,我...
    開封第一講書人閱讀 36,988評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎召噩,沒(méi)想到半個(gè)月后母赵,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,476評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡具滴,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,948評(píng)論 2 324
  • 正文 我和宋清朗相戀三年凹嘲,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片构韵。...
    茶點(diǎn)故事閱讀 38,064評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡周蹭,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出疲恢,到底是詐尸還是另有隱情凶朗,我是刑警寧澤,帶...
    沈念sama閱讀 33,712評(píng)論 4 323
  • 正文 年R本政府宣布显拳,位于F島的核電站棚愤,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏杂数。R本人自食惡果不足惜宛畦,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,261評(píng)論 3 307
  • 文/蒙蒙 一矛绘、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧刃永,春花似錦货矮、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至读规,卻和暖如春抓督,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背束亏。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工铃在, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人碍遍。 一個(gè)月前我還...
    沈念sama閱讀 45,511評(píng)論 2 354
  • 正文 我出身青樓定铜,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親怕敬。 傳聞我的和親對(duì)象是個(gè)殘疾皇子揣炕,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,802評(píng)論 2 345

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