当前位置:   article > 正文

【HarmonyOS】低代码开发之FA卡片开发流程

卡片开发

【关键词】

低代码开发、FA卡片开发

1开发准备

1.1FA卡片开发注意事项

参考文档:

基于JS UI实现的Java卡片开发指导

1、只定义一个FA卡片

首先通过DevEco Studio创建一个工程,创建完成之后,找到src/main/config.json文件,

在config.json配置文件中,在module--->abilities--->forms节点下,只有一条数据。

forms节点下的isDefault字段值为true,表示该卡片为默认卡片,每个Ability有且只有一个默认卡片。

备注:forms节点的数据结构是数组,这里的要求是内部只有一个{}对象数据。

 2、提供多种尺寸

在forms节点下的supportDimensions字段中配置多种尺寸,比如这里配置了1*2和2*2两种尺寸:

3、对应卡片的js相关资源

“js”模块中的name字段要与“forms”模块中的jsComponentName字段的值一致,为js资源的实例名。

4、添加相应权限

例如:卡片需要请求网络数据,需要添加相应的网络权限,打开config.json文件,在module节点下添加reqPermissions节点,如下图所示:

 5、快照

需要在元服务开发态默认将快照图定义成和2*2卡片相同的内容,用于对新用户展示。

 1.2低码SDK初始化

基于景区模板开发可以跳过此步骤,低码模板中已经有低码SDK。

在项目工程的根目录下build.gradle文件中的dependencies闭包中添加如下依赖:

在entry的根目录下的build.gradle文件的头部添加以下内容:

 同时在dependencies闭包中添加低码SDK的依赖:

 在entry/src/main/java/包名/MyApplication.java文件中进行SDK初始化:

1.3数据模型初始化

基于景区模板开发可以跳过此步骤,此步骤主要介绍如何在云侧创建数据模型。

登录AppGallery Connect控制台,找到对应的项目,然后选中低代码平台中的数据模型,点击新建数据模型:

之后点击编辑,为该数据模型添加所需的字段:

 所需的数据字段创建完成之后,点击保存。

回到数据模型列表所在页面,点击管理数据,之后点击新建,创建以上数据模型所需的数据:

在所需的数据全部添加完成之后,数据模型就创建完成了。

1.4差异化内容展示方案

第一种:负一屏通过AGC自定义参数

在云侧数据模型中创建了extraParam这个预留参数字段(注意:此处可以创建多个字段,需要和AGC管理台位置感应服务中创建的字段保持一致),通过在AGC管理台---位置感应服务---服务申请中创建相应的字段,端侧获取该字段值,通过对不同值进行判断处理,控制端侧卡片展示不同样式内容。

第二种:小艺建议通过不同POI ID

在云侧数据模型中创建了poi_id字段(字段名称需要根据实际创建),端侧获取该字段值,通过对不同值进行判断处理,小艺建议通过不同POI ID实现控制端侧卡片展示不同的样式内容。

2开发卡片

2.1卡片目录结构

卡片的典型开发目录结构如下图所示,详细介绍参见文件组织

 目录中主要文件夹如下:

  • pages:用于存放卡片的页面。
  • common:用于存放公共资源文件,比如:图片资源。
  • resource:用于存放资源配置文件,比如:多分辨率加载配置文件。
  • i18n:用于配置不同语言场景下资源内容,比如:应用文本词条、图片路径等。

目录中文件分类如下:

  • .hml结尾的HML模板文件:用于描述卡片页面的布局结构。
  • .css结尾的CSS样式文件:用于描述页面的样式。
  • .json结尾的JSON文件:用于配置卡片中使用的变量action事件。

2.2布局卡片

在index.hml文件中实现卡片的页面布局。最外层使用div组件和stack组件。stack组件内部使用了两个div组件分别布局1x2和2x2两种不同大小的卡片样式,并且绑定click事件。1x2格式卡片为左右结构,左侧text组件显示标题,右侧Image组件显示播放按钮,绑定消息事件。2x2格式卡片为上下结构,上方text组件显示标题,下方Image组件显示播放按钮,并且绑定消息事件。

  1. <div class="container">
  2. <stack style="height: 100%;width: 100%;">
  3. <div class="item-container-small" show="{{smallCard}}" "routerEvent">
  4. <text class="title-small">{{title}}</text>
  5. <image src="{{btnSrc}}" class="btn-img-small" "messageEvent"></image>
  6. </div>
  7. <div class="item-container-normal" show="{{normalCard}}" "routerEvent">
  8. <text class="title-normal">{{title}}</text>
  9. <image src="{{btnSrc}}" class="btn-img-normal" "messageEvent"></image>
  10. </div>
  11. </stack>
  12. </div>

