#夏日挑战赛# HarmonyOS应用开发-实现底部导航栏功能 原创 精华

发布于 2022-7-26 20:38
浏览
3收藏

[本文正在参加星光计划3.0–夏日挑战赛] https://ost.51cto.com/posts/13641

前言

HarmonyOS应用开发最常见的一种功能就是底部导航栏,今天就带着大家一起来感受不一样的底部导航栏的实现-自定义Component的方式。

自定义View

创建布局

首先创建一个item_route.xml的布局,代码如下:

<?xml version="1.0" encoding="utf-8"?>
<DirectionalLayout
    xmlns:ohos="http://schemas.huawei.com/res/ohos"
    ohos:height="match_parent"
    ohos:width="match_content"
    ohos:alignment="center"
    ohos:orientation="vertical">

    <Image
        ohos:id="$+id:img"
        ohos:height="25vp"
        ohos:width="25vp"
        ohos:bottom_margin="1vp"
        ohos:scale_mode="inside"/>

    <Text
        ohos:id="$+id:text"
        ohos:height="match_content"
        ohos:width="match_content"
        ohos:text_color="$color:hintColor"
        ohos:text_size="10vp"/>

</DirectionalLayout>

创建自定义Component的类RouteView.java

public class RouteView extends DirectionalLayout {
    private static final int ITEM_LAYOUT = ResourceTable.Layout_item_route;

    private List<Data> data;
    private Callback callback;

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

    public RouteView(Context context, AttrSet attrSet) {
        this(context, attrSet, null);
    }

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

    private void init() {
        setOrientation(HORIZONTAL);   //设置水平方向
    }

    public void setCallback(Callback callback) {
        this.callback = callback;
    }

    public void setData(DataBuilder builder) {
        setData(builder, 0);
    }

    public void setData(DataBuilder builder, int p) {
        data = builder.build();
        createItems();
        select(p);
    }

    public void select(int p) {
        for (int i = 0; i < data.size(); i++) {
            data.get(i).selected = i == p;
            refreshItemData(i);
        }
    }

    private void createItems() {
        data.forEach(d -> addComponent(createItem(),
                new LayoutConfig(LayoutConfig.MATCH_PARENT, LayoutConfig.MATCH_PARENT, HORIZONTAL, 1)));
    }

    private Component createItem() {
        Component component = LayoutScatter.getInstance(getContext())
                .parse(ITEM_LAYOUT, null, false);
        ViewHolder holder = new ViewHolder();
        holder.img = (Image) component.findComponentById(ResourceTable.Id_img);
        holder.text = (Text) component.findComponentById(ResourceTable.Id_text);


        component.setTag(holder);
        component.setClickedListener(v -> {
            int p = getChildIndex(v);
            if (p < 0) return;

            select(p);
            if (callback != null) callback.onItemClicked(p);
        });
        return component;
    }

    private void refreshItemData(int p) {
        Component component = getComponentAt(p);
        Object tag = component.getTag();
        if (!(tag instanceof ViewHolder)) return;

        ViewHolder holder = (ViewHolder) tag;
        holder.text.setText(data.get(p).text);

        holder.img.setPixelMap(data.get(p).getImageRes());
        holder.text.setTextColor(ResUtil.getColorObj(getContext(), data.get(p).getColorRes()));
    }

    public interface Callback {
        void onItemClicked(int p);
    }

    static class ViewHolder {
        Text text;
        Image img;
    }

    static class Data {
        String text;
        int imageRes, selectedImageRes;
        int colorRes, selectedColorRes;
        boolean selected;

        public Data(String text, int imageRes, int selectedImageRes, int colorRes, int selectedColorRes) {
            this.text = text;
            this.imageRes = imageRes;
            this.selectedImageRes = selectedImageRes;
            this.colorRes = colorRes;
            this.selectedColorRes = selectedColorRes;
        }

        public int getImageRes() {
            return selected ? selectedImageRes : imageRes;
        }

        public int getColorRes() {
            return selected ? selectedColorRes : colorRes;
        }
    }

    public static class DataBuilder {
        private final List<Data> data = new ArrayList<>();

        public DataBuilder add(String text, int imageRes, int selectedImageRes, int colorRes, int selectedColorRes) {
            data.add(new Data(text, imageRes, selectedImageRes, colorRes, selectedColorRes));
            return this;
        }

