鴻蒙OS 添加智能穿戴模塊

2020-09-18 17:59 更新

以下根據(jù)實(shí)際的開發(fā)樣例來(lái)展示如何在已有的 HarmonyOS 工程中添加一個(gè)智能穿戴模塊。

如圖所示,這是一個(gè)睡眠檢測(cè)應(yīng)用,應(yīng)用分為主界面和詳情界面,可以選擇使用 PageSlider 實(shí)現(xiàn)界面間的切換。PageSlider 是一個(gè)布局管理器,用于實(shí)現(xiàn)左右滑動(dòng)以及上下滑動(dòng)的翻頁(yè)效果。

圖1 開發(fā)樣例效果圖 點(diǎn)擊放大

  1. 在 DevEco Studio 上方的導(dǎo)航欄中,依次點(diǎn)擊“File > New > Module”,在“Device”中選擇“Wearable”,添加一個(gè)新模塊。

  1. 在左側(cè)的“Project”窗口,打開“entry > src > main > resources > base”,右鍵點(diǎn)擊“base”文件夾,選擇“New > Directory”,命名為“l(fā)ayout”。

  1. 右鍵點(diǎn)擊“l(fā)ayout”文件夾,選擇“New > File”,新建兩個(gè)UI布局文件,分別命名為“l(fā)ayout_sleep.xml”和“l(fā)ayout_detail.xml”。

主界面的UI布局文件是“l(fā)ayout_sleep.xml”,其完整示例代碼如下:

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

    
       <Image
               ohos:id="$+id:sleep_moon_img"
               ohos:width="46vp"
               ohos:height="46vp"
               ohos:top_margin="11vp"
               ohos:layout_alignment="horizontal_center"/>

    
       <Text
               ohos:width="match_parent"
               ohos:height="19.5vp"
               ohos:alpha="0.66"
               ohos:layout_alignment="horizontal_center"
               ohos:text_alignment="center"
               ohos:text="$string:sleep"
               ohos:text_color="$color:sleep_text_white"
               ohos:text_size="16vp"/>

    
       <DirectionalLayout xmlns:ohos="http://schemas.huawei.com/res/ohos"
                     ohos:width="match_content"
                     ohos:height="65vp"
                     ohos:top_margin="8vp"
                     ohos:layout_alignment="horizontal_center"
                     ohos:orientation="horizontal">
           <Text
                   ohos:id="$+id:sleep_hour_text"
                   ohos:width="match_content"
                   ohos:height="match_content"
                   ohos:layout_alignment="center"
                   ohos:text_alignment="center"
                   ohos:text="$string:dash"
                   ohos:text_color="$color:sleep_text_white"
                   ohos:text_size="58vp"
           />
           <Text
                   ohos:width="match_content"
                   ohos:height="match_content"
                   ohos:left_margin="2vp"
                   ohos:alpha="0.66"
                   ohos:layout_alignment="bottom"
                   ohos:bottom_padding="9.5vp"
                   ohos:text="$string:hour"
                   ohos:text_color="$color:sleep_text_white"
                   ohos:text_size="16vp"
           />
           <Text
                   ohos:id="$+id:sleep_min_text"
                   ohos:width="match_content"
                   ohos:height="match_content"
                   ohos:left_margin="2vp"
                   ohos:layout_alignment="center"
                   ohos:text_alignment="center"
                   ohos:text="$string:double_dash"
                   ohos:text_color="$color:sleep_text_white"
                   ohos:text_size="58vp"
           />
           <Text
                   ohos:width="match_content"
                   ohos:height="match_content"
                   ohos:left_margin="2vp"
                   ohos:alpha="0.66"
                   ohos:layout_alignment="bottom"
                   ohos:bottom_padding="9.5vp"
                   ohos:text="$string:minute"
                   ohos:text_color="$color:sleep_text_white"
                   ohos:text_size="16vp"
           />
       </DirectionalLayout>
       <DirectionalLayout xmlns:ohos="http://schemas.huawei.com/res/ohos"
                     ohos:width="match_content"
                     ohos:height="25vp"
                     ohos:top_margin="20.5vp"
                     ohos:layout_alignment="horizontal_center"
                     ohos:orientation="horizontal">
           <Text
                   ohos:width="match_content"
                   ohos:height="19.5vp"
                   ohos:text="$string:goal"
                   ohos:alpha="0.66"
                   ohos:text_alignment="bottom"
                   ohos:bottom_margin="1vp"
                   ohos:text_color="$color:sleep_text_white"
                   ohos:text_size="16vp"
           />
           <Text
                   ohos:id="$+id:sleep_goal_text"
                   ohos:width="match_content"
                   ohos:height="match_parent"
                   ohos:left_margin="2vp"
                   ohos:text="$string:target_sleep_time"
                   ohos:text_weight="600"
                   ohos:text_color="$color:sleep_text_white"
                   ohos:bottom_padding="2vp"
                   ohos:text_size="21vp"
           />
           <Text
                   ohos:width="match_content"
                   ohos:height="19.5vp"
                   ohos:left_margin="2vp"
                   ohos:alpha="0.66"
                   ohos:text="$string:hour"
                   ohos:text_color="$color:sleep_text_white"
                   ohos:text_size="16vp"
           />
       </DirectionalLayout>
   </DirectionalLayout>