2.3设置卡片样式

在index.css文件中定义页面样式,除通用样式外,各组件还可以定义自己特有的样式。例如:text组件设置font-size。组件样式的设置参见各组件的详细介绍。

  1. .container {
  2. flex-direction: column;
  3. justify-content: center;
  4. align-items: center;
  5. }
  6. .title-small {
  7. font-size: 12fp;
  8. margin-start: 15px;
  9. }
  10. .title-normal {
  11. margin-top: 30px;
  12. font-size: 20fp;
  13. }
  14. .item-container-small {
  15. width: 100%;
  16. height: 100%;
  17. align-items: center;
  18. }
  19. .item-container-normal {
  20. width: 100%;
  21. height: 100%;
  22. align-items: center;
  23. flex-direction: column;
  24. }
  25. .btn-img-small {
  26. margin-start: 25px;
  27. width: 32px;
  28. height: 32px;
  29. }
  30. .btn-img-normal {
  31. margin-top: 20px;
  32. width: 64px;
  33. height: 64px;
  34. }

2.4配置卡片事件

在index.json文件中配置卡片使用的变量和事件,在data字段中声明变量,在actions字段中声明事件。

本示例在action中定义了名为“routerEvent”的事件,事件类型为“router”,用于跳转到MainAbility。更多用户自定义事件可以使用action:"message"实现。

  1. {
  2. "data": {
  3. "smallCard": false,
  4. "normalCard": true,
  5. "type": "",
  6. "title": "",
  7. "btnSrc": ""
  8. },
  9. "actions": {
  10. "routerEvent": {
  11. "action": "router",
  12. "bundleName": "com.lowcode.scenicarea",
  13. "abilityName": "com.lowcode.scenicarea.MainAbility",
  14. "params": {
  15. "message": "add detail"
  16. }
  17. },
  18. "messageEvent": {
  19. "action": "message",
  20. "params": {
  21. "message": "play"
  22. }
  23. }
  24. }
  25. }

除了通过页面主动触发事件,还可以通过卡片各生命周期的回调函数来实现业务处理。卡片生命周期回调在卡片对应的PageAbility中,本示例对应的pageAbility为com.lowcode.scenicarea.MainAbility。在MainAbility的卡片创建回调函数onCreateForm中通过Intent获取卡片的ID、name等信息:

  1. public class MainAbility extends AceAbility {
  2. public static final int DEFAULT_DIMENSION_2X2 = 2;
  3. private static final int INVALID_FORM_ID = -1;
  4. private static final HiLogLabel TAG = new HiLogLabel(HiLog.DEBUG, 0x0, MainAbility.class.getName());
  5. private String topWidgetSlice;
  6. @Override
  7. protected ProviderFormInfo onCreateForm(Intent intent) {
  8. HiLog.info(TAG, "onCreateForm");
  9. String formName = intent.getStringParam(AbilitySlice.PARAM_FORM_NAME_KEY);
  10. int dimension = intent.getIntParam(AbilitySlice.PARAM_FORM_DIMENSION_KEY, DEFAULT_DIMENSION_2X2);
  11. HiLog.info(TAG, "onCreateForm: formId=" + formId + ",formName=" + formName);
  12. FormControllerManager formControllerManager = FormControllerManager.getInstance(this);
  13. FormController formController = formControllerManager.getController(formId);
  14. formController = (formController == null) ? formControllerManager.createFormController(formId,
  15. formName, dimension) : formController;
  16. if (formController == null) {
  17. HiLog.error(TAG, "Get null controller. formId: " + formId + ", formName: " + formName);
  18. return null;
  19. }
  20. return formController.bindFormData(formId);
  21. }
  22. ...
  23. }

2.5低码获取数据模型数据

创建数据模型端侧数据实体CardInfo.java:

  1. public class CardInfo {
  2. private String id;
  3. private String type;
  4. private String title;
  5. private String btnSrc;
  6. private String extraParam;
  7. private String poi_id;
  8. setter(); // 定义各个变量的setter方法
  9. getter(); // 定义各个变量的getter方法
  10. }

