目錄:編程
一、引言json
二、功能介紹架構
引言佈局
BottomNavigationBar底部導航欄,能夠說全部的app是這樣的頁面架構,緣由很簡單,操做簡單,模塊化清晰,頁面切換流暢,並且每頁均可以展現不一樣的風格。相信開發者已經很熟悉Android的底部導航欄的開發以及開發流程,那麼接下來將對比Android來說解鴻蒙的底部導航欄的實現步驟。post
功能介紹字體
鴻蒙BottomNavigationBar底部導航欄,根據所須要底部button的數量,動態生成對應的底部button,而且能夠設置默認字體顏色,選中字體顏色,默認icon,選中icon屬性。模擬器效果圖以下:this
看了效果圖,是否是都想知道在實際工做中,是如何使用的呢?接下來給你們詳細介紹下BottomNavigationBar如何使用。
BottomNavigationBar使用指南
Ø 新建工程, 添加組件Har包依賴
在應用模塊中添加HAR,只須要將mylibrarybottom-debug.har複製到entry\libs目錄下便可。
Ø 修改相關文件
1. 修改主頁面的佈局文件ability_main.xml:
2. 修改MainAbilitySlice代碼:
3. 修改BaseAbilitySlinct代碼:
4. MainAbility的代碼:
配置好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/