詳情界面的 UI 布局文件是“l(fā)ayout_detail.xml”,其完整示例代碼如下:

   <?xml version="1.0" encoding="utf-8"?>
   <DirectionalLayout xmlns:ohos="http://schemas.huawei.com/res/ohos"
                 ohos:width="match_parent"
                 ohos:height="match_parent"
                 ohos:orientation="vertical"
                 ohos:background_element="#FF000000">
       <Text
               ohos:id="$+id:detail_nodata_date"
               ohos:width="match_content"
               ohos:height="23vp"
               ohos:top_margin="20vp"
               ohos:layout_alignment="horizontal_center"
               ohos:text_alignment="bottom"
               ohos:text_color="$color:sleep_text_white"
               ohos:text_weight="600"
               ohos:text_size="19vp"/>

    
       <Image
               ohos:id="$+id:detail_nodata_img"
               ohos:width="46vp"
               ohos:height="46vp"
               ohos:top_margin="25vp"
               ohos:layout_alignment="horizontal_center"
               ohos:scale_type="scale_to_center"/>

    
       <Text
               ohos:width="match_content"
               ohos:height="match_content"
               ohos:alpha="0.66"
               ohos:top_margin="12vp"
               ohos:layout_alignment="horizontal_center"
               ohos:text_alignment="center"
               ohos:text="$string:no_data"
               ohos:text_color="$color:sleep_text_white"
               ohos:text_size="16vp"/>
       <Text
               ohos:width="match_content"
               ohos:height="match_content"
               ohos:alpha="0.66"
               ohos:layout_alignment="horizontal_center"
               ohos:text_alignment="center"
               ohos:text="$string:wearing_watch_tips"
               ohos:text_color="$color:sleep_text_white"
               ohos:text_size="16vp"/>
   </DirectionalLayout>

  1. 在左側(cè)項(xiàng)目文件欄中,選擇“entry > src > main > java > 應(yīng)用包名 > slice”,在對(duì)應(yīng)的 AbilitySlice 文件的 onStart 里,使用代碼創(chuàng)建 PageSlider,添加這兩個(gè)相應(yīng)的界面。

   public class SleepPageSlice extends AbilitySlice {
       private static final String TAG = "SleepPageSlice";

    
       private static final HiLogLabel LABEL = new HiLogLabel(HiLog.LOG_APP, 0, TAG);

    
       private List<ComponentOwner> list = new ArrayList<>();

    
       private PageSliderProvider provider = new PageSliderProvider() {
           @Override
           public int getCount() {
               return list.size();
           }

    
           @Override
           public Object createPageInContainer(ComponentContainer componentContainer, int index) {
               if (index >= list.size() || componentContainer == null) {
                   HiLog.error(LABEL, "instantiateItem index error");
                   return Optional.empty();
               }
               ComponentOwner container = list.get(index);
               componentContainer.addComponent(container.getComponent());
               container.instantiateComponent();
               return container.getComponent();
           }

    
           @Override
           public void destroyPageFromContainer(ComponentContainer componentContainer, int index, Object object) {
               HiLog.info(LABEL, "destroyItem index:" + index);
               if (index >= list.size() || componentContainer == null) {
                   return;
               }
               Component content = list.get(index).getComponent();
               componentContainer.removeComponent(content);
               return;
           }

    
           @Override
           public boolean isPageMatchToObject(Component component, Object object) {
               return component == object;
           }

    
           @Override
           public void startUpdate(ComponentContainer container) {
               super.startUpdate(container);
               HiLog.info(LABEL, "startUpdate");
           }
       };

    
       @Override
       public void onStart(Intent intent) {
           super.onStart(intent);
           HiLog.info(LABEL, "onStart");

    
           // 添加子頁(yè)面
           list.add(new SleepComponentOwner(this));
           list.add(new DetailComponentOwner(this));

    
           // 設(shè)置主界面
           DirectionalLayout layout = new DirectionalLayout(this);
           ComponentContainer.LayoutConfig config = new ComponentContainer.LayoutConfig(
                   ComponentContainer.LayoutConfig.MATCH_PARENT,
                   ComponentContainer.LayoutConfig.MATCH_PARENT);
           layout.setLayoutConfig(config);

    
           // 使用PageSlider做滑動(dòng)效果
           PageSlider slider = new PageSlider(this);
           ComponentContainer.LayoutConfig sliderConfig = new ComponentContainer.LayoutConfig(
                   ComponentContainer.LayoutConfig.MATCH_PARENT,
                   ComponentContainer.LayoutConfig.MATCH_PARENT);
           slider.setLayoutConfig(sliderConfig);
           slider.setOrientation(DirectionalLayout.VERTICAL);
           slider.setProvider(provider);

    
           layout.addComponent(slider);

    
           setUIContent(layout);
       }
   }

  1. 增加 ComponentOwner 容器接口和兩個(gè)頁(yè)面的實(shí)現(xiàn)方式,如下是容器的接口 ComponentOwner.java。

   public interface ComponentOwner {
       // 獲取存放的component
       Component getComponent();

    
       // 當(dāng)包含的component被添加到容器時(shí)回調(diào)
       void instantiateComponent();
   }

  1. 增加第一個(gè)頁(yè)面 SleepComponentOwner 和第二個(gè)頁(yè)面 DetailComponentOwner,這兩個(gè)頁(yè)面從 xml 里加載。以下是首頁(yè) SleepComponentOwner 的實(shí)現(xiàn)。

   public class SleepComponentOwner implements ComponentOwner {
       private static final String TAG = "SleepViewContainer";

    
       private static final HiLogLabel LABEL = new HiLogLabel(HiLog.LOG_APP, 0, TAG);

    
       // 目標(biāo)睡眠時(shí)長(zhǎng)默認(rèn)值,單位:分鐘
       private static final int DEFAULT_SLEEP_TARGET_TIME = 480;

    
       // 睡眠時(shí)長(zhǎng)默認(rèn)值,單位:分鐘
       private static final int DEFAULT_SLEEP_TIME = 0;

    
       private CircleProgressDrawTask drawTask;

    
       private AbilityContext myContext;

    
       private Component root;

    
       public SleepComponentOwner(AbilityContext context) {
           init(context);
       }

    
       private void init(AbilityContext context) {
           myContext = context;
           LayoutScatter scatter = LayoutScatter.getInstance(context);
           root = scatter.parse(ResourceTable.Layout_layout_sleep, null, false);
           drawTask = new CircleProgressDrawTask(root);
           drawTask.setMaxValue(DEFAULT_SLEEP_TARGET_TIME);

    
           Component imageView = root.findComponentById(ResourceTable.Id_sleep_moon_img);
           imageView.setBackground(new VectorElement(context, ResourceTable.Graphic_ic_icon_moon));
       }

    
       @Override
       public Component getComponent() {
           return root;
       }

    
       @Override
       public void instantiateComponent() {
           return;
       }
   }