创建获取数据模型端侧数据工具类DataUtil.java:

  1. public class DataUtil {
  2. private static final HiLogLabel TAG = new HiLogLabel(HiLog.INFO, 0x00201, "jscard");
  3. public static List<CardInfo> getData() {
  4. List<CardInfo> mCardInfos = new ArrayList<>();
  5. DataModelRequest request = new DataModelRequest();
  6. request.setModelId("1179644081647666113");
  7. request.setStatus(0);
  8. request.setMethodName("list");
  9. DataModelReqParams params = new DataModelReqParams();
  10. params.setOrderBy("id");
  11. params.setOrderType("asc");
  12. request.setParams(params);
  13. HarmonyTask<DataModelResponse> task = AGConnectLowCode.getInstance().callDataModel(request);
  14. task.addOnCompleteListener(new OnHarmonyCompleteListener<DataModelResponse>() {
  15. @Override
  16. public void onComplete(HarmonyTask<DataModelResponse> harmonyTask) {
  17. if (harmonyTask.isSuccessful()) {
  18. DataModelResponse response = harmonyTask.getResult();
  19. List<Map<String, Object>> list = response.getData().getRecords();
  20. mCardInfos.clear();
  21. for (Map<String, Object> map : list) {
  22. CardInfo cardInfo = new CardInfo();
  23. cardInfo.setId(map.get("id").toString());
  24. cardInfo.setType(map.get("type").toString());
  25. cardInfo.setTitle(map.get("title").toString());
  26. cardInfo.setBtnSrc(map.get("btnSrc").toString());
  27. cardInfo.setExtraParam(map.get("extraParam").toString());
  28. cardInfo.setPoi_id(map.get("poi_id").toString());
  29. mCardInfos.add(cardInfo);
  30. }
  31. HiLog.info(TAG, "success");
  32. } else {
  33. HiLog.info(TAG, "fail");
  34. }
  35. }
  36. });
  37. return mCardInfos;
  38. }
  39. }

创建单例管理类DataInstance.java用来管理数据和Player播放器:

  1. public class DataInstance {
  2. private static class SingletonHolder {
  3. private static final DataInstance INSTANCE = new DataInstance();
  4. }
  5. private DataInstance () {}
  6. public static final DataInstance getInstance() {
  7. return SingletonHolder.INSTANCE;
  8. }
  9. private List<CardInfo> list;
  10. private Player player;
  11. private boolean isPlay= false;
  12. public boolean isPlay() {
  13. return isPlay;
  14. }
  15. public void setPlay(boolean play) {
  16. isPlay = play;
  17. }
  18. public Player getPlayer() {
  19. return player;
  20. }
  21. public void setPlayer(Player player) {
  22. this.player = player;
  23. }
  24. public List<CardInfo> getList() {
  25. return list;
  26. }
  27. public void setList(List<CardInfo> list) {
  28. this.list = list;
  29. }
  30. }

2.6初始化卡片

首先在MainAbility.java类的onStart()方法中初始化数据和播放器:

  1. public class MainAbility extends AceAbility {
  2. public static final int DEFAULT_DIMENSION_2X2 = 2; //System-defined constant. Do not change its value.
  3. private static final int INVALID_FORM_ID = -1;
  4. private static final HiLogLabel TAG = new HiLogLabel(HiLog.DEBUG, 0x0, "jscard");
  5. private String topWidgetSlice;
  6. private Player mPlayer;
  7. @Override
  8. public void onStart(Intent intent) {
  9. super.onStart(intent);
  10. DataInstance.getInstance().setList(DataUtil.getData());
  11. initPlayer();
  12. }
  13. @Override
  14. public void onStop() {
  15. super.onStop();
  16. }
  17. private void initPlayer() {
  18. if (mPlayer == null) {
  19. mPlayer = new Player(this);
  20. }
  21. DataInstance.getInstance().setPlayer(mPlayer);
  22. Source source = new Source("https://www.cambridgeenglish.org/images/153149-movers-sample-listening-test-vol2.mp3");
  23. DataInstance.getInstance().getPlayer().setSource(source);
  24. DataInstance.getInstance().getPlayer().prepare();
  25. }
  26. @Override
  27. protected ProviderFormInfo onCreateForm(Intent intent) {
  28. HiLog.info(TAG, "onCreateForm");
  29. long formId = intent.getLongParam(AbilitySlice.PARAM_FORM_IDENTITY_KEY, INVALID_FORM_ID);
  30. String formName = intent.getStringParam(AbilitySlice.PARAM_FORM_NAME_KEY);
  31. int dimension = intent.getIntParam(AbilitySlice.PARAM_FORM_DIMENSION_KEY, DEFAULT_DIMENSION_2X2);
  32. HiLog.info(TAG, "onCreateForm: formId=" + formId + ",formName=" + formName);
  33. FormControllerManager formControllerManager = FormControllerManager.getInstance(this);
  34. FormController formController = formControllerManager.getController(formId);
  35. formController = (formController == null) ? formControllerManager.createFormController(formId,
  36. formName, dimension) : formController;
  37. if (formController == null) {
  38. HiLog.error(TAG, "Get null controller. formId: " + formId + ", formName: " + formName);
  39. return null;
  40. }
  41. if (DataInstance.getInstance().getList()==null || DataInstance.getInstance().getList().size()==0){
  42. DataInstance.getInstance().setList(DataUtil.getData());
  43. initPlayer();
  44. }
  45. return formController.bindFormData(formId);
  46. }
  47. ……
  48. }

