鸿蒙OS 添加智能穿戴模块

2020-09-18 17:59 更新

以下根据实际的开发样例来展示如何在已有的 HarmonyOS 工程中添加一个智能穿戴模块。

如图所示,这是一个睡眠检测应用,应用分为主界面和详情界面,可以选择使用 PageSlider 实现界面间的切换。PageSlider 是一个布局管理器,用于实现左右滑动以及上下滑动的翻页效果。

图1 开发样例效果图 点击放大

  1. 在 DevEco Studio 上方的导航栏中,依次点击“File > New > Module”,在“Device”中选择“Wearable”,添加一个新模块。

  1. 在左侧的“Project”窗口,打开“entry > src > main > resources > base”,右键点击“base”文件夹,选择“New > Directory”,命名为“layout”。

  1. 右键点击“layout”文件夹,选择“New > File”,新建两个UI布局文件,分别命名为“layout_sleep.xml”和“layout_detail.xml”。

主界面的UI布局文件是“layout_sleep.xml”,其完整示例代码如下:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <DirectionalLayout xmlns:ohos="http://schemas.huawei.com/res/ohos"
  3. ohos:width="match_parent"
  4. ohos:height="match_parent"
  5. ohos:background_element="#FF000000"
  6. ohos:orientation="vertical">
  7. <Image
  8. ohos:id="$+id:sleep_moon_img"
  9. ohos:width="46vp"
  10. ohos:height="46vp"
  11. ohos:top_margin="11vp"
  12. ohos:layout_alignment="horizontal_center"/>
  13. <Text
  14. ohos:width="match_parent"
  15. ohos:height="19.5vp"
  16. ohos:alpha="0.66"
  17. ohos:layout_alignment="horizontal_center"
  18. ohos:text_alignment="center"
  19. ohos:text="$string:sleep"
  20. ohos:text_color="$color:sleep_text_white"
  21. ohos:text_size="16vp"/>
  22. <DirectionalLayout xmlns:ohos="http://schemas.huawei.com/res/ohos"
  23. ohos:width="match_content"
  24. ohos:height="65vp"
  25. ohos:top_margin="8vp"
  26. ohos:layout_alignment="horizontal_center"
  27. ohos:orientation="horizontal">
  28. <Text
  29. ohos:id="$+id:sleep_hour_text"
  30. ohos:width="match_content"
  31. ohos:height="match_content"
  32. ohos:layout_alignment="center"
  33. ohos:text_alignment="center"
  34. ohos:text="$string:dash"
  35. ohos:text_color="$color:sleep_text_white"
  36. ohos:text_size="58vp"
  37. />
  38. <Text
  39. ohos:width="match_content"
  40. ohos:height="match_content"
  41. ohos:left_margin="2vp"
  42. ohos:alpha="0.66"
  43. ohos:layout_alignment="bottom"
  44. ohos:bottom_padding="9.5vp"
  45. ohos:text="$string:hour"
  46. ohos:text_color="$color:sleep_text_white"
  47. ohos:text_size="16vp"
  48. />
  49. <Text
  50. ohos:id="$+id:sleep_min_text"
  51. ohos:width="match_content"
  52. ohos:height="match_content"
  53. ohos:left_margin="2vp"
  54. ohos:layout_alignment="center"
  55. ohos:text_alignment="center"
  56. ohos:text="$string:double_dash"
  57. ohos:text_color="$color:sleep_text_white"
  58. ohos:text_size="58vp"
  59. />
  60. <Text
  61. ohos:width="match_content"
  62. ohos:height="match_content"
  63. ohos:left_margin="2vp"
  64. ohos:alpha="0.66"
  65. ohos:layout_alignment="bottom"
  66. ohos:bottom_padding="9.5vp"
  67. ohos:text="$string:minute"
  68. ohos:text_color="$color:sleep_text_white"
  69. ohos:text_size="16vp"
  70. />
  71. </DirectionalLayout>
  72. <DirectionalLayout xmlns:ohos="http://schemas.huawei.com/res/ohos"
  73. ohos:width="match_content"
  74. ohos:height="25vp"
  75. ohos:top_margin="20.5vp"
  76. ohos:layout_alignment="horizontal_center"
  77. ohos:orientation="horizontal">
  78. <Text
  79. ohos:width="match_content"
  80. ohos:height="19.5vp"
  81. ohos:text="$string:goal"
  82. ohos:alpha="0.66"
  83. ohos:text_alignment="bottom"
  84. ohos:bottom_margin="1vp"
  85. ohos:text_color="$color:sleep_text_white"
  86. ohos:text_size="16vp"
  87. />
  88. <Text
  89. ohos:id="$+id:sleep_goal_text"
  90. ohos:width="match_content"
  91. ohos:height="match_parent"
  92. ohos:left_margin="2vp"
  93. ohos:text="$string:target_sleep_time"
  94. ohos:text_weight="600"
  95. ohos:text_color="$color:sleep_text_white"
  96. ohos:bottom_padding="2vp"
  97. ohos:text_size="21vp"
  98. />
  99. <Text
  100. ohos:width="match_content"
  101. ohos:height="19.5vp"
  102. ohos:left_margin="2vp"
  103. ohos:alpha="0.66"
  104. ohos:text="$string:hour"
  105. ohos:text_color="$color:sleep_text_white"
  106. ohos:text_size="16vp"
  107. />
  108. </DirectionalLayout>
  109. </DirectionalLayout>