以下是第二個(gè)頁(yè)面 DetailComponentOwner 的實(shí)現(xiàn)。

   public class DetailComponentOwner implements ComponentOwner {
       private static final String TAG = "DetailViewContainer";

    
       private static final HiLogLabel LABEL = new HiLogLabel(HiLog.LOG_APP, 0, TAG);

    
       private AbilityContext myContext;

    
       private ComponentContainer root;

    
       public DetailComponentOwner(AbilityContext context) {
           init(context);
       }

    
       private void init(AbilityContext context) {
           root = new DirectionalLayout(context);
           ComponentContainer.LayoutConfig config = new ComponentContainer.LayoutConfig(
                   ComponentContainer.LayoutConfig.MATCH_PARENT,
                   ComponentContainer.LayoutConfig.MATCH_PARENT);
           root.setLayoutConfig(config);
           myContext = context;
       }

    
       @Override
       public Component getComponent() {
           return root;
       }

    
       @Override
       public void instantiateComponent() {
           return;
       }
   }

  1. 增加一個(gè)類型為Page的Ability,并在config.json里進(jìn)行注冊(cè)。需要在onStart里調(diào)用setSwipeToDismiss(true),來(lái)設(shè)置右滑退出。示例代碼如下:

   public class PageAbility extends Ability {
       @Override
       public void onStart(Intent intent) {
           super.onStart(intent);
           super.setMainRoute(SleepPageSlice.class.getName());
           setSwipeToDismiss(true);
       }
   }

