HarmonyOS三方件開發指南(17)-BottomNavigationBar

目錄:編程

一、引言json

二、功能介紹架構

三、BottomNavigationBar使用指南app

四、BottomNavigationBar開發指南ide

五、《HarmonyOS三方件開發指南》文章合集模塊化

 

引言佈局

       BottomNavigationBar底部導航欄,能夠說全部的app是這樣的頁面架構,緣由很簡單,操做簡單,模塊化清晰,頁面切換流暢,並且每頁均可以展現不一樣的風格。相信開發者已經很熟悉Android的底部導航欄的開發以及開發流程,那麼接下來將對比Android來說解鴻蒙的底部導航欄的實現步驟。post

功能介紹字體

      鴻蒙BottomNavigationBar底部導航欄,根據所須要底部button的數量,動態生成對應的底部button,而且能夠設置默認字體顏色,選中字體顏色,默認icon,選中icon屬性。模擬器效果圖以下:this

【軟通動力】HarmonyOS三方件開發指南(17)-BottomNavigationBar

看了效果圖,是否是都想知道在實際工做中,是如何使用的呢?接下來給你們詳細介紹下BottomNavigationBar如何使用。

BottomNavigationBar使用指南

Ø 新建工程, 添加組件Har包依賴

在應用模塊中添加HAR,只須要將mylibrarybottom-debug.har複製到entry\libs目錄下便可。

Ø 修改相關文件

1. 修改主頁面的佈局文件ability_main.xml:

【軟通動力】HarmonyOS三方件開發指南(17)-BottomNavigationBar

2. 修改MainAbilitySlice代碼:

【軟通動力】HarmonyOS三方件開發指南(17)-BottomNavigationBar

3. 修改BaseAbilitySlinct代碼:

【軟通動力】HarmonyOS三方件開發指南(17)-BottomNavigationBar

4. MainAbility的代碼:

【軟通動力】HarmonyOS三方件開發指南(17)-BottomNavigationBar

配置好1-4步,接下來就看如何給對應的底部導航欄添加Fraction

1. initBottom 方法以下:

private void initBottom() {
    tabBottomLayout = (BottomNavigationBar)  mAbilitySliceProvider.findComponentById(ResourceTable.Id_bottom_navigation_bar);
    bottomInfoList = new ArrayList<>();
    // 獲取string.json文件中定義的字符串
    String home = mAbilitySliceProvider.getString(ResourceTable.String_home);
    String favorite = mAbilitySliceProvider.getString(ResourceTable.String_favorite);
    String category = mAbilitySliceProvider.getString(ResourceTable.String_category);
    String profile = mAbilitySliceProvider.getString(ResourceTable.String_mine);
    // 首頁
    BottomBarInfo<Integer> homeInfo = new BottomBarInfo<>(home,
            ResourceTable.Media_category_norma1,
            ResourceTable.Media_category_norma2,
            defaultColor, tintColor);
    homeInfo.fraction = HomeFraction.class;
    // 收藏
    BottomBarInfo<Integer> favoriteInfo = new BottomBarInfo<>(favorite,
            ResourceTable.Media_category_norma1,
            ResourceTable.Media_category_norma2,
            defaultColor, tintColor);
    favoriteInfo.fraction = SecondFraction.class;
    // 分類
    BottomBarInfo<Integer> categoryInfo = new BottomBarInfo<>(category,
            ResourceTable.Media_category_norma1,
            ResourceTable.Media_category_norma2,
            defaultColor, tintColor);
    categoryInfo.fraction = ThirdFraction.class;
    // 個人
    BottomBarInfo<Integer> profileInfo = new BottomBarInfo<>(profile,
            ResourceTable.Media_category_norma1,
            ResourceTable.Media_category_norma2,
            defaultColor, tintColor);
    profileInfo.fraction = MineFraction.class;

    // 將每一個條目的數據放入到集合
    bottomInfoList.add(homeInfo);
    bottomInfoList.add(favoriteInfo);
    bottomInfoList.add(categoryInfo);
    bottomInfoList.add(profileInfo);
    // 設置底部導航欄的透明度
    tabBottomLayout.setBarBottomAlpha(0.85f);
    // 初始化全部的條目
    tabBottomLayout.initInfo(bottomInfoList);
    initFractionBarComponent();
    tabBottomLayout.addBarSelectedChangeListener((index, prevInfo, nextInfo) ->
            // 顯示fraction
            mFractionBarComponent.setCurrentItem(index));
    // 設置默認選中的條目,該方法必定要在最後調用
    tabBottomLayout.defaultSelected(homeInfo);

2. 建立fraction類,繼承BaseFraction

1. 引入須要展現頁面的佈局文件

@Override
public int getUIComponent() {
    return ResourceTable.Layout_layout_fraction_home;
}

2. 操做佈局文件中的控件

@Override
public void initComponent(Component component) {
    text = (Text) component.findComponentById(ResourceTable.Id_text);
}

BottomNavigationBar開發指南

       底部導航欄,在應用中真的很是常見,核心思想就是底部有幾個選項,而後點擊其中任意一個,切換至對應的頁面。接下來主要介紹下核心實現步驟。

主要封裝的原則是,動態的,經過外界傳遞,固定過的則封裝起來。其中底部導航欄的圖片、文字、文字的顏色是變的,其它的能夠封裝起來,外界只須要把每一個條目的圖片、文字以及文字的顏色傳入進來便可,內部來實現底部導航欄。在封裝的時候,須要面向接口編程,同時使用泛型。

定義接口IBarLayout

一、定義一個IBarLayout接口,第一個泛型就是底部導航欄中的每一個條目,第二個泛型是每一個條目的數據。在接口裏面提供一些方法,能夠根據數據查找條目,能夠添加監聽,能夠設置默認選中的條目,能夠初始化全部的條目,當某個條目被選中後須要經過回調方法。

代碼以下:

public interface IBarLayout<Bar extends ComponentContainer, D> {

    /**
     * 根據數據查找條目
     *
     * @param info 數據
     * @return 條目
     */
    Bar findBar(D info);

    /**
     * 添加監聽
     *
     * @param listener
     */
    void addBarSelectedChangeListener(OnBarSelectedListener<D> listener);

    /**
     * 默認選中的條目
     *
     * @param defaultInfo
     */
    void defaultSelected(D defaultInfo);

    /**
     * 初始化全部的條目
     *
     * @param infoList
     */
    void initInfo(List<D> infoList);

    interface OnBarSelectedListener<D> {

        /**
         * 當某個條目被選中後的回調,該方法會被調用屢次
         *
         * @param index 點擊後選中條目的下標
         * @param preInfo 點擊前選中的條目
         * @param nextInfo 點擊後選中的條目
         */
        void onBarSelectedChange(int index, D preInfo, D nextInfo);
    }
}

二、再定義一個單個條目的接口IBar,泛型就是每一個條目的數據,接口裏面定義方法,能夠設置條目的數據,能夠動態修改某個條目的大小

代碼以下:

/**
 * 單個條目的接口
 */
public interface IBar<D> extends IBarLayout.OnBarSelectedListener<D> {

    /**
     * 設置條目的數據
     *
     * @param data
     */
    void setBarInfo(D data);

    /**
     * 動態修改某個條目的大小
     *
     * @param height
     */
    void resetHeight(int height);
}

每一個條目所對應的實體類BottomBarInfo

每一個條目都有本身的圖片、文字、文字的顏色,咱們把這些屬性定義在一個實體類中。因爲顏色能夠是整型,也能夠是字符串,這裏定義泛型,泛型就是文字的顏色。具體是哪一種類型的顏色,由調用者來決定。

注意下BarType這個枚舉,咱們的底部導航欄支持兩種類型,IMAGE表明下圖,某個條目只顯示圖片,也可讓某個條目凸出來,只須要將條目的高度變高便可。

public class BottomBarInfo<Color> extends TopBottomBarInfo {

    public enum BarType {
        /**
         * 顯示圖片和文案
         */
        IMAGE_TEXT,
        /**
         * 只顯示圖片
         */
        IMAGE
    }

    /**
     * 條目的名稱
     */
    public String name;
    public BarType tabType;
    public Class<? extends Fraction> fraction;

    public BottomBarInfo(String name, int defaultImage, int selectedImage) {
        this.name = name;
        this.defaultImage = defaultImage;
        this.selectedImage = selectedImage;
        this.tabType = BarType.IMAGE;
    }

    public BottomBarInfo(String name, int defaultImage, int selectedImage, Color defaultColor, Color tintColor) {
        this.name = name;
        this.defaultImage = defaultImage;
        this.selectedImage = selectedImage;
        this.defaultColor = defaultColor;
        this.tintColor = tintColor;
        this.tabType = BarType.IMAGE_TEXT;
    }
}

單個條目的封裝

        定義BottomBar,繼承相對佈局,實現以前定義的IBar接口,泛型就是每一個條目所對應的實體類,因爲目前並不知道泛型的具體類型,因此泛型直接使用問號來代替。BottomBar就是單個條目。

咱們須要將component對象放入到BottomBar中,因此第二個參數傳this,第三個參數爲true。

public class BottomBar extends DependentLayout implements IBar<BottomBarInfo<?>> { /** * 當前條目所對應的數據 */ private BottomBarInfo<Color> tabInfo; private Text mTabName; private Image mTabImage; public BottomBar(Context context) { this(context, null); } public BottomBar(Context context, AttrSet attrSet) { this(context, attrSet, ""); } public BottomBar(Context context, AttrSet attrSet, String styleName) { super(context, attrSet, styleName); Component component = LayoutScatter.getInstance(context).parse(ResourceTable.Layout_layout_bar_bottom, this, true); mTabImage = (Image) component.findComponentById(ResourceTable.Id_image); mTabName = (Text) component.findComponentById(ResourceTable.Id_name); mTabImage.setScaleMode(Image.ScaleMode.INSIDE); } /** * 設置條目的數據 * * @param data */ @Override public void setBarInfo(BottomBarInfo<?> data) {
        tabInfo = (BottomBarInfo<Color>) data;
        inflateInfo(false, true);
    }

    /**
     * 初始化條目
     *
     * @param selected true 選中
     * @param init true 初始化
     */
    private void inflateInfo(boolean selected, boolean init) {
        if (tabInfo.tabType == BottomBarInfo.BarType.IMAGE_TEXT) {
            if (init) {
                // 圖片和名稱均可見
                mTabName.setVisibility(VISIBLE);
                mTabImage.setVisibility(VISIBLE);
                if (!TextUtils.isEmpty(tabInfo.name)) {
                    // 設置條目的名稱
                    mTabName.setText(tabInfo.name);
                }
            }
            if (selected) {
                // 顯示選中的圖片
                mTabImage.setPixelMap(tabInfo.selectedImage);
                mTabName.setTextColor(new Color(parseColor(tabInfo.tintColor)));
            } else {
                // 顯示未選中的圖片
                mTabImage.setPixelMap(tabInfo.defaultImage);
                mTabName.setTextColor(new Color(parseColor(tabInfo.defaultColor)));
            }
        } else if (tabInfo.tabType == BottomBarInfo.BarType.IMAGE) {
            if (init) {
                // 僅僅顯示圖片,將名稱隱藏
                mTabName.setVisibility(HIDE);
                mTabImage.setVisibility(VISIBLE);
            }
            if (selected) {
                // 顯示選中的圖片
                mTabImage.setPixelMap(tabInfo.selectedImage);
            } else {
                // 顯示未選中的圖片
                mTabImage.setPixelMap(tabInfo.defaultImage);
            }
        }
    }

    private int parseColor(Object color) {
        if (color instanceof String) {
            return Color.getIntColor((String) color);
        } else {
            return (int) color;
        }
    }

    /**
     * 動態修改某個tab的高度
     *
     * @param height tab的高度
     */
    @Override
    public void resetHeight(int height) {
        ComponentContainer.LayoutConfig config = getLayoutConfig();
        config.height = height;
        setLayoutConfig(config);
        mTabName.setVisibility(HIDE);
    }

    /**
     * 當某個條目被選中後的回調,該方法會被調用屢次
     *
     * @param index 點擊後選中條目的下標
     * @param preInfo 點擊前選中的條目
     * @param nextInfo 點擊後選中的條目
     */
    @Override
    public void onBarSelectedChange(int index, BottomBarInfo<?> preInfo, BottomBarInfo<?> nextInfo) {
        if (nextInfo.tabType == BottomBarInfo.BarType.IMAGE) {
            // 當前條目的類型是IMAGE類型,則不作任何處理
            return;
        }
        if (preInfo == nextInfo) {
            // 假設當前選中的是條目1,同時點擊的也是條目1,那就不須要作任何操做了
            return;
        }
        if (preInfo != tabInfo && nextInfo != tabInfo) {
            /**
             * 假設有三個條目,條目一、條目二、條目3,preInfo是條目1,nextInfo是條目3,tabInfo是條目2,
             * 點擊前選中的是條目1,點擊後選中的條目3,此時條目2就不須要作任何操做了
             */
            return;
        }
        if (preInfo == tabInfo) {
            // 將點擊前的條目反選
            inflateInfo(false, false);
        } else {
            // 選中被點擊的條目
            inflateInfo(true, false);
        }
    }

    public BottomBarInfo<Color> getTabInfo() {
        return tabInfo;
    }

    public Text getTabName() {
        return mTabName;
    }

    public Image getImage() {
        return mTabImage;
    }
}

底部導航欄的封裝

  定義BottomNavigationBar,繼承棧佈局。第一個泛型就是底部導航欄的條目,第二個泛型就是每一個條目的數據。

public class BottomNavigationBar extends StackLayout implements IBarLayout<BottomBar, BottomBarInfo<?>> {

    private static final int ID_TAB_BOTTOM = 0XFF;
    /**
     * 事件監聽的集合
     */
    private List<OnBarSelectedListener<BottomBarInfo<?>>> tabSelectedListeners = new ArrayList<>();
    /**
     * 當前選中的條目
     */
    private BottomBarInfo<?> selectedInfo; /** * 底部導航欄的透明度 */ private float barBottomAlpha = 1; /** * 底部導航欄的高度 */ private float barBottomHeight = 50; /** * 底部導航欄線條的高度 */ private float barBottomLineHeight = 0.5f; /** * 底部導航欄線條的顏色 */ private RgbColor barBottomLineColor = new RgbColor(223, 224, 225); /** * 全部的tab */ private List<BottomBarInfo<?>> infoList;

    public BottomNavigationBar(Context context) {
        this(context, null);
    }

    public BottomNavigationBar(Context context, AttrSet attrSet) {
        this(context, attrSet, "");
    }

    public BottomNavigationBar(Context context, AttrSet attrSet, String styleName) {
        super(context, attrSet, styleName);
    }

    /**
     * 根據數據查找條目
     *
     * @param info 條目的數據
     * @return 條目
     */
    @Override
    public BottomBar findBar(BottomBarInfo<?> info) { ComponentContainer componentContainer = (ComponentContainer) findComponentById(ID_TAB_BOTTOM); for (int i = 0; i < componentContainer.getChildCount(); i++) { Component component = componentContainer.getComponentAt(i); if (component instanceof BottomBar) { BottomBar bottomBar = (BottomBar) component; if (bottomBar.getTabInfo() == info) { return bottomBar; } } } return null; } /** * 添加監聽 * * @param listener 監聽 */ @Override public void addBarSelectedChangeListener(OnBarSelectedListener<BottomBarInfo<?>> listener) {
        tabSelectedListeners.add(listener);
    }

    /**
     * 默認選中的條目
     *
     * @param defaultInfo 默認選中條目的信息
     */
    @Override
    public void defaultSelected(BottomBarInfo<?> defaultInfo) { onSelected(defaultInfo); } /** * 初始化全部的條目 * * @param infoList 全部條目的信息 */ @Override public void initInfo(List<BottomBarInfo<?>> infoList) {
        if (infoList == null || infoList.isEmpty()) {
            return;
        }
        this.infoList = infoList;
        // 移除以前已經添加的組件,防止重複添加
        removeComponent();
        selectedInfo = null;
        // 添加背景
        addBackground();
        // 添加條目
        addBottomBar();
        // 添加線條
        addBottomLine();
    }

    /**
     * 添加線條
     */
    private void addBottomLine() {
        Component line = new Component(getContext());
        // 目前不支持直接設置背景顏色,只能經過Element來設置背景
        ShapeElement element = new ShapeElement();
        element.setShape(ShapeElement.RECTANGLE);
        element.setRgbColor(barBottomLineColor);
        line.setBackground(element);
        LayoutConfig config = new LayoutConfig(ComponentContainer.LayoutConfig.MATCH_PARENT,
                DisplayUtils.vp2px(getContext(), barBottomLineHeight));
        // 位於底部
        config.alignment = LayoutAlignment.BOTTOM;
        config.setMarginBottom(DisplayUtils.vp2px(getContext(), barBottomHeight - barBottomLineHeight));
        line.setAlpha(barBottomAlpha);
        addComponent(line, config);
    }

    /**
     * 添加條目
     */
    private void addBottomBar() {
        // 每一個條目的寬度就是屏幕寬度除以條目的總個數
        int width = DisplayUtils.getDisplayWidthInPx(getContext()) / infoList.size();
        // 高度是固定的值,這裏須要作屏幕適配,將vp轉換成像素
        int height = DisplayUtils.vp2px(getContext(), barBottomHeight);
        StackLayout stackLayout = new StackLayout(getContext());
        stackLayout.setId(ID_TAB_BOTTOM);
        for (int i = 0; i < infoList.size(); i++) {
            BottomBarInfo<?> info = infoList.get(i); // 建立佈局配置對象 LayoutConfig config = new LayoutConfig(width, height); // 設置底部對齊 config.alignment = LayoutAlignment.BOTTOM; // 設置左邊距 config.setMarginLeft(i * width); BottomBar bottomBar = new BottomBar(getContext()); tabSelectedListeners.add(bottomBar); // 初始化每一個條目 bottomBar.setBarInfo(info); // 添加條目 stackLayout.addComponent(bottomBar, config); // 設置點擊事件 bottomBar.setClickedListener(component -> onSelected(info)); } LayoutConfig layoutConfig = new LayoutConfig(ComponentContainer.LayoutConfig.MATCH_PARENT, ComponentContainer.LayoutConfig.MATCH_CONTENT); layoutConfig.alignment = LayoutAlignment.BOTTOM; addComponent(stackLayout, layoutConfig); } /** * 點擊條目後給外界回調 * * @param nextInfo 點擊後須要選中的條目 */ private void onSelected(BottomBarInfo<?> nextInfo) {
        for (OnBarSelectedListener<BottomBarInfo<?>> listener : tabSelectedListeners) {
            listener.onBarSelectedChange(infoList.indexOf(nextInfo), selectedInfo, nextInfo);
        }
        if (nextInfo.tabType == BottomBarInfo.BarType.IMAGE_TEXT) {
            selectedInfo = nextInfo;
        }
    }

    /**
     * 添加背景
     */
    private void addBackground() {
        Component component = new Component(getContext());
        // 目前還不能直接設置背景顏色,只能經過Element來設置背景
        ShapeElement element = new ShapeElement();
        element.setShape(ShapeElement.RECTANGLE);
        RgbColor rgbColor = new RgbColor(255, 255, 255);
        element.setRgbColor(rgbColor);
        component.setBackground(element);
        component.setAlpha(barBottomAlpha);
        LayoutConfig config = new LayoutConfig(ComponentContainer.LayoutConfig.MATCH_PARENT,
                DisplayUtils.vp2px(getContext(), barBottomHeight));
        config.alignment = LayoutAlignment.BOTTOM;
        addComponent(component, config);
    }

    /**
     * 移除以前已經添加的組件,防止重複添加
     */
    private void removeComponent() {
        for (int i = getChildCount() - 1; i > 0; i--) {
            removeComponentAt(i);
        }
        tabSelectedListeners.removeIf(listener ->
                listener instanceof BottomBar);
    }

    /**
     * 設置底部導航欄的透明度
     *
     * @param barBottomAlpha 底部導航欄的透明度
     */
    public void setBarBottomAlpha(float barBottomAlpha) {
        this.barBottomAlpha = barBottomAlpha;
    }

    /**
     * 設置底部導航欄的高度
     *
     * @param barBottomHeight 底部導航欄的高度
     */
    public void setBarBottomHeight(float barBottomHeight) {
        this.barBottomHeight = barBottomHeight;
    }

    /**
     * 設置底部導航欄線條的高度
     *
     * @param barBottomLineHeight 底部導航欄線條的高度
     */
    public void setBarBottomLineHeight(float barBottomLineHeight) {
        this.barBottomLineHeight = barBottomLineHeight;
    }

    /**
     * 設置底部導航欄線條的顏色
     *
     * @param barBottomLineColor 底部導航欄線條的顏色
     */
    public void setBarBottomLineColor(RgbColor barBottomLineColor) {
        this.barBottomLineColor = barBottomLineColor;
    }
}

initInfo(List<BottomBarInfo<?>> infoList)該方法由外界調用,外界將全部的條目信息傳遞過來,咱們將條目添加到底部導航欄。首先移除以前已經添加的組件,防止重複添加,而後添加背景,添加條目,添加線條。

更多原創,請關注:軟通動力HarmonyOS學院https://harmonyos.51cto.com/column/30

做者:軟通田可輝

想了解更多內容,請訪問51CTO和華爲合做共建的鴻蒙社區:https://harmonyos.51cto.com/

相關文章
相關標籤/搜索