详情界面的 UI 布局文件是“layout_detail.xml”,其完整示例代码如下:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <DirectionalLayout xmlns:ohos="http://schemas.huawei.com/res/ohos"
  3. ohos:width="match_parent"
  4. ohos:height="match_parent"
  5. ohos:orientation="vertical"
  6. ohos:background_element="#FF000000">
  7. <Text
  8. ohos:id="$+id:detail_nodata_date"
  9. ohos:width="match_content"
  10. ohos:height="23vp"
  11. ohos:top_margin="20vp"
  12. ohos:layout_alignment="horizontal_center"
  13. ohos:text_alignment="bottom"
  14. ohos:text_color="$color:sleep_text_white"
  15. ohos:text_weight="600"
  16. ohos:text_size="19vp"/>
  17. <Image
  18. ohos:id="$+id:detail_nodata_img"
  19. ohos:width="46vp"
  20. ohos:height="46vp"
  21. ohos:top_margin="25vp"
  22. ohos:layout_alignment="horizontal_center"
  23. ohos:scale_type="scale_to_center"/>
  24. <Text
  25. ohos:width="match_content"
  26. ohos:height="match_content"
  27. ohos:alpha="0.66"
  28. ohos:top_margin="12vp"
  29. ohos:layout_alignment="horizontal_center"
  30. ohos:text_alignment="center"
  31. ohos:text="$string:no_data"
  32. ohos:text_color="$color:sleep_text_white"
  33. ohos:text_size="16vp"/>
  34. <Text
  35. ohos:width="match_content"
  36. ohos:height="match_content"
  37. ohos:alpha="0.66"
  38. ohos:layout_alignment="horizontal_center"
  39. ohos:text_alignment="center"
  40. ohos:text="$string:wearing_watch_tips"
  41. ohos:text_color="$color:sleep_text_white"
  42. ohos:text_size="16vp"/>
  43. </DirectionalLayout>

  1. 在左侧项目文件栏中,选择“entry > src > main > java > 应用包名 > slice”,在对应的 AbilitySlice 文件的 onStart 里,使用代码创建 PageSlider,添加这两个相应的界面。

  1. public class SleepPageSlice extends AbilitySlice {
  2. private static final String TAG = "SleepPageSlice";
  3. private static final HiLogLabel LABEL = new HiLogLabel(HiLog.LOG_APP, 0, TAG);
  4. private List<ComponentOwner> list = new ArrayList<>();
  5. private PageSliderProvider provider = new PageSliderProvider() {
  6. @Override
  7. public int getCount() {
  8. return list.size();
  9. }
  10. @Override
  11. public Object createPageInContainer(ComponentContainer componentContainer, int index) {
  12. if (index >= list.size() || componentContainer == null) {
  13. HiLog.error(LABEL, "instantiateItem index error");
  14. return Optional.empty();
  15. }
  16. ComponentOwner container = list.get(index);
  17. componentContainer.addComponent(container.getComponent());
  18. container.instantiateComponent();
  19. return container.getComponent();
  20. }
  21. @Override
  22. public void destroyPageFromContainer(ComponentContainer componentContainer, int index, Object object) {
  23. HiLog.info(LABEL, "destroyItem index:" + index);
  24. if (index >= list.size() || componentContainer == null) {
  25. return;
  26. }
  27. Component content = list.get(index).getComponent();
  28. componentContainer.removeComponent(content);
  29. return;
  30. }
  31. @Override
  32. public boolean isPageMatchToObject(Component component, Object object) {
  33. return component == object;
  34. }
  35. @Override
  36. public void startUpdate(ComponentContainer container) {
  37. super.startUpdate(container);
  38. HiLog.info(LABEL, "startUpdate");
  39. }
  40. };
  41. @Override
  42. public void onStart(Intent intent) {
  43. super.onStart(intent);
  44. HiLog.info(LABEL, "onStart");
  45. // 添加子页面
  46. list.add(new SleepComponentOwner(this));
  47. list.add(new DetailComponentOwner(this));
  48. // 设置主界面
  49. DirectionalLayout layout = new DirectionalLayout(this);
  50. ComponentContainer.LayoutConfig config = new ComponentContainer.LayoutConfig(
  51. ComponentContainer.LayoutConfig.MATCH_PARENT,
  52. ComponentContainer.LayoutConfig.MATCH_PARENT);
  53. layout.setLayoutConfig(config);
  54. // 使用PageSlider做滑动效果
  55. PageSlider slider = new PageSlider(this);
  56. ComponentContainer.LayoutConfig sliderConfig = new ComponentContainer.LayoutConfig(
  57. ComponentContainer.LayoutConfig.MATCH_PARENT,
  58. ComponentContainer.LayoutConfig.MATCH_PARENT);
  59. slider.setLayoutConfig(sliderConfig);
  60. slider.setOrientation(DirectionalLayout.VERTICAL);
  61. slider.setProvider(provider);
  62. layout.addComponent(slider);
  63. setUIContent(layout);
  64. }
  65. }

  1. 增加 ComponentOwner 容器接口和两个页面的实现方式,如下是容器的接口 ComponentOwner.java。

  1. public interface ComponentOwner {
  2. // 获取存放的component
  3. Component getComponent();
  4. // 当包含的component被添加到容器时回调
  5. void instantiateComponent();
  6. }

  1. 增加第一个页面 SleepComponentOwner 和第二个页面 DetailComponentOwner,这两个页面从 xml 里加载。以下是首页 SleepComponentOwner 的实现。

  1. public class SleepComponentOwner implements ComponentOwner {
  2. private static final String TAG = "SleepViewContainer";
  3. private static final HiLogLabel LABEL = new HiLogLabel(HiLog.LOG_APP, 0, TAG);
  4. // 目标睡眠时长默认值,单位:分钟
  5. private static final int DEFAULT_SLEEP_TARGET_TIME = 480;
  6. // 睡眠时长默认值,单位:分钟
  7. private static final int DEFAULT_SLEEP_TIME = 0;
  8. private CircleProgressDrawTask drawTask;
  9. private AbilityContext myContext;
  10. private Component root;
  11. public SleepComponentOwner(AbilityContext context) {
  12. init(context);
  13. }
  14. private void init(AbilityContext context) {
  15. myContext = context;
  16. LayoutScatter scatter = LayoutScatter.getInstance(context);
  17. root = scatter.parse(ResourceTable.Layout_layout_sleep, null, false);
  18. drawTask = new CircleProgressDrawTask(root);
  19. drawTask.setMaxValue(DEFAULT_SLEEP_TARGET_TIME);
  20. Component imageView = root.findComponentById(ResourceTable.Id_sleep_moon_img);
  21. imageView.setBackground(new VectorElement(context, ResourceTable.Graphic_ic_icon_moon));
  22. }
  23. @Override
  24. public Component getComponent() {
  25. return root;
  26. }
  27. @Override
  28. public void instantiateComponent() {
  29. return;
  30. }
  31. }