如下是配置文件 config.json,注冊(cè) PageAbility的時(shí)候,需要指明 action.system.home,用來(lái)保證該 Ability 能在 launcher 上顯示對(duì)應(yīng)的圖標(biāo)。

   {
       "app": {
           "bundleName": "com.huawei.health.sleep",
           "vendor": "huawei",
           "version": {
               "code": 1,
               "name": "1.0.8.27"
           },
           "apiVersion: {
               "compatible": 3,
               "target": 3
           }
       },
       "deviceConfig": {
           "default": {
               }
           }
       },
       "module": {
           "package": "com.huawei.health.sleep",
           "name": ".SleepApplication",
           "distro": {
               "moduleType": "entry",
               "deliveryWithInstall": true,
               "moduleName": "entry"
           },
           "deviceType": [
               "wearable"
           ],
           "reqCapabilities": [
               "video_support"
           ],
           "abilities": [
               {
                   "name": ".PageAbility",
                   "description": "$string:ability_description",
                   "icon": "$media:icon_app",
                   "label": "$string:app_name",
                   "launchType": "standard",
                   "orientation": "portrait",
                   "visible": true,
                   "permissions": [],
                   "skills": [
                       {
                           "actions": [
                               "action.system.home"
                           ],
                           "entities": [
                               "entity.system.home"
                           ],
                       }
                   ],
                   "type": "page",
                   "formEnabled": false
               }
           ]
       }
   }

    

在睡眠界面中,我們用到了圓環(huán)效果,這里我們看一下圓環(huán)效果是如何實(shí)現(xiàn)的,如何實(shí)現(xiàn)自定義 Component 的效果。調(diào)用方代碼如下:

drawTask = new CircleProgressDrawTask(root);

Componet 類提供了 UI 的基本組件,包括方法addDrawTask(Component.DrawTask task)。該方法可以給任意一個(gè)Componet 添加一段自定義繪制的代碼。自定義 Component 的實(shí)現(xiàn)方法如下:

  1. 創(chuàng)建一個(gè)自定義 DrawTask,包含與該 Componet 相關(guān)的自定義屬性和繪制的代碼。
  2. 在構(gòu)造方法里傳入宿主 Component,跟自定義的 DrawTask 綁定。

