Android底部導(dǎo)航——BottomNavigationView+ViewPager+Fragment

前言:
Android端采用底部導(dǎo)航欄的APP非常多赖淤,比如微信蜀漆、微博、支付寶...等等咱旱,這也不能說(shuō)是盲目學(xué)習(xí)iOS确丢,畢竟好東西大家都可以用,各家操作系統(tǒng)也都在博采眾長(zhǎng)吐限,互相學(xué)習(xí)鲜侥。Android端的底部導(dǎo)航欄有著一套規(guī)范,詳情诸典。

這篇文章為大家?guī)?lái)
1.官方BottomNavigationView的使用方法
2.結(jié)合ViewPager描函、Fragment實(shí)現(xiàn)一個(gè)流行UI布局!
3.并用反射解決ViewPager與BottomNavigationView側(cè)滑聯(lián)動(dòng)時(shí)的一個(gè)小問(wèn)題狐粱。

最終實(shí)現(xiàn)效果:


圖片.png

在谷歌官方發(fā)布BottomNavigationView控件之前我們可以自己組合控件實(shí)現(xiàn)舀寓,比如LinearLayout + TextView(使用android:drawableTop屬性+selector狀態(tài)切換)、RadioGroup + RadioButton等等組合控件的方法自定義實(shí)現(xiàn)復(fù)雜效果肌蜻。除了第三方外互墓,現(xiàn)在我們多了一個(gè)選擇。

開(kāi)始
1.新建project然后導(dǎo)入以下support:design library蒋搜,BottomNavigationView就在這個(gè)design庫(kù)中篡撵。順帶導(dǎo)入這個(gè)V4包判莉,因?yàn)榇龝?huì)要使用到ViewPager(這里其實(shí)不用記這么麻煩的庫(kù)跟版本怎么寫(xiě),直接在AS 的design面板把控件拖進(jìn)來(lái)就會(huì)自動(dòng)導(dǎo)入了育谬。)

compile'com.android.support:design:25.0.1'
compile'com.android.support:support-v4:25.0.1'

2.在res下新建menu文件夾券盅,新建一個(gè)menu菜單,多少個(gè)可以根據(jù)自己的需要:

<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:id="@+id/item_news" android:icon="@mipmap/ic_news_gray" android:title="新聞" />
    <item android:id="@+id/item_lib" android:icon="@mipmap/ic_library_gray" android:title="圖書(shū)" />
    <item android:id="@+id/item_find" android:icon="@mipmap/ic_discovery_gray" android:title="發(fā)現(xiàn)" />
    <item android:id="@+id/item_more" android:icon="@mipmap/ic_more_gray" android:title="更多" />
</menu>

3.接著是布局文件主要代碼如下(最下面的View效果是加一個(gè)陰影):

<android.support.v4.view.ViewPager
        android:id="@+id/viewpager"
        android:layout_width="match_parent" 
        android:layout_height="match_parent" 
        android:layout_above="@+id/bottom_navigation" />
    <android.support.design.widget.BottomNavigationView 
        android:id="@+id/bottom_navigation" 
        android:layout_width="match_parent" 
        android:layout_height="wrap_content" 
        android:layout_alignParentBottom="true" 
        app:itemIconTint="@drawable/bottom_navigation_selector"
        app:itemTextColor="@drawable/bottom_navigation_selector"
        app:menu="@menu/menu_bottom_navigation" />

    <View 
        android:layout_width="match_parent" 
        android:layout_height="5dp" 
        android:layout_above="@id/bottom_navigation" 
        android:background="@drawable/bottom_shadow" />

app:itemIconTint="@drawable/bottom_navigation_selector" 為icon著色膛檀,寫(xiě)個(gè)selector即可锰镀。
app:itemTextColor="@drawable/bottom_navigation_selector"該屬性為文字著色,同樣寫(xiě)個(gè)selector實(shí)現(xiàn)點(diǎn)擊顏色切換的效果宿刮。