以下是第二个页面 DetailComponentOwner 的实现。

  1. public class DetailComponentOwner implements ComponentOwner {
  2. private static final String TAG = "DetailViewContainer";
  3. private static final HiLogLabel LABEL = new HiLogLabel(HiLog.LOG_APP, 0, TAG);
  4. private AbilityContext myContext;
  5. private ComponentContainer root;
  6. public DetailComponentOwner(AbilityContext context) {
  7. init(context);
  8. }
  9. private void init(AbilityContext context) {
  10. root = new DirectionalLayout(context);
  11. ComponentContainer.LayoutConfig config = new ComponentContainer.LayoutConfig(
  12. ComponentContainer.LayoutConfig.MATCH_PARENT,
  13. ComponentContainer.LayoutConfig.MATCH_PARENT);
  14. root.setLayoutConfig(config);
  15. myContext = context;
  16. }
  17. @Override
  18. public Component getComponent() {
  19. return root;
  20. }
  21. @Override
  22. public void instantiateComponent() {
  23. return;
  24. }
  25. }

  1. 增加一个类型为Page的Ability,并在config.json里进行注册。需要在onStart里调用setSwipeToDismiss(true),来设置右滑退出。示例代码如下:

  1. public class PageAbility extends Ability {
  2. @Override
  3. public void onStart(Intent intent) {
  4. super.onStart(intent);
  5. super.setMainRoute(SleepPageSlice.class.getName());
  6. setSwipeToDismiss(true);
  7. }
  8. }

