1. 程式人生 > >方便快捷地實現底部導航欄,包含顯示未讀數、提示小紅點、動畫等功能

方便快捷地實現底部導航欄,包含顯示未讀數、提示小紅點、動畫等功能

輕量級底部導航欄

目前市場上的App,幾乎都有底部頁籤導航欄,所以我們在開發的時候經常需要用到這個,雖然 github 上有不少已經封裝好的底部導航欄的工具,例如 bottombar,alphaIndicator(仿微信滑動漸變底部控制元件)等,但是這些控制元件由於功能太多,而且也沒有給予詳細的介紹文件,所以用起來不是特別容易,有時候我們僅僅只是想要一個簡簡單單的底部導航,但是又不想去自己在佈局中搞一個個 LinearLayout 或者 RadioGroup,然後切換頁籤的時候更換圖示,讓 ViewPager 跳轉到對應的頁面等一系列繁瑣的操作,這時候,你可以使用 BottomBarLayout,簡簡單單就可以實現以下效果:
這裡寫圖片描述

顯示未讀數、提示小紅點、提示訊息
這裡寫圖片描述

BottomBarLayout的使用

BottomBarItem屬性介紹

<!--預設狀態下的圖示-->
<attr name="iconNormal" format="reference"/>
<!--選中狀態下的圖示-->
<attr name="iconSelected" format="reference"/>
<!--底部文字-->
<attr name="itemText" format="string"/>
<!--文字大小-->
<attr
name="itemTextSize" format="dimension"/>
<!--預設狀態下的文字顏色--> <attr name="textColorNormal" format="color"/> <!--選中狀態下的文字顏色--> <attr name="textColorSelected" format="color"/> <!--文字和圖示的頂部距離--> <attr name="itemMarginTop" format="dimension"/> <!--是否開啟觸控背景效果--> <attr
name="openTouchBg" format="boolean"/>
<!--設定觸控背景--> <attr name="touchDrawable" format="reference"/> <!--設定圖示的寬度--> <attr name="iconWidth" format="dimension"/> <!--設定圖示的高度--> <attr name="iconHeight" format="dimension"/> <!--設定BottomBarItem的padding--> <attr name="itemPadding" format="dimension"/> <!--設定未讀數字體大小--> <attr name="unreadTextSize" format="dimension"/> <!--設定提示訊息字型大小--> <attr name="msgTextSize" format="dimension"/> <!--設定未讀陣列閾值 大於閾值的數字將顯示為 n+ n為設定的閾值--> <attr name="unreadThreshold" format="integer"/>

佈局檔案中配置

在xml檔案中,配置 BottomBarLayout,包裹子條目 BottomBarItem

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

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

    <com.chaychan.library.BottomBarLayout
        android:id="@+id/bbl"
        android:layout_width="match_parent"
        android:layout_height="45dp"
        android:orientation="horizontal"
        android:gravity="center"
        android:layout_gravity="center"
        android:background="@color/tab_gb">

        <com.chaychan.library.BottomBarItem
            android:layout_width="0dp"
            android:layout_weight="1"
            android:layout_height="match_parent"
            app:iconNormal="@mipmap/tab_home_normal"
            app:iconSelected="@mipmap/tab_home_selected"
            app:itemText="首頁"
            app:textColorNormal="@color/tab_normal_color"
            app:textColorSelected="@color/tab_selected_color"
            app:itemTextSize="8sp"
            app:itemMarginTop="-5dp"/>

        <com.chaychan.library.BottomBarItem
            android:layout_width="0dp"
            android:layout_weight="1"
            android:layout_height="match_parent"
            app:iconNormal="@mipmap/tab_video_normal"
            app:iconSelected="@mipmap/tab_video_selected"
            app:itemText="視訊"
            app:textColorNormal="@color/tab_normal_color"
            app:textColorSelected="@color/tab_selected_color"
            app:itemTextSize="8sp"
            app:itemMarginTop="-5dp"/>


        <com.chaychan.library.BottomBarItem
            android:layout_width="0dp"
            android:layout_weight="1"
            android:layout_height="match_parent"
            app:iconNormal="@mipmap/tab_micro_normal"
            app:iconSelected="@mipmap/tab_micro_selected"
            app:itemText="微頭條"
            app:textColorNormal="@color/tab_normal_color"
            app:textColorSelected="@color/tab_selected_color"
            app:itemTextSize="8sp"
            app:itemMarginTop="-5dp"/>

        <com.chaychan.library.BottomBarItem
            android:layout_width="0dp"
            android:layout_weight="1"
            android:layout_height="match_parent"
            app:iconNormal="@mipmap/tab_me_normal"
            app:iconSelected="@mipmap/tab_me_selected"
            app:itemText="我的"
            app:textColorNormal="@color/tab_normal_color"
            app:textColorSelected="@color/tab_selected_color"
            app:itemTextSize="8sp"
            app:itemMarginTop="-5dp"/>

    </com.chaychan.library.BottomBarLayout>

</LinearLayout>