在WidgetImpl.java类的bindFormData()方法中初始化卡片展示:

  1. private Context mContext;
  2. public WidgetImpl(Context context, String formName, Integer dimension) {
  3. super(context, formName, dimension);
  4. HiLog.info(TAG, "WidgetImpl");
  5. this.mContext = context;
  6. }
  7. @Override
  8. public ProviderFormInfo bindFormData(long formId) {
  9. HiLog.info(TAG, "bind form data");
  10. ZSONObject object = new ZSONObject();
  11. if (dimension == DIMENSION_1X2) {
  12. object.put("smallCard", true);
  13. object.put("normalCard", false);
  14. object.put("type", DataInstance.getInstance().getList().get(0).getType());
  15. object.put("title", DataInstance.getInstance().getList().get(0).getTitle());
  16. if (!DataInstance.getInstance().isPlay()){
  17. object.put("btnSrc", DataInstance.getInstance().getList().get(0).getBtnSrc());
  18. }else {
  19. object.put("btnSrc", DataInstance.getInstance().getList().get(1).getBtnSrc());
  20. }
  21. } else if (dimension == DIMENSION_2X2) {
  22. object.put("smallCard", false);
  23. object.put("normalCard", true);
  24. object.put("type", DataInstance.getInstance().getList().get(2).getType());
  25. object.put("title", DataInstance.getInstance().getList().get(2).getTitle());
  26. if (!DataInstance.getInstance().isPlay()){
  27. object.put("btnSrc", DataInstance.getInstance().getList().get(2).getBtnSrc());
  28. }else {
  29. object.put("btnSrc", DataInstance.getInstance().getList().get(3).getBtnSrc());
  30. }
  31. }
  32. FormBindingData bindingData = new FormBindingData(object);
  33. ProviderFormInfo formInfo = new ProviderFormInfo();
  34. formInfo.setJsBindingData(bindingData);
  35. return formInfo;
  36. }

2.7卡片效果展示

cke_212422.png

2.8音乐播放/暂停事件

此部分功能同样在WidgetImpl.java类中实现:

创建音乐播放/暂停的方法:

  1. // 暂停音乐
  2. private void pause() {
  3. DataInstance.getInstance().getPlayer().pause();
  4. }
  5. // 播放音乐
  6. private void play() {
  7. DataInstance.getInstance().getPlayer().play();
  8. }

音乐播放/暂停时更新播放按钮的图片资源:

  1. // 更新数据
  2. private void updateForm(long formId, String btnSrc) {
  3. ZSONObject object = new ZSONObject();
  4. object.put("btnSrc", btnSrc);
  5. try {
  6. if (mContext instanceof Ability) {
  7. ((Ability) mContext).updateForm(formId, new FormBindingData(object));
  8. }
  9. } catch (FormException e) {
  10. HiLog.error(TAG, e.getMessage());
  11. }
  12. }

在2.4中配置了卡片的消息事件,通过消息事件触发音乐播放,在onTriggerFormEvent()方法中接收事件并处理播放/暂停及按钮的样式更新逻辑:

  1. @Override
  2. public void onTriggerFormEvent(long formId, String message) {
  3. HiLog.info(TAG, "onTriggerFormEvent");
  4. if (!TextUtils.isEmpty(message)){
  5. ZSONObject zsonObject = ZSONObject.stringToZSON(message);
  6. String msg = zsonObject.getString("message");
  7. if (!TextUtils.isEmpty(msg) && "play".equals(msg)){
  8. if (!DataInstance.getInstance().isPlay()){
  9. DataInstance.getInstance().setPlay(true);
  10. if (dimension==DIMENSION_1X2){
  11. updateForm(formId,DataInstance.getInstance().getList().get(1).getBtnSrc());
  12. }else if (dimension==DIMENSION_2X2){
  13. updateForm(formId,DataInstance.getInstance().getList().get(3).getBtnSrc());
  14. }
  15. play();
  16. }else {
  17. DataInstance.getInstance().setPlay(false);
  18. if (dimension == DIMENSION_1X2){
  19. updateForm(formId,DataInstance.getInstance().getList().get(0).getBtnSrc());
  20. }else if (dimension==DIMENSION_2X2){
  21. updateForm(formId,DataInstance.getInstance().getList().get(2).getBtnSrc());
  22. }
  23. pause();
  24. }
  25. }
  26. }
  27. }