實(shí)現(xiàn)睡眠圓環(huán)效果的示例代碼如下。

public class CircleProgressDrawTask implements Component.DrawTask {
    // 用于配置圓環(huán)的粗細(xì),具體參數(shù)可以在xml文件中配置
    private static final String STROKE_WIDTH_KEY = "stroke_width";

 
    // 用于配置圓環(huán)的最大值,具體參數(shù)可以在xml文件中配置
    private static final String MAX_PROGRESS_KEY = "max_progress";

 
    // 用于配置圓環(huán)的當(dāng)前值,具體參數(shù)可以在xml文件中配置
    private static final String CURRENT_PROGRESS_KEY = "current_progress";

 
    // 用于配置起始位置的顏色,具體參數(shù)可以在xml文件中配置
    private static final String START_COLOR_KEY = "start_color";

 
    // 用于配置結(jié)束位置的顏色,具體參數(shù)可以在xml文件中配置
    private static final String END_COLOR_KEY = "end_color";

 
    // 用于配置背景色,具體參數(shù)可以在xml文件中配置
    private static final String BACKGROUND_COLOR_KEY = "background_color";

 
    // 用于配置起始位置的角度,具體參數(shù)可以在xml文件中配置
    private static final String START_ANGLE = "start_angle";

 
    private static final float MAX_ARC = 360f;

 
    private static final int DEFAULT_STROKE_WIDTH = 10;

 
    private static final int DEFAULT_MAX_VALUE = 100;

 
    private static final int DEFAULT_START_COLOR = 0xFFB566FF;

 
    private static final int DEFAULT_END_COLOR = 0xFF8A2BE2;

 
    private static final int DEFAULT_BACKGROUND_COLOR = 0xA8FFFFFF;

 
    private static final int DEFAULT_START_ANGLE = -90;

 
    private static final float DEFAULT_LINER_MAX = 100f;

 
    private static final int HALF = 2;

 
    // 圓環(huán)的寬度, 默認(rèn)10個(gè)像素
    private int myStrokeWidth = DEFAULT_STROKE_WIDTH;

 
    // 最大的進(jìn)度值, 默認(rèn)是100
    private int myMaxValue = DEFAULT_MAX_VALUE;

 
    // 當(dāng)前的進(jìn)度值, 默認(rèn)是0
    private int myCurrentValue = 0;

 
    // 起始位置的顏色, 默認(rèn)淺紫色
    private Color myStartColor = new Color(DEFAULT_START_COLOR);

 
    // 結(jié)束位置的顏色, 默認(rèn)深紫色
    private Color myEndColor = new Color(DEFAULT_END_COLOR);

 
    // 背景顏色, 默認(rèn)淺灰色
    private Color myBackgroundColor = new Color(DEFAULT_BACKGROUND_COLOR);

 
    // 當(dāng)前的進(jìn)度值, 默認(rèn)從-90度進(jìn)行繪制
    private int myStartAngle = DEFAULT_START_ANGLE;

 
    private Component myComponent;

 
    // 傳入要進(jìn)行修改的component
    public CircleProgressDrawTask(Component component) {
        myComponent = component;
        myComponent.addDrawTask(this);
    }

 
    // 設(shè)置當(dāng)前進(jìn)度并且刷新component,value值為當(dāng)前進(jìn)度
    public void setValue(int value) {
        myCurrentValue = value;
        myComponent.invalidate();
    }

 
    public void setMaxValue(int maxValue) {
        myMaxValue = maxValue;
        myComponent.invalidate();
    }

 
    @Override
    public void onDraw(Component component, Canvas canvas) {
        // 通過(guò)canvas實(shí)現(xiàn)繪制圓環(huán)的功能
        ......
    }
}

  
以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)