java檔案中設定

找過對應的 ViewPager 和 BottomBarLayout,為 ViewPager 設定 Adapter,然後為 BottomBarLayout 設定 ViewPager

mVpContent.setAdapter(new MyAdapter(getSupportFragmentManager()));
mBottomBarLayout.setViewPager(mVpContent);

這樣就實現底部導航欄功能了

開啟滑動效果

頁籤之間的切換預設關閉了滑動效果,如果需要開啟可以通過呼叫 BottomBarLayout 的 setSmoothScroll() 方法:

mBottomBarLayout.setSmoothScroll(true);

也可以在佈局檔案中指定 BottomBarLayout 的 smoothScroll 屬性為 true

設定條目選中的監聽

mBottomBarLayout.setOnItemSelectedListener(new BottomBarLayout.OnItemSelectedListener() {
        @Override
        public void onItemSelected(final BottomBarItem bottomBarItem, int position) {
               //do something
        }
});

顯示未讀數、提示小紅點、提示訊息

mBottomBarLayout.setUnread(0,20);//設定第一個頁籤的未讀數為20
mBottomBarLayout.setUnread(1,101);//設定第二個頁籤的未讀書
mBottomBarLayout.showNotify(2);//設定第三個頁籤顯示提示的小紅點
mBottomBarLayout.setMsg(3,"NEW");//設定第四個頁籤顯示NEW提示文字

當設定的未讀數小於或等於0時,消失未讀數的小紅點將會消失;

當未讀數為1-99時,則顯示對應的數字;

當未讀數大於99時,顯示99+;

設定未讀數閾值

未讀數的閾值可以指定 BottomBarItem的unreadThreshold 屬性,預設該值為99,如設定 app:unreadThreshold=”999” , 若未讀數超過該值,則顯示”999+”。

隱藏提示小紅點、提示訊息

mBottomBarLayout.hideNotify(2);//隱藏第三個頁籤顯示提示的小紅點
mBottomBarLayout.hideMsg(3);//隱藏第四個頁籤顯示的提示文字

BottomBarItem的介紹

BottomBarItem 繼承於 LinearLayout,其 子View 有顯示圖示的 ImageView 和展示文字的 TextView,分別可以通過 getImageView() 和 getTextView() 方法獲取到對應的子控制元件。

github 上不少底部導航欄的控制元件都沒能獲取到對應的子控制元件,所以在需要對子控制元件進行操作的時候極不方便,有一些的思路並不是用 ImageView 和 TextView,而是用繪製的,所以也不能獲取到對應的顯示圖示的控制元件或展示文字的控制元件,造成無法獲取到該控制元件,無法進行一些業務上的操作,比如類似今日頭條的底部的首頁,點選首頁的頁籤,會更換成載入中的圖示,執行旋轉動畫,BottomBarLayout 可以輕鬆地做到這個需求。演示效果如下:
這裡寫圖片描述

只需為 BottomBarLayout 設定頁籤選中的監聽,在回撥中進行以下處理:

mBottomBarLayout.setOnItemSelectedListener(new BottomBarLayout.OnItemSelectedListener() {
    @Override
    public void onItemSelected(final BottomBarItem bottomBarItem, int position) {
        if (position == 0){
            //如果是第一個,即首頁
            if (mBottomBarLayout.getCurrentItem() == position){
                //如果是在原來位置上點選,更換首頁圖示並播放旋轉動畫
                bottomBarItem.setIconSelectedResourceId(R.mipmap.tab_loading);//更換成載入圖示
                bottomBarItem.setStatus(true);

                //播放旋轉動畫
                if (mRotateAnimation == null) {
                    mRotateAnimation = new RotateAnimation(0, 360,
                            Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF,
                            0.5f);
                    mRotateAnimation.setDuration(800);
                    mRotateAnimation.setRepeatCount(-1);
                }
                ImageView bottomImageView = bottomBarItem.getImageView();
                bottomImageView.setAnimation(mRotateAnimation);
                bottomImageView.startAnimation(mRotateAnimation);//播放旋轉動畫

                //模擬資料重新整理完畢
                mHandler.postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        bottomBarItem.setIconSelectedResourceId(R.mipmap.tab_home_selected);//更換成首頁原來圖示
                        bottomBarItem.setStatus(true);//重新整理圖示
                        cancelTabLoading(bottomBarItem);
                    }
                },3000);
                return;
            }
        }

        //如果點選了其他條目
        BottomBarItem bottomItem = mBottomBarLayout.getBottomItem(0);
        bottomItem.setIconSelectedResourceId(R.mipmap.tab_home_selected);//更換為原來的圖示
        cancelTabLoading(bottomItem);//停止旋轉動畫
    }
});

/**停止首頁頁籤的旋轉動畫*/
private void cancelTabLoading(BottomBarItem bottomItem) {
    Animation animation = bottomItem.getImageView().getAnimation();
    if (animation != null){
        animation.cancel();
    }
}

