HarmonyOS应用开发【登录页面跳转示例】

jackwangcumt 发表于 2021/07/01 06:52:46 2021/07/01
【摘要】 华为鸿蒙OS是一款面向未来、面向全场景(移动办公、运动健康、社交通信、媒体娱乐等)的分布式操作系统。在传统的单设备系统能力的基础上,它提出了基于同一套系统能力、适配多种终端形态的分布式理念,能够支持手机、平板、智能穿戴、智慧屏、车机等多种终端设备。本文重点介绍鸿蒙OS开发的APP如何实现页面跳转和参数传递,并用登录作为场景示例。

1 HarmonyOS基础概念

根据官方文档上的说明,HarmonyOS的用户应用程序包以APP Pack(Application Package)形式发布,它是由一个或多个HAP(HarmonyOS Ability Package)以及描述每个HAP属性的pack.info组成。HAP是Ability的部署包,HarmonyOS应用代码围绕Ability组件展开。一个HAP是由代码、资源、第三方库及应用配置文件组成的模块包,可分为entry和feature两种模块类型:

entry:应用的主模块。一个APP中,对于同一设备类型必须有且只有一个entry类型的HAP,可独立安装运行。
feature:应用的动态特性模块。一个APP可以包含一个或多个feature类型的HAP,也可以不含。只有包含Ability的HAP才能够独立运行。

另外,涉及如下的集中核心概念:

Ability :是应用所具备的能力的抽象,一个应用可以包含一个或多个Ability。Ability分为两种类型:FA(Feature Ability)和PA(Particle Ability)。FA/PA是应用的基本组成单元,能够实现特定的业务功能。FA有UI界面,而PA无UI界面。

库文件:是应用依赖的第三方代码(例如so、jar、bin、har等二进制文件),存放在libs目录。

资源文件:应用的资源文件(字符串、图片、音频等)存放于resources目录下,便于开发者使用和维护。

配置文件 (config.json) :是应用的Ability信息,用于声明应用的Ability,以及应用所需权限等信息。

pack.info:描述应用软件包中每个HAP的属性,由IDE编译生成,应用市场根据该文件进行拆包和HAP的分类存储。

HAR:(HarmonyOS Ability Resources)可以提供构建应用所需的所有内容,包括源代码、资源文件和config.json文件。HAR不同于HAP,HAR不能独立安装运行在设备上,只能作为应用模块的依赖项被引用。

2 HarmonyOS快速入门

    首先需要正确安装最新版本的DevEco Studio工具并进行华为开发者账号注册并实名验证,如果没安装,可参考之前的博客进行学习安装。当开发环境配置完成后,用DevEco Studio工具创建一个新工程,设备类型以【Phone】为例,使用Java语言开发,模板选择【Empty Feature Ability(Java)】。在Java UI框架中,提供了两种编写布局的方式:在XML中声明UI布局和在代码中创建布局。

    在【Project】窗口,点击【entry > src > main > resources > base > layout】,打开【ability_main.xml】文件。ability_main页面内添加两个文本和按钮,通过Text和Button组件来实现,并使用DependentLayout布局,【ability_main.xml】页面代码如下:

<?xml version="1.0" encoding="utf-8"?>
<DependentLayout
    xmlns:ohos="http://schemas.huawei.com/res/ohos"
    ohos:width="match_parent"
    ohos:height="match_parent"
    ohos:background_element="$media:appbg"
    >

    <DirectionalLayout
        ohos:top_margin="70vp"
        ohos:height="match_content"
        ohos:id="$+id:frame_container"
        ohos:background_element="$media:01"
        ohos:layout_alignment="center"
        ohos:horizontal_center="true"
        ohos:width="match_content"/>

    <Text
        ohos:id="$+id:text"
        ohos:width="match_content"
        ohos:height="50fp"
        ohos:text="$string:HelloWorld"
        ohos:text_color="#000000"
        ohos:horizontal_center="true"
        ohos:text_size="20fp"
        ohos:top_margin="10vp"
        ohos:below="$id:frame_container"
        ohos:text_alignment="vertical_center"

        />

    <TextField
        ohos:id="$+id:txt_username"
        ohos:height="50vp"
        ohos:width="match_parent"
        ohos:text="admin"
        ohos:text_color="#000000"
        ohos:text_size="19fp"
        ohos:right_padding="20vp"
        ohos:left_padding="20vp"
        ohos:hint="Enter User Name"
        ohos:vertical_center="true"
        ohos:center_in_parent="true"
        ohos:text_alignment="vertical_center"
        ohos:below="$id:text"
        ohos:margin="10vp"
        ohos:background_element="$graphic:background_text_field"
        />


    <TextField
        ohos:id="$+id:txt_upwd"
        ohos:height="50vp"
        ohos:width="match_parent"
        ohos:text="12345"
        ohos:text_color="#000000"
        ohos:text_input_type="pattern_password"
        ohos:text_size="19fp"
        ohos:right_padding="20vp"
        ohos:left_padding="20vp"
        ohos:center_in_parent="true"
        ohos:vertical_center="true"
        ohos:text_alignment="vertical_center"
        ohos:below="$id:txt_username"
        ohos:hint="Enter Password"
        ohos:margin="10vp"
        ohos:background_element="$graphic:background_text_field"
        />

    <Button
        ohos:id="$+id:btn_login"
        ohos:width="match_content"
        ohos:height="match_content"
        ohos:text="$string:btn_login"
        ohos:text_size="19fp"
        ohos:text_color="#FFFFFF"
        ohos:top_padding="8vp"
        ohos:bottom_padding="8vp"
        ohos:right_padding="70vp"
        ohos:left_padding="70vp"
        ohos:center_in_parent="true"
        ohos:below="$id:txt_upwd"
        ohos:margin="10vp"
        ohos:background_element="$graphic:background_button"
        />
</DependentLayout>

其中声明了两个文本字段,一个表示用户名,一个表示密码,其中密码用ohos:text_input_type="pattern_password"进行定义,而Button控件的UI样式可以通过ohos:background_element="$graphic:background_button"来定义,这样定义的样式,可以在不同的Button实例中引用,非常方便。MainAbilitySlices是第一个加载的Ability,它是由MainAblility启动的,MainAblility代码示例如下所示:

package com.example.myhmos;

import com.example.myhmos.slice.MainAbilitySlice;
import ohos.aafwk.ability.Ability;
import ohos.aafwk.content.Intent;

public class MainAbility extends Ability {
    @Override
    public void onStart(Intent intent) {
        super.onStart(intent);
        super.setMainRoute(MainAbilitySlice.class.getName());
    }
}

而MainAbilitySlices文件要模拟的事情,就是用户登录,其示例代码如下:

package com.example.myhmos.slice;

import com.example.myhmos.ResourceTable;
import ohos.aafwk.ability.AbilitySlice;
import ohos.aafwk.content.Intent;
import ohos.agp.components.*;
import ohos.agp.components.element.FrameAnimationElement;
import ohos.agp.components.element.ShapeElement;
import ohos.agp.utils.Color;
import ohos.agp.window.dialog.ToastDialog;
import ohos.hiviewdfx.HiLog;
import ohos.hiviewdfx.HiLogLabel;
import okhttp3.Call;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;

//https://developer.harmonyos.com/cn/docs/documentation/doc-guides/start-overview-0000000000029602
//https://developer.harmonyos.com/cn/docs/documentation/doc-guides/ui-java-animation-0000000000580278
public class MainAbilitySlice extends AbilitySlice {

    FrameAnimationElement frameAnimationElement;
    DirectionalLayout componentContainer ;
    @Override
    public void onStart(Intent intent) {
        super.onStart(intent);
        // 加载XML布局
        super.setUIContent(ResourceTable.Layout_ability_main);

        if (componentContainer == null){
            frameAnimationElement =
                    new FrameAnimationElement(getContext(), ResourceTable.Graphic_animation_element);

            Component component = new Component(getContext());
            component.setWidth(300);
            component.setHeight(300);
            component.setBackground(frameAnimationElement);
            componentContainer = (DirectionalLayout) findComponentById(ResourceTable.Id_frame_container);
            frameAnimationElement.start();
            componentContainer.removeAllComponents();
            componentContainer.addComponent(component);
        }


        // 点击登录
        Button button = (Button) findComponentById(ResourceTable.Id_btn_login);
        button.setClickedListener((componentE -> {

            button.setEnabled(false);

            String uname ="";
            String upwd ="";

            TextField txt_username = (TextField) findComponentById(ResourceTable.Id_txt_username);

            if (txt_username != null){
                uname = txt_username.getText().trim();

            }
            TextField txt_pwd = (TextField) findComponentById(ResourceTable.Id_txt_upwd);

            if (txt_pwd != null){
                upwd = txt_pwd.getText().trim();
            }

            if ("admin".equals(uname) && "12345".equals(upwd)){
                ToastDialog toastDialog = new ToastDialog(this);
                toastDialog.setText("登录成功");
                toastDialog.show();
                // 点击按钮跳转至第二个页面
                Intent intent2 = new Intent();
                intent2.setParam("user",uname);
                present(new SecondAbilitySlice(), intent2);

            }else{
                // 显示TextField错误
                ShapeElement errorElement = new ShapeElement(this, ResourceTable.Graphic_background_text_field_error);
                errorElement.setAlpha(120);
                txt_username.setBackground(errorElement);
                txt_username.setText("");
                txt_username.setHint("密码错误");
                txt_username.setHintColor(Color.RED);
                txt_username.setHorizontalPadding(100,20);
                // TextField失焦
                txt_username.clearFocus();
                ToastDialog toastDialog = new ToastDialog(this);
                toastDialog.setText("登录失败");
                toastDialog.show();

            }
            button.setEnabled(true);
        }));


    }

