鸿蒙开发(十一)组件动画、@Extend、@Styles、网络请求

211 阅读7分钟

我们在日常使用的应用中经常会看到组件的动画,组件动画的实现原理是通过改变组件的位置/角度/尺寸等属性,让组件在一段时间内产生平移/旋转/缩放等变化,从而产生流畅的视觉效果。ArkUI给我们提供了多种实现组件动画的接口,如显示动画(animateTo)、属性动画(.animation)等,我们今天来学习属性动画。

新建一个工程AnimationDemo,默认又生成了我们属性的HelloWorld页面。我们现在希望HelloWorld不要一开始就显现出来,而是有个2秒时长垂直方向上的展开动画。我们可以通过改变Text在垂直方向上的缩放比例来实现。首先我们需要定义一个状态变量textScaleY,用来保存Text组件垂直方向上的缩放比例,并通过通用属性方法.Scale()来应用该比例:

image.png

这时我们发现预览器中的HelloWorld文本消失了,这是因为我们设置了Text在垂直方向上的缩放比例是0,自然就看不到了。接着我们在生命周期函数onPageShow()中,将缩放比例设置为1:

image.png

我们发现HelloWorld字样又回来了。这是因为页面渲染之后,Text在垂直方向上的缩放比例又“立即”恢复成了1也就是原大小,所以看起来跟我们没修改代码前是一样的。但是我们希望这个过程是可被观察到的动画过渡效果,所以我们接下来通过属性方法.animation()给它设置属性动画。.animation()接收一个AnimationParam类型的对象作为参数:

declare interface AnimateParam {
    //动画的持续时间(单位毫秒,默认0,无动画效果)
    duration?: number;
    //动画的播放速度(默认1,正常速度)
    tempo?: number;
    //动画速率变化曲线(匀速、先快后慢、先慢后快等)
    curve?: Curve | string | ICurve;
    //动画延迟播放的时间(单位毫秒,默认0,立即播放)
    delay?: number;
    //动画的循环次数(默认1,-1表示无限循环)
    iterations?: number;
    //动画的播放模式(默认PlayMode.Normal,动画播放完后从头开始播放)
    playMode?: PlayMode;
    //动画播放完成后的回调函数
    onFinish?: () => void;
    //动画调用完成回调的类型(默认FinishCallBackType.REMOVE,动画被移除时进行回调)
    finishCallbackType?: FinishCallbackType;
    //动画的期望帧率
    expectedFrameRateRange?: ExpectedFrameRateRange;
}

我们只需要对持续时间和动画曲线进行设置,其他的参数保持默认即可)。将持续时长设置为2秒(2000),动画曲线设置为Curve.EaseOut(先快后慢):

image.png

保存代码后,我们就可以看到预览器中的动画效果:

PixPin_2024-12-18_15-54-53.gif

(需要注意.animation()方法调用的时机必须是在调用了需要产生变化的属性对应的属性方法之后,否则不生效。本例子中,.animation()是在.scale()之后调用的,如果调换顺序,属性动画就不生效了,小伙伴们可以自行验证。)

现在我们给页面增加并列的两个按钮,让它们分别控制文本的展开和收起:

image.png

效果如下:

PixPin_2024-12-18_16-21-08.gif

我们发现上面的代码中,两个按钮的样式有很多是重复的,比如宽度、高度、按钮类型、圆角半径等等,如果不同的组件都需要调用相同的属性方法去设置按钮的样式时,代码就会显得比较臃肿,那么有没有办法改进我们的代码呢?我们之前学习过组件的模块化(组件封装),知道组件可以复用,实际上组件的样式也是可以复用的。通过使用@Extend和@Styles装饰器,我们就能对组件的样式进行复用。

@Extend装饰器装饰一个全局方法,用来设置某一类型的组件的属性(包括通用属性和扩展属性)。例如上面例子中的两个Button,我们可以把它们相同的部分提取出来放到@Extend装饰的全局方法buttonExtend()中:

image.png

现在可以对我们的代码进行精简了:

image.png

可以看到显示效果和原来是一样的。当然,@Extend装饰的函数也可以接收参数,用于对同一类型的不同组件的属性设置不同的值:

image.png

我们再来看@Styles装饰器。@Styles可以装饰一个方法(全局或者成员方法均可),对不同类型组件的通用属性(扩展属性不支持)进行设置,如下图,由于fontSize不属于组件的通用属性,因此无法在@Styles装饰的方法中进行设置:

image.png

我们可以在不同类型的组件上调用该方法:

image.png

(@Styles装饰的方法同样也可以通过传递参数来进行组件的个性化样式设置,这里不再赘述)**

在实际的项目开发中,数据完全本地化的应用已经非常少见了,大部分的应用每次打开都会或多或少地从服务器或者公共API更新数据,因此网络连接几乎是应用开发必不可少的一部分。网络连接又分为短连接(比如HttpRequest)和长连接(比如Socket/WebSocket等),最常用的就是Http数据请求。鸿蒙给我们提供了NetworkKitRemoteCommunicationKit两个模块来实现Http数据请求功能,相较于前者,后者由华为基于NAPI封装而来,功能更加强大,使用也更加简单。因此我们主要学习该方式。

新建一个项目RcpDemo。进行网络请求之前,我们需要先申请网络权限(预览器和模拟器中运行项目不需要权限也可以进行网络请求,但真机不行)。打开module.json5文件,在module字段下增加以下配置:

image.png

接着我们在Index页面代码中导入需要用到的工具包(BasicServicesKit中定义了之后捕捉到的错误类型):

image.png

现在我们就可以进行网络请求了。为了方便快速测试,我们直接用微软必应的搜索接口cn.bing.com/search?q=关键… (该接口支持GET请求类型,返回的是一个网页,我们可以用RichText来显示返回的网页内容)。

我们先来按照官网示例尝试最简单的调用方式。将Index页面修改为拥有一个搜索栏和一个富文本框的样式,并在点击搜索按钮时对搜索栏中的关键字进行搜索。由于富文本框RichText在预览器中无法使用,所以我们在模拟器中查看效果:

image.png

接下来我们在搜索栏的提交事件响应函数中,进行网络请求。首先我们需要调用rcp.createSession()方法创建会话,然后直接调用会话的get()方法发送get请求,参数url需要我们先进行拼接,然后在then()里面对结果进行处理,此处我们让请求的结果直接转为string赋值给this.content,这样返回的网页就会在RichText中显示出来。最后别忘了在finally()中关闭会话,以释放会话占用的资源:

image.png

测试效果如下图:

PixPin_2024-12-18_23-10-17.gif

细心的小伙伴可能会发现,如果在搜索栏中输入了中文,请求就无响应了。这是因为我们在拼接get()的参数url时,没有对keyword进行URI编码。我们给它加上:

image.png

再次测试搜索中文,就能正常发送请求了:

image.png

我们再来看rcp模块的一些进阶用法。createSession()方法可以接受一个SessionConfiguration类型的参数,用于定制会话:

image.png

image.png

我们先关注其中的baseAdress,也就是会话的基地址,由于一个应用往往只连接了一个服务器,也就是说基地址是相同的,我们在SessionConfiguration中配置了基地址后,之后的业务逻辑只需要关注对应的接口就可以了,发送请求的时候会自动把基地址拼接到url参数之前:

image.png

除了设置基地址,rcp网络请求还有自定义拦截器、绕过服务器证书校验等进阶用法,我们将在之后结合实战来讲解。