最后来看一下播放的效果:

2.9、差异化内容展示

在实际应用中,有时我们希望服务卡片能随用户所处的场景展示不同的内容,比如:当用户身处景点A时,卡片展示景点A的服务信息;当用户游玩到景点B时,卡片能更新展示景点B的服务信息。

针对此需求,我们可以结合“位置感应服务”来实现,当用户靠近注册的Beacon设备位置区域时,在负一屏的卡片中展示相应的内容。

在申请位置感应服务时,我们定义一个参数extFaParamTitle来设置不同区域展示的内容,然后在卡片侧获取extFaParamTitle的值,最后根据extFaParamTitle配置的不同值,来实现不同场景下展示不同内容。具体实现方式如下。

1. 开通“位置感应服务”后,申请服务时,通过“自定义参数”处参数extFaParamTitle来设置不同地方展示的标题内容。

cke_44978.png

2.在DataInstance.java中定义extFaParamTitle变量。

  1. private String extFaParamTilte;
  2. public String getExtFaParamTilte() {
  3. return extFaParamTilte;
  4. }
  5. public void setExtFaParamTilte(String extFaParamTilte) {
  6. this.extFaParamTilte = extFaParamTilte;
  7. }

3. 在MainAbility.java中的onCreateForm()方法中获取extFaParamTitle字段的值。

  1. @Override
  2. protected ProviderFormInfo onCreateForm(Intent intent) {
  3. HiLog.info(TAG, "onCreateForm");
  4. // ......此处省略部分代码
  5. // 1、从intent中获取IntentParams
  6. IntentParams intentParams = intent.getParam(AbilitySlice.PARAM_FORM_CUSTOMIZE_KEY);
  7. if (intentParams!=null){
  8. // 2、从IntentParams中获取使用方法传递的参数
  9. String extFaParam = (String) intentParams.getParam("extFaParam");
  10. HiLog.info(TAG,"extFaParam"+extFaParam);
  11. ZSONObject extraParamJson = ZSONObject.stringToZSON(extFaParam);
  12. // "{"extFaParamTilte":"xxxxs"}"
  13. String extFaParamTilte = extraParamJson.getString("extFaParamTilte");
  14. HiLog.info(TAG,"extFaParamTilte"+extFaParamTilte);
  15. DataInstance.getInstance().setExtFaParamTilte(extFaParamTilte);
  16. }
  17. // ......此处省略部分代码
  18. return formController.bindFormData(formId);
  19. }

4. 在WidgetImpl.java的bindFormData()方法中通过判断extFaParamTitle字段的值,让卡片展示不同的标题。

  1. @Override
  2. public ProviderFormInfo bindFormData(long formId) {
  3.    HiLog.info(TAG, "bind form data");
  4.    ZSONObject object = new ZSONObject();
  5.    if (dimension == DIMENSION_1X2) {
  6.        // ......此处省略部分代码
  7.        // 根据动态获取的配置项里的字段值,动态修改标题内容
  8.        if (!TextUtils.isEmpty(DataInstance.getInstance().getExtFaParamTilte())){
  9. object.put("title",DataInstance.getInstance().getExtFaParamTilte());
  10.        }
  11.        // ......此处省略部分代码
  12.    } else if (dimension == DIMENSION_2X2) {
  13.        // ......此处省略部分代码
  14.        // 根据动态获取的配置项里的字段值,动态修改标题内容
  15.        if (!TextUtils.isEmpty(DataInstance.getInstance().getExtFaParamTilte())){
  16.            object.put("title",DataInstance.getInstance().getExtFaParamTilte());
  17.        }
  18.        // ......此处省略部分代码
  19.    }
  20.    FormBindingData bindingData = new FormBindingData(object);
  21.    ProviderFormInfo formInfo = new ProviderFormInfo();
  22.    formInfo.setJsBindingData(bindingData);
  23.    return formInfo;
  24. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小丑西瓜9/article/detail/567228
推荐阅读
相关标签
  

闽ICP备14008679号