4.寫(xiě)完布局寫(xiě)代碼互站,完整activity文件如下:
實(shí)例化控件后為NavigationView添加監(jiān)聽(tīng)事件即可,代碼如下:

package com.fedming.bottomnavigationdemo;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.design.widget.BottomNavigationView;
import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;
import android.view.MenuItem;

import com.example.think.shaiwangbaodian.R;
import com.example.think.shaiwangbaodian.base.BaseFragment;

/**
 * Created by 程大龍 on 2018/10/15.
 * HomeActivity 主界面
 */
public class HomeActivity extends AppCompatActivity {
    private ViewPager viewPager;
    private MenuItem menuItem;
    private BottomNavigationView bottomNavigationView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_home);
        viewPager = (ViewPager) findViewById(R.id.viewpager);
        bottomNavigationView = (BottomNavigationView) findViewById(R.id.bottom_navigation);
        //默認(rèn) >3 的選中效果會(huì)影響ViewPager的滑動(dòng)切換時(shí)的效果僵缺,故利用反射去掉
        BottomNavigationViewHelper.disableShiftMode(bottomNavigationView);
        bottomNavigationView.setOnNavigationItemSelectedListener(
                new BottomNavigationView.OnNavigationItemSelectedListener() {
                    @Override
                    public boolean onNavigationItemSelected(@NonNull MenuItem item) {
                        switch (item.getItemId()) {
                            case R.id.item_news:
                                viewPager.setCurrentItem(0);
                            break;
                            case R.id.item_lib:
                                viewPager.setCurrentItem(1);
                                break;
                                case
                                        R.id.item_find:
                                    viewPager.setCurrentItem(2);
                                break;
                                case R.id.item_more:
                                    viewPager.setCurrentItem(3);
                                    break;
                        }
                        return false;
                    }
                });

        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) { }
                });

        setupViewPager(viewPager);
    }

    private void setupViewPager(ViewPager viewPager) { 
        ViewPagerAdapter adapter = new ViewPagerAdapter(getSupportFragmentManager()); 
        adapter.addFragment(BaseFragment.newInstance("新聞")); 
        adapter.addFragment(BaseFragment.newInstance("圖書(shū)")); 
        adapter.addFragment(BaseFragment.newInstance("發(fā)現(xiàn)")); 
        adapter.addFragment(BaseFragment.newInstance("更多")); 
        viewPager.setAdapter(adapter);
    }
   
    }
    

可以看到BottomNavigationView+ViewPager+Fragment可以實(shí)現(xiàn)流行的布局框架胡桃。


圖片.png

不知道細(xì)心的朋友是否可能發(fā)現(xiàn)了,上面的代碼為什么用了反射呢磕潮?
原因就是官方的BottomNavigationView默認(rèn)有個(gè)放大的ShiftingMode效果翠胰,但是尚未支持代碼層級(jí)的切換。在3個(gè)menu item及以下時(shí)默認(rèn)關(guān)閉自脯,而到了4個(gè)及以上時(shí)就懵逼了之景,因?yàn)槲覀兪且鯲iewPager的側(cè)滑。


圖片.png

沒(méi)辦法了膏潮,查了一圈資料锻狗,發(fā)現(xiàn)官方這個(gè)控件還不支持代碼層級(jí)的切換選項(xiàng)(如果你發(fā)現(xiàn)了,請(qǐng)告訴我)焕参。迫不得已轻纪,只能看源碼,開(kāi)啟反射模式了叠纷!
圖片.png
import android.support.design.internal.BottomNavigationItemView;
import android.support.design.internal.BottomNavigationMenuView;
import android.support.design.widget.BottomNavigationView;

import java.lang.reflect.Field;

public class BottomNavigationViewHelper {
    public static 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 (NoSuchFieldException | IllegalAccessException e) {
            e.printStackTrace();
        }
    }
}