    @Override
    public void onActive() {
        super.onActive();
    }

    @Override
    public void onForeground(Intent intent) {
        super.onForeground(intent);
    }


}

此文件中,用FrameAnimationElement frameAnimationElement定义了一个可以用图片驱动的动画元素,这个示例也参考自官方的示例代码。后续就是监听按钮单击事件,并获取用户名控件和密码控件的值,并在本地进行值验证,当然了,实际项目则需要从数据库中进行验证,或者调用远程API进行验证。这里只是模拟,并说明如何在不同的页面进行跳转。核心代码如下:

// 点击按钮跳转至第二个页面
Intent intent2 = new Intent();
intent2.setParam("user",uname);
present(new SecondAbilitySlice(), intent2);

第二个页面SecondAbilitySlice的UI代码如下:

<?xml version="1.0" encoding="utf-8"?>
<DependentLayout
    xmlns:ohos="http://schemas.huawei.com/res/ohos"
    ohos:width="match_parent"
    ohos:height="match_parent">
    <Text
        ohos:id="$+id:text_msg"
        ohos:width="match_content"
        ohos:height="match_content"
        ohos:text="$string:HelloMsg"
        ohos:text_color="#000000"
        ohos:text_size="32fp"
        ohos:center_in_parent="true"/>
    <Button
        ohos:id="$+id:btn_pre"
        ohos:width="match_content"
        ohos:height="match_content"
        ohos:text="$string:btn_pre_page"
        ohos:text_size="19fp"
        ohos:text_color="#FFFFFF"
        ohos:top_padding="8vp"
        ohos:bottom_padding="8vp"
        ohos:right_padding="70vp"
        ohos:left_padding="70vp"
        ohos:center_in_parent="true"
        ohos:below="$id:text_msg"  
        ohos:margin="10vp"
        ohos:background_element="$graphic:background_button"
        />
</DependentLayout>

其逻辑非常简单,就是获取第一页面传递过来的用户名,并显示,同时用按钮可以返回第一页。SecondAbilitySlice后台代码如下:

package com.example.myhmos.slice;

import com.example.myhmos.ResourceTable;
import ohos.aafwk.ability.AbilitySlice;
import ohos.aafwk.content.Intent;
import ohos.agp.colors.RgbColor;
import ohos.agp.components.Button;
import ohos.agp.components.DependentLayout;
import ohos.agp.components.Text;
import ohos.agp.components.element.ShapeElement;
import ohos.agp.utils.Color;
import ohos.agp.components.DependentLayout.LayoutConfig;
import ohos.global.resource.Resource;

public class SecondAbilitySlice extends AbilitySlice {
    @Override
    public void onStart(Intent intent) {
        super.onStart(intent);
        // 加载XML布局
        super.setUIContent(ResourceTable.Layout_ability_second);

        Button btnPre = (Button) findComponentById(ResourceTable.Id_btn_pre);
        // 点击按钮跳转至第一个页面
        btnPre.setClickedListener(listener -> present(new MainAbilitySlice(), new Intent()));
        Text text = (Text) findComponentById(ResourceTable.Id_text_msg);
        if( text != null ){
            text.setText(intent.getStringParam("user"));
            text.setTextColor(Color.RED);
        }

    }
}

下面给出Text和Button定义的UI样式,background_text_field_error.xml代码如下:

<?xml version="1.0" encoding="UTF-8" ?>
<shape xmlns:ohos="http://schemas.huawei.com/res/ohos"
       ohos:shape="rectangle">
    <corners
        ohos:radius="80"/>
    <solid
        ohos:color="#E74C3C"  />
    <stroke
        ohos:color="#E74C3C"
        ohos:width="6"/>
</shape>

background_button.xml代码如下:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:ohos="http://schemas.huawei.com/res/ohos"
       ohos:shape="rectangle">
    <corners ohos:radius="100"/>
    <solid ohos:color="#007DFF"/>
</shape>

background_text_field.xml代码如下:

<?xml version="1.0" encoding="UTF-8" ?>
<shape xmlns:ohos="http://schemas.huawei.com/res/ohos"
       ohos:shape="rectangle">
    <corners
        ohos:radius="80"/>
    <solid
        ohos:color="#EEEEEE"/>
</shape>

动画定义animation_element.xml代码如下:

<?xml version="1.0" encoding="utf-8"?>
<!--
 Copyright (c) 2021 Huawei Device Co., Ltd.
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
 -->
<animation-list xmlns:ohos="http://schemas.huawei.com/res/ohos"
                ohos:oneshot="false">
    <item ohos:duration="100" ohos:element="$media:01"/>
    <item ohos:duration="100" ohos:element="$media:02"/>
    <item ohos:duration="100" ohos:element="$media:03"/>
    <item ohos:duration="100" ohos:element="$media:04"/>
    <item ohos:duration="100" ohos:element="$media:05"/>
    <item ohos:duration="100" ohos:element="$media:06"/>
    <item ohos:duration="100" ohos:element="$media:07"/>
    <item ohos:duration="100" ohos:element="$media:08"/>
    <item ohos:duration="100" ohos:element="$media:09"/>
    <item ohos:duration="100" ohos:element="$media:10"/>
    <item ohos:duration="100" ohos:element="$media:11"/>
    <item ohos:duration="100" ohos:element="$media:12"/>
</animation-list>

至此,本示例代码的核心项目文件如下所示:

2.png

如果鸿蒙OS需要访问网络资源,那么config.json中需要设置,这里定义了网络权限配置以及网络访问的设置。根据相关文档说明,鸿蒙OS默认需要访问https的网络资源,如果需要访问http的,同样需要配置:

{
  "app": {
    "bundleName": "com.example.myhmos",
    "vendor": "example",
    "version": {
      "code": 1000000,
      "name": "1.0.0"
    }
  },
  "deviceConfig": {
    "default": {
      "network": {
        "cleartextTraffic": true,
        "securityConfig": {
          "domainSettings": {
            "cleartextPermitted": true,
            "domains": [
              {
                "subdomains": true,
                "name": "192.168.0.107"
              },
              {
                "subdomains": true,
                "name": "api.k780.com"
              }
            ]
          }
        }
      }
    }
  },
  "module": {
    "reqPermissions": [{
      "name": "ohos.permission.GET_NETWORK_INFO"
    },{
      "name": "ohos.permission.SET_NETWORK_INFO"
    },{
      "name": "ohos.permission.INTERNET"
    }],
    "package": "com.example.myhmos",
    "name": ".MyApplication",
    "mainAbility": "com.example.myhmos.MainAbility",
    "deviceType": [
      "phone",
      "tablet"
    ],
    "distro": {
      "deliveryWithInstall": true,
      "moduleName": "entry",
      "moduleType": "entry",
      "installationFree": false
    },
    "abilities": [
      {
        "skills": [
          {
            "entities": [
              "entity.system.home"
            ],
            "actions": [
              "action.system.home"
            ]
          }
        ],
        "orientation": "unspecified",
        "name": "com.example.myhmos.MainAbility",
        "icon": "$media:icon",
        "description": "$string:mainability_description",
        "label": "$string:app_Name",
        "type": "page",
        "launchType": "standard"
      }
    ]
  }
}

运行此示例,则相关界面如下(图片是一个动画效果):

3.png

当登录信息错误时,会显示红色的提示,背景色也会变成淡红色,如下图所示:

4.png

登录成功后,第二页可以获取第一页传递的参数,并显示,界面如下:

5.png

注意: 如果鸿蒙OS需要调用外部API,由于此是远程模拟器,因此,本地开启的API地址可能无法直接进行访问,需要映射到公网IP才行。

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区),文章链接,文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件至:cloudbbs@huaweicloud.com进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容。
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。