目录: 1、引言 2、功能介绍 3、BottomNavigationBar使用指南 4、BottomNavigationBar开发指南 5、《HarmonyOS三方件开发指南》文章合集 引言 BottomNavigationBar底部导航栏,可以说所有的a…
目录:
1、引言
2、功能介绍
3、BottomNavigationBar使用指南
4、BottomNavigationBar开发指南
5、《HarmonyOS三方件开发指南》文章合集
引言
BottomNavigationBar底部导航栏,可以说所有的app是这样的页面架构,原因很简单,操作简单,模块化清晰,页面切换流畅,而且每页都可以展示不同的风格。相信开发者已经很熟悉Android的底部导航栏的开发以及开发流程,那么接下来将对比Android来讲解鸿蒙的底部导航栏的实现步骤。
功能介绍
鸿蒙BottomNavigationBar底部导航栏,根据所需要底部button的数量,动态生成对应的底部button,并且可以设置默认字体颜色,选中字体颜色,默认icon,选中icon属性。模拟器效果图如下:

看了效果图,是不是都想知道在实际工作中,是如何使用的呢?接下来给大家详细介绍下BottomNavigationBar如何使用。
BottomNavigationBar使用指南
Ø 新建工程, 添加组件Har包依赖
在应用模块中添加HAR,只需要将mylibrarybottom-debug.har复制到entry\libs目录下即可。
Ø 修改相关文件
- 修改主页面的布局文件ability_main.xml:

- 修改MainAbilitySlice代码:

- 修改BaseAbilitySlinct代码:

- MainAbility的代码:

配置好1-4步,接下来就看如何给对应的底部导航栏添加Fraction - initBottom 方法如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| private void initBottom() { tabBottomLayout = (BottomNavigationBar) mAbilitySliceProvider.findComponentById(ResourceTable.Id_bottom_navigation_bar); bottomInfoList = new ArrayList<>(); 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) -> mFractionBarComponent.setCurrentItem(index)); tabBottomLayout.defaultSelected(homeInfo);
|
- 创建fraction类,继承BaseFraction
- 引入需要展示页面的布局文件
1 2 3 4
| @Override public int getUIComponent() { return ResourceTable.Layout_layout_fraction_home; }
|
- 操作布局文件中的控件
1 2 3 4
| @Override public void initComponent(Component component) { text = (Text) component.findComponentById(ResourceTable.Id_text); }
|
BottomNavigationBar开发指南
底部导航栏,在应用中真的非常常见,核心思想就是底部有几个选项,然后点击其中任意一个,切换至对应的页面。接下来主要介绍下核心实现步骤。
主要封装的原则是,动态的,通过外界传递,固定过的则封装起来。其中底部导航栏的图片、文字、文字的颜色是变的,其它的可以封装起来,外界只需要把每个条目的图片、文字以及文字的颜色传入进来即可,内部来实现底部导航栏。在封装的时候,需要面向接口编程,同时使用泛型。
定义接口IBarLayout
1、定义一个IBarLayout接口,第一个泛型就是底部导航栏中的每个条目,第二个泛型是每个条目的数据。在接口里面提供一些方法,可以根据数据查找条目,可以添加监听,可以设置默认选中的条目,可以初始化所有的条目,当某个条目被选中后需要通过回调方法。
代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| public interface IBarLayout<Bar extends ComponentContainer, D> {
Bar findBar(D info);
void addBarSelectedChangeListener(OnBarSelectedListener<D> listener);
void defaultSelected(D defaultInfo);
void initInfo(List<D> infoList);
interface OnBarSelectedListener<D> {
void onBarSelectedChange(int index, D preInfo, D nextInfo); } }
|
2、再定义一个单个条目的接口IBar,泛型就是每个条目的数据,接口里面定义方法,可以设置条目的数据,可以动态修改某个条目的大小
代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
|
public interface IBar<D> extends IBarLayout.OnBarSelectedListener<D> {
void setBarInfo(D data);
void resetHeight(int height); }
|
每个条目所对应的实体类BottomBarInfo
每个条目都有自己的图片、文字、文字的颜色,我们把这些属性定义在一个实体类中。由于颜色可以是整型,也可以是字符串,这里定义泛型,泛型就是文字的颜色。具体是哪种类型的颜色,由调用者来决定。
注意下BarType这个枚举,我们的底部导航栏支持两种类型,IMAGE代表下图,某个条目只显示图片,也可以让某个条目凸出来,只需要将条目的高度变高即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| 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。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
| 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); }
@Override public void setBarInfo(BottomBarInfo<?> data) { tabInfo = (BottomBarInfo<Color>) data; inflateInfo(false, 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; } }
@Override public void resetHeight(int height) { ComponentContainer.LayoutConfig config = getLayoutConfig(); config.height = height; setLayoutConfig(config); mTabName.setVisibility(HIDE); }
@Override public void onBarSelectedChange(int index, BottomBarInfo<?> preInfo, BottomBarInfo<?> nextInfo) { if (nextInfo.tabType == BottomBarInfo.BarType.IMAGE) { return; } if (preInfo == nextInfo) { return; } if (preInfo != tabInfo && nextInfo != tabInfo) {
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,继承栈布局。第一个泛型就是底部导航栏的条目,第二个泛型就是每个条目的数据。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238
| 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);
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); }
@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; }
@Override public void addBarSelectedChangeListener(OnBarSelectedListener<BottomBarInfo<?>> listener) { tabSelectedListeners.add(listener); }
@Override public void defaultSelected(BottomBarInfo<?> defaultInfo) { onSelected(defaultInfo); }
@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()); 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(); 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); }
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()); 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); }
public void setBarBottomAlpha(float barBottomAlpha) { this.barBottomAlpha = barBottomAlpha; }
public void setBarBottomHeight(float barBottomHeight) { this.barBottomHeight = barBottomHeight; }
public void setBarBottomLineHeight(float barBottomLineHeight) { this.barBottomLineHeight = barBottomLineHeight; }
public void setBarBottomLineColor(RgbColor barBottomLineColor) { this.barBottomLineColor = barBottomLineColor; } }
|
initInfo(List<BottomBarInfo<?>> infoList)该方法由外界调用,外界将所有的条目信息传递过来,我们将条目添加到底部导航栏。首先移除之前已经添加的组件,防止重复添加,然后添加背景,添加条目,添加线条。
更多原创,请关注:软通动力HarmonyOS学院https://harmonyos.51cto.com/column/30
作者:软通田可辉
想了解更多内容,请访问51CTO和华为合作共建的鸿蒙社区:https://harmonyos.51cto.com/
本文标题: HarmonyOS三方件开发指南(17)-BottomNavigationBar
本文作者: OSChina
发布时间: 2021年04月15日 09:53
最后更新: 2025年04月03日 11:07
原始链接: https://haoxiang.eu.org/7c73ebd9/
版权声明: 本文著作权归作者所有,均采用CC BY-NC-SA 4.0许可协议,转载请注明出处!