我們通過(guò)反射拿到了BottomNavigationMenuView刻帚,至于為什么是BottomNavigationView 的第一個(gè)子View這就要看源碼了,AS中直接可以點(diǎn)進(jìn)去看涩嚣。然后我們知道了這個(gè)效果是由mShiftingMode來(lái)決定的崇众,那么上面的代碼就好理解了。

在實(shí)例化BottomNavigationView后調(diào)用一次這行代碼即可:

BottomNavigationViewHelper.disableShiftMode(bottomNavigationView;

如果不需要配置側(cè)滑切換的話航厚,直接默認(rèn)效果就好顷歌,不要去反射修改了。禁止滑動(dòng)時(shí)幔睬,為ViewPager添加setOnTouchListener接口眯漩,在onTouch下直接消費(fèi)掉點(diǎn)擊事件。添加下面這段代碼:

//禁止ViewPager滑動(dòng)
 viewPager.setOnTouchListener(new View.OnTouchListener(){
    @Override
      public boolean onTouch(View v,MotionEvent event){
        return true;
        }
 });

最后來(lái)看下效果:

圖片.png

在文章的最后附上源碼大家有什么意見(jiàn)或者補(bǔ)充的可以指出溪窒。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末坤塞,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子澈蚌,更是在濱河造成了極大的恐慌摹芙,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,509評(píng)論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件宛瞄,死亡現(xiàn)場(chǎng)離奇詭異浮禾,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)份汗,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,806評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門(mén)盈电,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人杯活,你說(shuō)我怎么就攤上這事匆帚。” “怎么了旁钧?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,875評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵吸重,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我歪今,道長(zhǎng)嚎幸,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,441評(píng)論 1 293
  • 正文 為了忘掉前任寄猩,我火速辦了婚禮嫉晶,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘田篇。我一直安慰自己替废,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,488評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布斯辰。 她就那樣靜靜地躺著舶担,像睡著了一般。 火紅的嫁衣襯著肌膚如雪彬呻。 梳的紋絲不亂的頭發(fā)上衣陶,一...
    開(kāi)封第一講書(shū)人閱讀 51,365評(píng)論 1 302
  • 那天,我揣著相機(jī)與錄音闸氮,去河邊找鬼剪况。 笑死,一個(gè)胖子當(dāng)著我的面吹牛蒲跨,可吹牛的內(nèi)容都是我干的译断。 我是一名探鬼主播,決...
    沈念sama閱讀 40,190評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼或悲,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼孙咪!你這毒婦竟也來(lái)了堪唐?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,062評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤翎蹈,失蹤者是張志新(化名)和其女友劉穎淮菠,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體荤堪,經(jīng)...
    沈念sama閱讀 45,500評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡合陵,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,706評(píng)論 3 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了澄阳。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片拥知。...
    茶點(diǎn)故事閱讀 39,834評(píng)論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖碎赢,靈堂內(nèi)的尸體忽然破棺而出低剔,到底是詐尸還是另有隱情,我是刑警寧澤肮塞,帶...
    沈念sama閱讀 35,559評(píng)論 5 345
  • 正文 年R本政府宣布户侥,位于F島的核電站,受9級(jí)特大地震影響峦嗤,放射性物質(zhì)發(fā)生泄漏蕊唐。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,167評(píng)論 3 328
  • 文/蒙蒙 一烁设、第九天 我趴在偏房一處隱蔽的房頂上張望替梨。 院中可真熱鬧,春花似錦装黑、人聲如沸副瀑。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,779評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)糠睡。三九已至,卻和暖如春疚颊,著一層夾襖步出監(jiān)牢的瞬間狈孔,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,912評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工材义, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留均抽,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,958評(píng)論 2 370
  • 正文 我出身青樓其掂,卻偏偏與公主長(zhǎng)得像油挥,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,779評(píng)論 2 354