概述在应用中,界面通常都是动态的。如图1所示,在子目标列表中,当用户点击目标一,目标一会呈现展开状态,再次点击目标一,目标一呈现收起状态。界面会根据不同的状态展示不一样的效果。
arkui作为一种声明式ui,具有状态驱动ui更新的特点。当用户进行界面交互或有外部事件引起状态改变时,状态的变化会触发组件自动更新。所以在arkui中,我们只需要通过一个变量来记录状态。当改变状态的时候,arkui就会自动更新界面中受影响的部分。
arkui框架提供了多种管理状态的装饰器来修饰变量,使用这些装饰器修饰的变量即称为状态变量。
在组件范围传递的状态管理常见的场景如下:
场景装饰器
组件内的状态管理 @state
从父组件单向同步状态 @prop
与父组件双向同步状态 @link
跨组件层级双向同步状态 @provide和@consume
在组件内使用@state装饰器来修饰变量,可以使组件根据不同的状态来呈现不同的效果。若当前组件的状态需要通过其父组件传递而来,此时需要使用@prop装饰器;若是父子组件状态需要相互绑定进行双向同步,则需要使用@link装饰器。使用@provide和@consume装饰器可以实现跨组件层级双向同步状态。
在实际应用开发中,应用会根据需要封装数据模型。如果需要观察嵌套类对象属性变化,需要使用@observed和@objectlink装饰器,因为上述表格中的装饰器只能观察到对象的第一层属性变化。
另外,当状态改变,需要对状态变化进行监听做一些相应的操作时,可以使用@watch装饰器来修饰状态。
组件内的状态管理:@state实际开发中由于交互,组件的内容呈现可能产生变化。当需要在组件内使用状态来控制ui的不同呈现方式时,可以使用@state装饰器。以任务管理应用为例,当点击子目标列表的其中一项,列表项会展开。当再次点击同一项,列表项会收起。所以,对于某一个列表项来说,它的呈现方式会受列表项是否展开这个状态影响。
将是否展开这个状态定义为isexpanded变量,当其值为false表示目标项收起,值为true时表示目标项展开。
此时,需要使用@state装饰器修饰isexpanded,使其成为目标项内部的状态变量。通过@state装饰后,框架内部会建立数据与视图间的绑定,
当isexpanded状态变化时,目标项会随之展开或收起。
其具体实现只要用@state修饰isexpanded变量,定义是否展开状态。然后通过条件渲染,实现是否显示进度调整面板和列表项的高度变化。最后,监听列表项的点击事件,在onclick回调中改变isexpanded状态。
这样就实现了对相同列表项点击时,列表项的展开和收起功能。当用户反复点击同一个列表项时,组件内的isexpanded状态变化,列表项会自动更新。
@componentexport default struct targetlistitem { @state isexpanded: boolean = false; ... build() { ... column() { ... if (this.isexpanded) { blank() progresseditpanel(...) } } .height(this.isexpanded ? $r('app.float.expanded_item_height') : $r('app.float.list_item_height')) .onclick(() = > { ... this.isexpanded = !this.isexpanded; ... }) ... }}从父组件单向同步状态:@prop当子组件中的状态依赖从父组件传递而来时,需要使用@prop装饰器,@prop修饰的变量可以和其父组件中的状态建立单向同步关系。当父组件中状态变化时,该状态值也会更新至@prop修饰的变量;对@prop修饰的变量的修改不会影响其父组件中的状态。
图4 列表的编辑模式
如图4所示,在目标管理应用中,当用户点击子目标列表的“编辑”文本,列表进入编辑模式,点击取消,列表退出编辑模式。
整个列表是自定义组件targetlist,顶部是文本显示区域,主要是text组件,底部是一个button组件。中间区域则是用来显示每个目标项,目标项是自定义组件targetlistitem。
从图中可以看出,targetlistitem是targetlist的子组件。targetlist是targetlistitem父组件。
图5 targetlist和targetlistitem
对于父组件targetlist,其顶部显示的文本和底部按钮会随编辑模式的变化而变化,因此父组件拥有编辑模式状态。
对于子组件targetlistitem,其最右侧是否预留位置和显示勾选框也会随编辑模式变化,因此子组件也拥有编辑模式状态。
但是是否进入编辑模式,其触发点是在用户点击列表的“编辑”或取消按钮,状态变化的源头仅在于父组件targetlist。当父组件targetlist中的编辑模式变化时,子组件targetlistitem的编辑模式状态需要随之变化。
图6 从父组件单向同步iseditmode状态
在父组件targetlist中可以定义一个是否进入编辑模式的状态,即用@state修饰iseditmode。@state修饰的变量不仅是组件内部的状态,也可以作为子组件单向或双向同步的数据源。arkui提供了@prop装饰器,@prop修饰的变量可以和其父组件中的状态建立单向同步关系,所以用@prop修饰子组件targetlistitem中的iseditmode变量。
在父组件targetlist中,用@state修饰iseditmode,定义编辑模式状态。然后利用条件渲染实现根据是否进入编辑模式,显示不同的文本和按钮。同时,在父组件中需要在用户点击时改变状态,触发界面更新。
当点击“编辑”事件发生时,进入编辑模式,显示取消、全选文本和勾选框,同时显示删除按钮;当点击“取消”事件发生时,退出编辑模式,显示“编辑”文本和“添加子目标”按钮。
@componentexport default struct targetlist { @state iseditmode: boolean = false; ... build() { column() { row() { ... if (this.iseditmode) { text($r('app.string.cancel_button')) .onclick(() = > { this.iseditmode = false; ... }) ... text($r('app.string.select_all_button'))... checkbox()... } else { text($r('app.string.edit_button')) .onclick(() = > { this.iseditmode = true; }) ... } ... } ... list({ space: commonconstants.list_space }) { foreach(this.targetdata, (item: taskitembean, index: number) = > { listitem() { targetlistitem({ iseditmode: this.iseditmode, ... }) } }, (item, index) = > json.stringify(item) + index) } ... if (this.iseditmode) { button($r('app.string.delete_button')) } else { button($r('app.string.add_task')) } } ... }}在子组件targetlistitem中,使用@prop修饰子组件的iseditmode变量,定义子组件的编辑模式状态。然后同样根据是否进入编辑模式,控制目标项最右侧是否预留位置和显示勾选框。
@componentexport default struct targetlistitem { @prop iseditmode: boolean; ... column() { ... } .padding({ ... right: this.iseditmode ? $r('app.float.list_edit_padding') : $r('app.float.list_padding') }) ... if (this.iseditmode) { row() { checkbox()... } } ...}最后,最关键的一步就是要在父组件中使用子组件时,将父组件的编辑模式状态this.iseditmode传递给子组件的编辑模式状态iseditmode。
@componentexport default struct targetlist { @state iseditmode: boolean = false; ... build() { column() { ... list({ space: commonconstants.list_space }) { foreach(this.targetdata, (item: taskitembean, index: number) = > { listitem() { targetlistitem({ iseditmode: this.iseditmode, ... }) } }, (item, index) = > json.stringify(item) + index) } ... } ... }}与父组件双向同步状态:@link若是父子组件状态需要相互绑定进行双向同步时,可以使用@link装饰器。父组件中用于初始化子组件@link变量的必须是在父组件中定义的状态变量。
图7 切换目标项
在目标管理应用中,当用户点击同一个目标,目标项会展开或者收起。当用户点击不同的目标项时,除了被点击的目标项展开,同时前一次被点击的目标项会收起。
如图7所示,当目标一展开时,点击目标三,目标三会展开,同时目标一会收起。再点击目标一时,目标一展开,同时目标三会收起。
从目标一切换到目标三的流程中,关键在于最后目标一的收起,当点击目标三时,目标一需要知道点击了目标三,目标一才会收起。
图8 子目标列表目标项位置索引
在子目标列表中,每个列表项都有其位置索引值index属性,表示目标项在列表中的位置。index从0开始,即第一个目标项的索引值为0,第二个目标项的索引值为1,以此类推。此外,clickindex用来记录被点击的目标项索引。当点击目标一时,clickindex为0,点击目标三时,clickindex为2。
在父组件子目标列表和每个子组件目标项中都拥有clickindex状态。当目标一展开时,clickindex为0。此时点击目标三,目标三的clickindex变为2,只要其父组件子目标列表感知到clickindex状态变化,同时将此变化传递给目标一。目标一的clickindex即可同步改变为2,即目标一感知到此时点击了目标三。
图9 与父组件双向同步clickindex状态
将列表和目标项对应到列表组件targetlist和列表项targetlistitem。首先,需要在父组件targetlist中定义clickindex状态。
若此时子组件中的clickindex用@prop装饰器修饰,当子组件中clickindex变化时,父组件无法感知,因为@prop装饰器建立的是从父组件到子组件的单向同步关系。
arkui提供了@link装饰器,用于与父组件双向同步状态。当子组件targetlistitem中的clickindex用@link修饰,可与父组件targetlist中的clickindex建立双向同步关系。
@componentexport default struct targetlist { @state clickindex: number = commonconstants.default_click_index; ... targetlistitem({ clickindex: $clickindex, ... }) ...}首先,在父组件targetlist中用@state装饰器定义点击的目标项索引状态。然后,在子组件targetlistitem中用@link装饰器定义clickindex,当点击目标项时,clickindex更新为当前目标索引值。
完成在父子组件中定义状态后,最关键的就是要建立父子组件的双向关联关系。在父组件中使用子组件时,将父组件的clickindex传递给子组件的clickindex。其中父组件的clickindex加上$表示传递的是引用。
@componentexport default struct targetlistitem { @link @watch('onclickindexchanged') clickindex: number; @state isexpanded: boolean = false ... onclickindexchanged() { if (this.clickindex != this.index) { this.isexpanded = false; } } build() { ... column() { ... } .onclick(() = > { ... this.clickindex = this.index; ... }) ... }}当目标一感知到点击了目标三时,还需要将目标一收起,切换列表项的功能才是完整的。此时,目标一感知到clickindex变为2,需要判断与目标一本身的位置索引值0不相等,从而将目标一收起。此时,就需要用到arkui中监听状态变化@watch的能力。用@watch修饰的状态,当状态发生变化时,会触发声明时定义的回调。
我们给targetlistitem的中的clickindex状态加上@watch(onclickindexchanged)。这表示需要监听clickindex状态的变化。当clickindex状态变化时,将触发onclickindexchanged回调:如果点击的列表项索引不等于当前列表项索引,则将isexpanded状态置为false,从而收起该目标项。
跨组件层级双向同步状态:@provide和@consume
跨组件层级双向同步状态是指@provide修饰的状态变量自动对提供者组件的所有后代组件可用,后代组件通过使用@consume装饰的变量来获得对提供的状态变量的访问。@provide作为数据的提供方,可以更新其子孙节点的数据,并触发页面渲染。@consume在感知到@provide数据的更新后,会触发当前自定义组件的重新渲染。
使用@provide的好处是开发者不需要多次将变量在组件间传递。
Mac电脑扩容建议:只需一款aigo国民好物移动固态硬盘
汽车继电器的具体安装方式是怎样的
MS5534CM气压传感器在气象站的作用
高速ADC提升分辨率与带宽
网线接水晶头哪几根比较重要-clan
鸿蒙开发教程-管理组件状态
shell脚本启动java程序详情解说
如何将耦合线带通滤波器转换为HFSS模型呢?
未来的智能家居市场将会遍布智能镜子显示屏
荣耀9评测:华为荣耀9为什么如此热销,除了颜值高配置强还有这两个功能不容小觑
中国联通\华为海洋建设6000公里南大西洋海底光缆
VR社交黑马!High Fidelity登陆Steam
干货来!七大超声波创意设计推荐
“宁德时代”如何缔造下个时代
深度解析:智慧用电是什么
安路科技再度荣膺“2023工控中国风云企业”
ORB_FPGA单层图像金字塔的ORB特征提取方案分析
MAX9626, MAX9627, MAX9628 ADC驱
任正非在华为软件架构提出三大原则
百度网盘从存储工具到个人云服务操作系统