Advertisement

鸿蒙系统导航栏,鸿蒙底部导航栏 vs 安卓底部导航栏

阅读量:

BottomNavigationBar 底部导航栏,可以说所有的 App 是这样的页面架构,原因很简单,操作简单,模块化清晰,页面切换流畅,而且每页都可以展示不同的风格。220569676_1_20210422063816472220569676_2_20210422063816488

相信开发者已经很熟悉 Android 的底部导航栏的开发以及开发流程,那么接下来将对比 Android 来讲解鸿蒙的底部导航栏的实现步骤。01功能介绍

鸿蒙 BottomNavigationBar 底部导航栏,根据所需要底部 button 的数量,动态生成对应的底部 button,并且可以设置默认字体颜色,选中字体颜色,默认 icon,选中 icon 属性。

模拟器效果图如下:220569676_3_20210422063816816

看了效果图,是不是都想知道在实际工作中,是如何使用的呢?接下来给大家详细介绍下 BottomNavigationBar 如何使用。02BottomNavigationBar 使用指南

①新建工程, 添加组件 Har 包依赖

在应用模块中添加 HAR,只需要将 mylibrarybottom-debug.har 复制到 entry\libs 目录下即可。

②修改相关文件修改主页面的布局文件 ability_main.xml:
220569676_4_20210422063816988修改 MainAbilitySlice 代码:
220569676_5_20210422063817129

修改 BaseAbilitySlinct 代码:
220569676_6_20210422063817269

MainAbility 的代码:220569676_7_20210422063817456

配置好 1-4 步,接下来就看如何给对应的底部导航栏添加 Fraction。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 homeInfo = new BottomBarInfo<>(home,

ResourceTable.Media_category_norma1,

ResourceTable.Media_category_norma2,

defaultColor, tintColor);

homeInfo.fraction = HomeFraction.class;

// 收藏

BottomBarInfo favoriteInfo = new BottomBarInfo<>(favorite,

ResourceTable.Media_category_norma1,

ResourceTable.Media_category_norma2,

defaultColor, tintColor);

favoriteInfo.fraction = SecondFraction.class;

// 分类

BottomBarInfo categoryInfo = new BottomBarInfo<>(category,

ResourceTable.Media_category_norma1,

ResourceTable.Media_category_norma2,

defaultColor, tintColor);

categoryInfo.fraction = ThirdFraction.class;

// 我的

BottomBarInfo 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);创建 fraction 类,继承 BaseFraction。引入需要展示页面的布局文件:@Override

public int getUIComponent(){

return ResourceTable.Layout_layout_fraction_home;

}

操作布局文件中的控件:@Override

public void initComponent(Component component){

text = (Text) component.findComponentById(ResourceTable.Id_text);

}03BottomNavigationBar 开发指南底部导航栏,在应用中真的非常常见,核心思想就是底部有几个选项,然后点击其中任意一个,切换至对应的页面。接下来主要介绍下核心实现步骤。主要封装的原则是,动态的,通过外界传递,固定过的则封装起来。其中底部导航栏的图片、文字、文字的颜色是变的,其它的可以封装起来,外界只需要把每个条目的图片、文字以及文字的颜色传入进来即可,内部来实现底部导航栏。在封装的时候,需要面向接口编程,同时使用泛型。①定义接口 IBarLayout定义一个 IBarLayout 接口,第一个泛型就是底部导航栏中的每个条目,第二个泛型是每个条目的数据。在接口里面提供一些方法,可以根据数据查找条目,可以添加监听,可以设置默认选中的条目,可以初始化所有的条目,当某个条目被选中后需要通过回调方法。

代码如下:public interface IBarLayout{

/**

  • 根据数据查找条目

  • @param info 数据

  • @return 条目

*/

Bar findBar(D info);

/**

  • 添加监听

  • @param listener

*/

void addBarSelectedChangeListener(OnBarSelectedListener listener);

/**

  • 默认选中的条目

  • @param defaultInfo

*/

void defaultSelected(D defaultInfo);

/**

  • 初始化所有的条目

  • @param infoList

*/

void initInfo(List infoList);

interface OnBarSelectedListener{

/**

  • 当某个条目被选中后的回调,该方法会被调用多次

  • @param index 点击后选中条目的下标

  • @param preInfo 点击前选中的条目

  • @param nextInfo 点击后选中的条目

*/

void onBarSelectedChange(int index, D preInfo, D nextInfo);

}

}再定义一个单个条目的接口 IBar,泛型就是每个条目的数据,接口里面定义方法,可以设置条目的数据,可以动态修改某个条目的大小。

代码如下:/**

  • 单个条目的接口

*/

public interface IBar extends IBarLayout.OnBarSelectedListener{

/**

  • 设置条目的数据

  • @param data

*/

void setBarInfo(D data);

/**

  • 动态修改某个条目的大小

  • @param height

*/

void resetHeight(int height);

}②每个条目所对应的实体类 BottomBarInfo每个条目都有自己的图片、文字、文字的颜色,我们把这些属性定义在一个实体类中。由于颜色可以是整型,也可以是字符串,这里定义泛型,泛型就是文字的颜色。具体是哪种类型的颜色,由调用者来决定。

注意下 BarType 这个枚举,我们的底部导航栏支持两种类型,IMAGE 代表下图,某个条目只显示图片,也可以让某个条目凸出来,只需要将条目的高度变高即可。public class BottomBarInfo 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>{

/**

  • 当前条目所对应的数据

*/

private BottomBarInfo 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) 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) {

/**

  • 假设有三个条目,条目1、条目2、条目3,preInfo是条目1,nextInfo是条目3,tabInfo是条目2,

  • 点击前选中的是条目1,点击后选中的条目3,此时条目2就不需要做任何操作了

*/

return;

}

if (preInfo == tabInfo) {

// 将点击前的条目反选

inflateInfo(false, false);

} else {

// 选中被点击的条目

inflateInfo(true, false);

}

}

public BottomBarInfo getTabInfo(){

return tabInfo;

}

public Text getTabName(){

return mTabName;

}

public Image getImage(){

return mTabImage;

}

}④底部导航栏的封装

定义 BottomNavigationBar,继承栈布局。第一个泛型就是底部导航栏的条目,第二个泛型就是每个条目的数据。public class BottomNavigationBar extends StackLayout implements IBarLayout>{

private static final int ID_TAB_BOTTOM = 0XFF;

/**

  • 事件监听的集合

*/

private List>> 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> 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

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> listener){

tabSelectedListeners.add(listener);

}

/**

  • 默认选中的条目

  • @param defaultInfo 默认选中条目的信息

*/

@Override

public void defaultSelected(BottomBarInfo> defaultInfo){

onSelected(defaultInfo);

}

/**

  • 初始化所有的条目

  • @param infoList 所有条目的信息

*/

@Override

public void initInfo(List> 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

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> 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>

infoList)该方法由外界调用,外界将所有的条目信息传递过来,我们将条目添加到底部导航栏。首先移除之前已经添加的组件,防止重复添加,然后添加背景,添加条目,添加线条。

全部评论 (0)

还没有任何评论哟~