如下是配置文件 config.json,注册 PageAbility的时候,需要指明 action.system.home,用来保证该 Ability 能在 launcher 上显示对应的图标。

  1. {
  2. "app": {
  3. "bundleName": "com.huawei.health.sleep",
  4. "vendor": "huawei",
  5. "version": {
  6. "code": 1,
  7. "name": "1.0.8.27"
  8. },
  9. "apiVersion: {
  10. "compatible": 3,
  11. "target": 3
  12. }
  13. },
  14. "deviceConfig": {
  15. "default": {
  16. }
  17. }
  18. },
  19. "module": {
  20. "package": "com.huawei.health.sleep",
  21. "name": ".SleepApplication",
  22. "distro": {
  23. "moduleType": "entry",
  24. "deliveryWithInstall": true,
  25. "moduleName": "entry"
  26. },
  27. "deviceType": [
  28. "wearable"
  29. ],
  30. "reqCapabilities": [
  31. "video_support"
  32. ],
  33. "abilities": [
  34. {
  35. "name": ".PageAbility",
  36. "description": "$string:ability_description",
  37. "icon": "$media:icon_app",
  38. "label": "$string:app_name",
  39. "launchType": "standard",
  40. "orientation": "portrait",
  41. "visible": true,
  42. "permissions": [],
  43. "skills": [
  44. {
  45. "actions": [
  46. "action.system.home"
  47. ],
  48. "entities": [
  49. "entity.system.home"
  50. ],
  51. }
  52. ],
  53. "type": "page",
  54. "formEnabled": false
  55. }
  56. ]
  57. }
  58. }

在睡眠界面中,我们用到了圆环效果,这里我们看一下圆环效果是如何实现的,如何实现自定义 Component 的效果。调用方代码如下:

  1. drawTask = new CircleProgressDrawTask(root);