        public List<Data> build() {
            return data;
        }
    }
}
  • 在项目的build.gradle中添加依赖,如下:
        api 'com.gitee.chinasoft_ohos:ohos-extension:1.0.0' 
    
  • 这里我们定义了一个接口Callback,用于点击选项卡的回调:代码如下:
    public interface Callback {
          void onItemClicked(int p);
      }
    
  • 定义了一个集合List<Data> 用于存放item资源,Data数据结构如下:
   static class Data {
       String text;
       int imageRes, selectedImageRes;
       int colorRes, selectedColorRes;
       boolean selected;
       /*
       *  text:显示文本
       *  imageRes:未选中的图片资源文件
       *  selectedImageRes:选中的资源文件
       *  colorRes:  未选中的文本颜色
       *  selectedColorRes: 选中时文本的颜色
       * */
       public Data(String text, int imageRes, int selectedImageRes, int colorRes, int selectedColorRes) {
           this.text = text;
           this.imageRes = imageRes;
           this.selectedImageRes = selectedImageRes;
           this.colorRes = colorRes;
           this.selectedColorRes = selectedColorRes;
       }
       //获取图片资源
       public int getImageRes() {
           return selected ? selectedImageRes : imageRes;
       }
       //获取文本颜色
       public int getColorRes() {
           return selected ? selectedColorRes : colorRes;
       }
   }
  • 另外,我们通过builder建造者模式,可以动态的添加和删除item,代码如下:
     public static class DataBuilder {
         private final List<Data> data = new ArrayList<>();
         public DataBuilder add(String text, int imageRes, int selectedImageRes, int colorRes, int selectedColorRes) {
             data.add(new Data(text, imageRes, selectedImageRes, colorRes, selectedColorRes));
             return this;
         }
         public List<Data> build() {
             return data;
         }
     }
    

应用开发整体框架

1、首先在MainAbility类中继承FractionAbility.
public class MainAbility extends FractionAbility{
  @Override
  public void onStart(Intent intent) {
      super.onStart(intent);
      super.setMainRoute(MainAbilitySlice.class.getName());
  }
}

2、创建两个Fraction的布局文件fr_home.xml、fr_me.xml。
fr_home.xml:

<?xml version="1.0" encoding="utf-8"?>
<DirectionalLayout
    xmlns:ohos="http://schemas.huawei.com/res/ohos"
    ohos:height="match_parent"
    ohos:width="match_parent"
    ohos:background_element="$color:bgColor"
    ohos:alignment="center"
    ohos:orientation="vertical">
    <Text
        ohos:height="match_content"
        ohos:width="match_content"
        ohos:text="首页页面"/>

</DirectionalLayout>

fr_me.xml:

<?xml version="1.0" encoding="utf-8"?>
<DirectionalLayout
   xmlns:ohos="http://schemas.huawei.com/res/ohos"
   ohos:height="match_parent"
   ohos:width="match_parent"
   ohos:background_element="$color:bgColor"
   ohos:alignment="center"
   ohos:orientation="vertical">
   <Text
       ohos:height="match_content"
       ohos:width="match_content"
       ohos:text="我的页面"/>

</DirectionalLayout>

3、创建HomeFraction、MeFraction类:
HomeFraction:

  public class HomeFraction extends Fraction {
   @Override
   protected void onStart(Intent intent) {
       super.onStart(intent);

   }
   @Override
   protected Component onComponentAttached(LayoutScatter scatter, ComponentContainer container, Intent intent) {
       return scatter.parse(ResourceTable.Layout_fr_home, null, false);
   }
}

MeFraction:

public class MeFraction extends Fraction {
   @Override
   protected void onStart(Intent intent) {
       super.onStart(intent);
   }
   @Override
   protected Component onComponentAttached(LayoutScatter scatter, ComponentContainer container, Intent intent) {
       return scatter.parse(ResourceTable.Layout_fr_me, null, false);
   }
}

4、在MainAbilitySlice类中初始化Fraction,实现RouteView.Callback回调。

   private void initFraction() {
       fractions = new ArrayList<>();
       homeFraction = new HomeFraction(this);
       fractions.add(homeFraction);
       fractions.add(new MeFraction(this));
       showFraction(0);
   }
   private void initView() {
       routeView = (RouteView) findComponentById(ResourceTable.Id_route);
       routeView.setCallback(this);
       RouteView.DataBuilder builder = new RouteView.DataBuilder()
               .add("首页", ResourceTable.Media_home, ResourceTable.Media_home_s                                                            ,
                       ResourceTable.Color_hintColor, ResourceTable.Color_tabTextColor)
               .add("我的", ResourceTable.Media_me, ResourceTable.Media_me_s,
                       ResourceTable.Color_hintColor, ResourceTable.Color_tabTextColor);
       routeView.setData(builder);
   }
    ......
   private void showFraction(int p) {
       scheduler = ((FractionAbility) getAbility()).getFractionManager().startFractionScheduler();

       for (int i = 0; i < fractions.size(); i++) {
           Fraction f = fractions.get(i);
           if (i == p) scheduler.replace(ResourceTable.Id_container, f);
           else scheduler.remove(f);
       }
       scheduler.submit();
   }

    ......
  @Override
   public void onItemClicked(int p) {
       showFraction(p);
   }

效果图如下所示:

#夏日挑战赛#  HarmonyOS应用开发-实现底部导航栏功能-开源基础软件社区

总结

本篇的难点在于使用自定义Component的方式实现了底部Tab导航的功能,可以使代码方便复用。

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
HarmonyOS应用开发框架.rar 1.25M 15次下载
已于2022-7-27 09:19:40修改
10
收藏 3
回复
举报
回复
添加资源
添加资源将有机会获得更多曝光,你也可以直接关联已上传资源 去关联
    相关推荐