實現思路

  1. 當點選頁籤載入的時候,BottomBarItem 通過呼叫 setIconSelectedResourceId() 設定成選中狀態下的圖示資源id為載入中圖示的資源id,完成圖示的更換操作;

  2. 通過 BottomBarItem 獲取到對應頁籤的 ImageView,對其設定旋轉動畫,執行旋轉動畫,當點選其他頁籤或者資料載入完成後,更換回原來的選中圖示,停止旋轉動畫。

原始碼解析

BottomBarLayout

BottomBarLayout 繼承於 LinearLayout,相當於是存放標籤(BottomBarItem)的容器,同時它也監聽了 ViewPager 滑動,通過 setViewPager(ViewPager viewPager) 傳入對應的 ViewPager 物件,在 BottomBarLayout 中為 ViewPager 設定滾動監聽,在對應 onPageSelected() 回撥中做處理。

onPageSelected() 回撥中的處理:

@Override
public void onPageSelected(int position) {
    resetState();
    mItemViews.get(position).setStatus(true);
    if (onItemSelectedListener != null){
        onItemSelectedListener.onItemSelected(getBottomItem(position),position);
    }
    mCurrentItem = position;//記錄當前位置
}

/**
* 重置當前按鈕的狀態
*/
private void resetState() {
  if (mCurrentItem < mItemViews.size()){
      mItemViews.get(mCurrentItem).setStatus(false);
  }
}

回撥中做的處理分別是:

  1. 重置頁籤的狀態,更改選中標籤圖示的狀態;

  2. 如果有設定 OnItemSelectedListener,即選中子條目的監聽,則回撥;

  3. 記錄當前選中的位置。

BottomBarItem

BottomBarItem 也是繼承於 LinearLayout,相當於是一個標籤條目,Orientation 為垂直方向,它的佈局中包含 ImageView 和 TextView,其中 ImageView 用於顯示標籤條目的圖示,TextView 用於顯示圖示下面的文字,還有一些 TextView 用於顯示未讀數,提示小紅點、提示訊息。

其中設定圖示的方法為:

public void setStatus(boolean isSelected) {
    mImageView.setImageDrawable(getResources().getDrawable(isSelected ? mIconSelectedResourceId : mIconNormalResourceId));
    mTextView.setTextColor(isSelected ? mTextColorSelected : mTextColorNormal);
}

通過傳入是否選中切換對應圖示的id,也就是 BottomBarLayout 中呼叫 mItemViews.get(position).setStatus(true/false) 來更改標籤的選中狀態。

顯示未讀數,提示小紅點、提示訊息的方法:

/**
 * 設定未讀數
 *
 * @param unreadNum 小於等於{@link com.chaychan.library.BottomBarItem#unreadNumThreshold}則隱藏,
 *                  大於0小於{@link com.chaychan.library.BottomBarItem#unreadNumThreshold}則顯示對應數字,
 *                  超過{@link com.chaychan.library.BottomBarItem#unreadNumThreshold}
 *                  顯示{@link com.chaychan.library.BottomBarItem#unreadNumThreshold}+
 */
public void setUnreadNum(int unreadNum) {
    setTvVisiable(mTvUnread);
    if (unreadNum <= 0) {
        mTvUnread.setVisibility(GONE);
    } else if (unreadNum <= unreadNumThreshold) {
        mTvUnread.setText(String.valueOf(unreadNum));
    } else {
        mTvUnread.setText(String.format(Locale.CHINA, "%d+", unreadNumThreshold));
    }
}

public void showNotify() {
    setTvVisiable(mTvNotify);
}

public void setMsg(String msg) {
    setTvVisiable(mTvMsg);
    mTvMsg.setText(msg);
}

其中,設定未讀數通過判斷傳入的未讀數做處理,如果小於等於0,則隱藏未讀數;如果大於閾值,則顯示為”閾值+”;提示小紅點、提示訊息的顯示則是將對應TextView設定為可見。

同理,還有隱藏提示小紅點、提示訊息的方法:

public void hideNotify() {
    mTvNotify.setVisibility(GONE);
}

public void hideMsg() {
    mTvMsg.setVisibility(GONE);
}

BottomLayout 中設定對應頁籤的未讀數的方法:

/**
 * 設定未讀數
 * @param position 底部標籤的下標
 * @param unreadNum 未讀數
 */
public void setUnread(int position,int unreadNum){
    mItemViews.get(position).setUnreadNum(unreadNum);
}

通過找到對應的 BottomBarItem,再呼叫 BottomBarItem 中的 setUnreadNum(int unreadNum) 方法,同理,顯示(隱藏)對應頁籤提示小紅點和顯示(隱藏)提示訊息的方法的處理也是如此。

好了,到這裡 BottomBarLayout 的介紹就到此為止了,之所以封裝這個控制元件主要是為了方便開發,希望可以幫助到更多人,如果大家有什麼想法或者意見不妨向我提出,我會不斷完善 BottomBarLayout 的。

原始碼地址:

https://github.com/chaychan/BottomBarLayout