Componet 类提供了 UI 的基本组件,包括方法addDrawTask(Component.DrawTask task)。该方法可以给任意一个Componet 添加一段自定义绘制的代码。自定义 Component 的实现方法如下:

  1. 创建一个自定义 DrawTask,包含与该 Componet 相关的自定义属性和绘制的代码。
  2. 在构造方法里传入宿主 Component,跟自定义的 DrawTask 绑定。

实现睡眠圆环效果的示例代码如下。

  1. public class CircleProgressDrawTask implements Component.DrawTask {
  2. // 用于配置圆环的粗细,具体参数可以在xml文件中配置
  3. private static final String STROKE_WIDTH_KEY = "stroke_width";
  4. // 用于配置圆环的最大值,具体参数可以在xml文件中配置
  5. private static final String MAX_PROGRESS_KEY = "max_progress";
  6. // 用于配置圆环的当前值,具体参数可以在xml文件中配置
  7. private static final String CURRENT_PROGRESS_KEY = "current_progress";
  8. // 用于配置起始位置的颜色,具体参数可以在xml文件中配置
  9. private static final String START_COLOR_KEY = "start_color";
  10. // 用于配置结束位置的颜色,具体参数可以在xml文件中配置
  11. private static final String END_COLOR_KEY = "end_color";
  12. // 用于配置背景色,具体参数可以在xml文件中配置
  13. private static final String BACKGROUND_COLOR_KEY = "background_color";
  14. // 用于配置起始位置的角度,具体参数可以在xml文件中配置
  15. private static final String START_ANGLE = "start_angle";
  16. private static final float MAX_ARC = 360f;
  17. private static final int DEFAULT_STROKE_WIDTH = 10;
  18. private static final int DEFAULT_MAX_VALUE = 100;
  19. private static final int DEFAULT_START_COLOR = 0xFFB566FF;
  20. private static final int DEFAULT_END_COLOR = 0xFF8A2BE2;
  21. private static final int DEFAULT_BACKGROUND_COLOR = 0xA8FFFFFF;
  22. private static final int DEFAULT_START_ANGLE = -90;
  23. private static final float DEFAULT_LINER_MAX = 100f;
  24. private static final int HALF = 2;
  25. // 圆环的宽度, 默认10个像素
  26. private int myStrokeWidth = DEFAULT_STROKE_WIDTH;
  27. // 最大的进度值, 默认是100
  28. private int myMaxValue = DEFAULT_MAX_VALUE;
  29. // 当前的进度值, 默认是0
  30. private int myCurrentValue = 0;
  31. // 起始位置的颜色, 默认浅紫色
  32. private Color myStartColor = new Color(DEFAULT_START_COLOR);
  33. // 结束位置的颜色, 默认深紫色
  34. private Color myEndColor = new Color(DEFAULT_END_COLOR);
  35. // 背景颜色, 默认浅灰色
  36. private Color myBackgroundColor = new Color(DEFAULT_BACKGROUND_COLOR);
  37. // 当前的进度值, 默认从-90度进行绘制
  38. private int myStartAngle = DEFAULT_START_ANGLE;
  39. private Component myComponent;
  40. // 传入要进行修改的component
  41. public CircleProgressDrawTask(Component component) {
  42. myComponent = component;
  43. myComponent.addDrawTask(this);
  44. }
  45. // 设置当前进度并且刷新component,value值为当前进度
  46. public void setValue(int value) {
  47. myCurrentValue = value;
  48. myComponent.invalidate();
  49. }
  50. public void setMaxValue(int maxValue) {
  51. myMaxValue = maxValue;
  52. myComponent.invalidate();
  53. }
  54. @Override
  55. public void onDraw(Component component, Canvas canvas) {
  56. // 通过canvas实现绘制圆环的功能
  57. ......
  58. }
  59. }
以上内容是否对您有帮助:
在线笔记
App下载
App下载

扫描二维码

下载编程狮App

公众号
微信公众号

编程狮公众号