1. 程式人生 > >Android底部導航欄——bottomnavigation結合viewpager的實現

Android底部導航欄——bottomnavigation結合viewpager的實現

前言

在谷歌官方釋出BottomNavigationView控制元件之前我們可以自己組合控制元件實現,比如LinearLayout + TextView(使用android:drawableTop屬性+selector狀態切換)、RadioGroup + RadioButton等等組合控制元件的方法自定義實現複雜效果。除了第三方外,現在我們多了一個選擇,就是使用bottomnavigation結合viewpager的實現。後期會有文章介紹前面的幾種實現方式,對比下來這種程式碼量最少,而且特別清晰。

一 使用LinearLayout + TextView實現了底部導航欄的效果

學習一種新的技術首先要看在gradle中的配置(菇涼能力有限這篇文章只講解這個控制元件是怎麼用的,感興趣的小夥伴可以去看原始碼):

1.1 build.gradle中

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

1.2 xml檔案中

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    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"
    tools:context="com.bottomnavigationview.MainActivity">

    <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_selector"
        app:itemTextColor="@drawable/bottom_selector"
        app:menu="@menu/bottom_menu" />

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

這裡簡單說一下android.support.design.widget.BottomNavigationView中的一些屬性:

  1. app:itemIconTint="@drawable/bottom_selector" :這個是底部欄圖片顏色變化的selector

  2. app:itemTextColor="@drawable/bottom_selector":這個是底部欄文字顏色變化的selector

  3. app:menu="@menu/bottom_menu":這個是底部欄模組item的定義,包括文字和圖片

1.3 在res下新建menu資料夾,新建一個menu選單 ,程式碼如下:
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">

    <item
        android:id="@+id/item_news"
        android:icon="@mipmap/icon_navigation_home_01"
        android:title="首頁" />
    <item
        android:id="@+id/item_lib"
        android:icon="@mipmap/icon_navigation_shop_01"
        android:title="搜尋" />
    <item
        android:id="@+id/item_find"
        android:icon="@mipmap/icon_navigation_ucenter_01"
        android:title="我的" />
    <item
        android:id="@+id/item_more"
        android:icon="@mipmap/icon_navigation_home_01"
        android:title="主頁" />
</menu>
1.4 在drawable下新建.xml檔案 顏色和文字變化的顏色一樣selector,程式碼如下
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:color="@color/p_select_color"         android:state_checked="true" />
    <item android:color="@color/p_99_color" android:state_checked="false" />
</selector>
1.5 MainActivity中的主要程式碼:
package com.bottomnavigationview;

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;

public class MainActivity extends AppCompatActivity {

    private ViewPager viewPager;
    private MenuItem menuItem;
    private BottomNavigationView bottomNavigationView;

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

        viewPager = (ViewPager) findViewById(R.id.viewpager);
        bottomNavigationView = (BottomNavigationView)findViewById(R.id.bottom_navigation);
        //預設 >3 的選中效果會影響ViewPager的滑動切換時的效果,故利用反射去掉
 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) {
            }
        });

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

        setupViewPager(viewPager);
    }

    private void setupViewPager(ViewPager viewPager) {
        ViewPagerAdapter adapter = new ViewPagerAdapter(getSupportFragmentManager());

        adapter.addFragment(BaseFragment.newInstance("首頁"));
        adapter.addFragment(BaseFragment.newInstance("搜尋"));
        adapter.addFragment(BaseFragment.newInstance("我的"));
        adapter.addFragment(BaseFragment.newInstance("主頁"));
        viewPager.setAdapter(adapter);
    }

}

注意點:

官方的BottomNavigationView用起來雖然簡單,但是有一個很坑的問題,就是它內部在item>4的時候出現動畫的效果。如下:

這裡寫圖片描述

在平時的開發中這種效果也是特別崩潰的,因為官方沒有預設的方法取消這種效果,這時候需要我們用到反射。

建立一個Helper類

package com.bottomnavigationview;

import android.annotation.TargetApi;
import android.os.Build;
import android.support.design.internal.BottomNavigationItemView;
import android.support.design.internal.BottomNavigationMenuView;
import android.support.design.widget.BottomNavigationView;

import java.lang.reflect.Field;

/**
 * Created by 涼菇涼 on 2017/8/2.
 */
public class BottomNavigationViewHelper {

    @TargetApi(Build.VERSION_CODES.KITKAT)
    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();
        }
    }

}

使用的時候


 BottomNavigationViewHelper.disableShiftMode(bottomNavigationView);

1.6 其實避了這個坑是特別簡單的,viewpager就不在這裡詳細說了。來上一張效果動圖。

這裡寫圖片描述

補充 :

BaseFragment中的程式碼

public class BaseFragment  extends Fragment {

    private static final String EXTRA_CONTENT = "content";


    public static BaseFragment newInstance(String content){
        Bundle arguments = new Bundle();
        arguments.putString(EXTRA_CONTENT, content);
        BaseFragment tabContentFragment = new BaseFragment();
        tabContentFragment.setArguments(arguments);
        return tabContentFragment;
    }

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View contentView = inflater.inflate(R.layout.fragment_tab_content, null);
        ((TextView)contentView.findViewById(R.id.tv_content)).setText(getArguments().getString(EXTRA_CONTENT));
        return contentView;
    }
}