【每日学点鸿蒙知识】PersistentStorage持久化、插槽方法、相对布局、上拉加载下拉刷新、List联动滑动
1、HarmonyOS 使用PersistentStorage持久化用户信息无效?
在首页通过@StorageLink(‘userInfoTest’) userInfoTest: string = ''获取,获不到,返回undefind。是什么原因呢?
首先在首页时,在Entry外声明PersistentStorage.persistProp,其次在登录完成设置值时不使用PersistentStorage.persistProp而是使用AppStorage.set来修改值。下面以userInfo为例:
PersistentStorage.persistProp('userInfo',"111") //可以直接放在import代码下方,此处只以userInfo为例,每一个需要持久化保存的都需要声明
//声明时设置的值可以任意,后续通过set修改,j尽量和目标值保持属性一致
@Entry
@Component
struct Index {
build() {
Column(){
Button("点击测试").onClick(()=>{
console.log("测试set")
AppStorage.set('userInfo', "ceshi") //直接使用AppStorage.set,而非PersistentStorage.persistProp,否则将无法修改
})
Button("查找userInfo").onClick(()=>{
const ut = AppStorage.get<string>('userInfo')
console.log(ut); //测试结果显示userInfo中存的值变成了"ceshi",杀进程,重新进入后直接点击button,log的结果依旧是"ceshi",实现持久化存储
})
}
}
}
2、HarmonyOS 有像 Vue 一样的插槽使用方法吗?
可以使用@BuilderParam装饰器,该装饰器用于声明任意UI描述的一个元素,类似slot占位符。参考链接:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/arkts-builderparam-V5
3、HarmonyOS RelativeContainer 经过offset设置的锚点位置坐标不能作为锚点,只能用没有经过offset配置的旧坐标作为锚点来给下一个组件进行标定?
比如说A组件经过了锚点配置了位置,此时A组件有一个坐标pos1,经过offset设置了新的位置pos2,B组件想要基于A的新位置pos2做标定,必须要将A组件的offset一并加上才能达到在pos2基础上做标定。
比如说下面三个组件,想要达成的效果是年龄和个人信息之间相差13,性别和年龄之间间隔13,但是实际上却是这三个组件之间的间隔为0
Row() {
Text('个人信息')
.fontColor(Color.White)
.fontSize(14)
.alignRules({
left: { anchor: 'row1', align: HorizontalAlign.Start },
center: { anchor: 'row1', align: VerticalAlign.Center }
})
.offset({
x: 13,
y: 0,
})
.id("person_info_text")
Button('年龄')
.fontSize(12)
.aspectRatio(1.857)
.alignRules({
left: { anchor: "person_info_text", align: HorizontalAlign.End },
center: { anchor: 'row1', align: VerticalAlign.Center }
})
.offset({
x: 13,
y: 0,
})
.id("age_btn")
Button('性别')
.fontSize(12)
.aspectRatio(1.857)
.alignRules({
left: { anchor: "age_btn", align: HorizontalAlign.End },
center: { anchor: 'row1', align: VerticalAlign.Center }
})
.offset({
x: 13,
y: 0,
})
.id("gender_btn")
RelativeContainer经过offset设置的锚点位置还是之前没有偏移的位置,所以后面的组件参考的位置还是偏移前的位置,所以要实现上面的效果需要这样设置:
.offset({
x: 13,
y: 0,
})
.offset({
x: 26,
y: 0,
})
.offset({
x: 39,
y: 0,
})
4、HarmonyOS 上拉加载下拉刷新的控件有没有已经封装好了的?
数据请求写在listTouchEvent里,关联性太强,需要一个封装好,把上拉下拉布局暴露在外由用户设定,监听也有的。
PullToRefresh可用的下拉刷新、上拉加载组件。可参考链接:https://gitee.com/openharmony-sig/ohos_pull_to_refresh
demo如下:
import { PullToRefresh } from '@ohos/pulltorefresh'
@Entry
@Component
struct Index {
@State refreshText: string = '';
private dataNumbers: string[] = ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10'];
private dataStrings: string[] =
['我的评论', '与我相关', '个人中心1', '个人中心2', '个人中心3', '我的发布', '设置', '退出登录'];
@State data: string[] = this.dataStrings;
// 需绑定列表或宫格组件
private scroller: Scroller = new Scroller();
build() {
Column() {
PullToRefresh({
// 必传项,列表组件所绑定的数据
data: $data,
// 必传项,需绑定传入主体布局内的列表或宫格组件
scroller: this.scroller,
// 必传项,自定义主体布局,内部有列表或宫格组件
customList: () => {
// 一个用@Builder修饰过的UI方法
this.getListView();
},
// 可选项,下拉刷新回调
onRefresh: () => {
return new Promise<string>((resolve, reject) => {
// 模拟网络请求操作,请求网络2秒后得到数据,通知组件,变更列表数据
setTimeout(() => {
resolve('刷新成功');
this.data = this.dataNumbers;
}, 2000);
});
},
// 可选项,上拉加载更多回调
onLoadMore: () => {
return new Promise<string>((resolve, reject) => {
// 模拟网络请求操作,请求网络2秒后得到数据,通知组件,变更列表数据
setTimeout(() => {
resolve('');
this.data.push("增加的条目" + this.data.length);
}, 2000);
});
},
customLoad: null,
customRefresh: null,
})
}
}
@Builder
private getListView() {
List({ space: 20, scroller: this.scroller }) {
ForEach(this.data, (item: string) => {
ListItem() {
Text(item)
.width(160)
.height(150)
.fontSize(20)
.textAlign(TextAlign.Center)
.backgroundColor('#95efd2')
}
})
}
.backgroundColor('#eeeeee')
.divider({ strokeWidth: 1, color: 0x222222 })
.edgeEffect(EdgeEffect.None) // 必须设置列表为滑动到边缘无效果
}
}
5、HarmonyOS List联动滑动?
有个页面需要两个List联动,且第一个List需要吸顶,但使用ListItemGroup做吸顶功能后,由于Index变成Apis文档中说的:ListItemGroup作为一个整体计算一个索引值,ListItemGroup内部的ListItem不计算索引值。导致两个List不再能根据Index联动.
demo示例如下:
// 商品顶部分类
interface CategoriesType {
current: string[],
hot: string[],
Categories: Map<string, Category[]>
}
interface Category {
code: string;
category: string;
}
@Entry
@Component
export default struct CityList{
private currentCategory: string = ''
private hotCategories: string[] = []
private groupCategories: Map<string, Category[]> = new Map
private groupNameList: string[] = ['A区', 'B区', 'C区', 'D区', 'F区', 'G区', 'H区', 'J区', 'K区', 'L区']
@State private selectGroupIndex: number = -1
private categoryScroller: ListScroller = new ListScroller()
private categoryScroller1: ListScroller = new ListScroller()
private isClickScroll:boolean = false
aboutToAppear() {
let jsonString: string = '{"current":["保健品专区"],"hot":["险种转换","保单挂失","保单补发"],"Categories":{"A区":[{"code":"001","category":"新增附加险"},{"code":"002","category":"保险附效1"},{"code":"003","category":"保险附效2"},{"code":"004","category":"保险附效3"},{"code":"005","category":"保险附效4"},{"code":"006","category":"保险附效5"},{"code":"007","category":"保险附效6"}],"B区":[{"code":"008","category":"保险附效1"},{"code":"009","category":"保险附效2"},{"code":"012","category":"保险附效3"}],"C区":[{"code":"008","category":"保险附效1"},{"code":"009","category":"保险附效2"},{"code":"010","category":"保险附效3"},{"code":"011","category":"保险附效4"},{"code":"012","category":"保险附效5"}],"D区":[{"code":"008","category":"保险附效1"},{"code":"009","category":"保险附效2"},{"code":"010","category":"保险附效3"},{"code":"011","category":"保险附效4"},{"code":"012","category":"保险附效5"}],"E区":[{"code":"008","category":"保险附效1"},{"code":"009","category":"保险附效2"},{"code":"010","category":"保险附效3"},{"code":"011","category":"保险附效4"},{"code":"012","category":"保险附效5"}],"F区":[{"code":"008","category":"保险附效1"},{"code":"009","category":"保险附效2"},{"code":"010","category":"保险附效3"},{"code":"011","category":"保险附效4"},{"code":"012","category":"保险附效5"}],"G区":[{"code":"008","category":"保险附效1"},{"code":"009","category":"保险附效2"},{"code":"010","category":"保险附效3"},{"code":"011","category":"保险附效4"},{"code":"012","category":"保险附效5"}],"H区":[{"code":"008","category":"保险附效1"},{"code":"009","category":"保险附效2"},{"code":"010","category":"保险附效3"},{"code":"011","category":"保险附效4"},{"code":"012","category":"保险附效5"}],"J区":[{"code":"008","category":"保险附效1"},{"code":"009","category":"保险附效2"},{"code":"010","category":"保险附效3"},{"code":"011","category":"保险附效4"},{"code":"012","category":"保险附效5"}],"K区":[{"code":"008","category":"保险附效1"},{"code":"009","category":"保险附效2"},{"code":"010","category":"保险附效3"},{"code":"011","category":"保险附效4"},{"code":"012","category":"保险附效5"}],"L区":[{"code":"008","category":"保险附效1"},{"code":"009","category":"保险附效2"},{"code":"010","category":"保险附效3"},{"code":"011","category":"保险附效4"},{"code":"012","category":"保险附效5"}]}}'
let data: CategoriesType = JSON.parse(jsonString) as CategoriesType
this.currentCategory = data.current[0]
this.hotCategories = data.hot
this.groupCategories = data.Categories
console.log('fxm = ', JSON.stringify(this.groupCategories['A区']))
}
build() {
Column() {
Row() {
this.navigationList()
}
.width('100%')
.height(42)
Column() {
this.categoryList()
}
.height('100%')
}
}
getCitiesWithGroupName(name: string): Category[] {
console.log('fxm ', this.groupCategories[name])
return this.groupCategories[name]
}
@Builder
itemHead(text: string) {
Text(text)
.fontSize(16)
.width("100%")
.padding({ left: 10 })
.height(45)
.backgroundColor(0xEEEEEE)
}
@Builder
categoryList() {
List({ scroller: this.categoryScroller }) {
ListItemGroup({ header: this.itemHead('当前专区') }) {
ListItem() {
Text(this.currentCategory)
.width("100%")
.height(45)
.fontSize(16)
.textAlign(TextAlign.Start)
.backgroundColor(0xFFFFFF)
.padding({ left: 10 })
}
}
ListItemGroup({ header: this.itemHead('热门专区') }) {
ForEach(this.hotCategories, (hotCategory: string) => {
ListItem() {
Text(hotCategory)
.width("100%")
.height(45)
.fontSize(16)
.textAlign(TextAlign.Start)
.backgroundColor(0xFFFFFF)
.padding({ left: 10 })
}
})
}
// A~L专区分组
ForEach(this.groupNameList, (item: string) => {
ListItemGroup({ header: this.itemHead(item) }) {
ForEach(this.getCitiesWithGroupName(item), (item: Category) => {
ListItem() {
Text(item.category)
.width("100%")
.height(45)
.fontSize(16)
.textAlign(TextAlign.Start)
.backgroundColor(0xFFFFFF)
.padding({ left: 10 })
}
}, (item: Category) => item.category)
}
})
}
.width('100%')
.height('100%')
.scrollBar(BarState.Off)
.sticky(StickyStyle.Header)
.onTouch(()=>{
// 分区列表触摸滚动,isClickScroll=false,防止滚动过程中与导航列表触发滚动冲突
this.isClickScroll = false
})
.onScrollIndex((start: number, end: number, center: number)=>{
// 通过selectGroupIndex状态变量与start联动控制导航列表选中状态
if(!this.isClickScroll)
this.selectGroupIndex = start - 2
})
}
@Builder
navigationList() {
List({scroller:this.categoryScroller1}) {
ForEach(this.groupNameList, (item: string, index: number) => {
ListItem() {
Text(item)
.width(42)
.height(30)
.fontSize(16)
.textAlign(TextAlign.Start)
.backgroundColor(index == this.selectGroupIndex ? 0xCCCCCC : Color.Transparent)
.padding({ left: 10 })
.borderRadius(15)
.onClick(() => {
// 导航列表选中isClickScroll=true,防止与分区列表滚动过程中带动导航列表状态变化
this.isClickScroll = true
this.selectGroupIndex = index
// 通过导航选中selectGroupIndex与Scroller控制分区列表滚动到对应位置
this.categoryScroller.scrollToIndex(index + 2, true, ScrollAlign.START)
})
}
}, (item: string) => item)
}
.listDirection(Axis.Horizontal)
.backgroundColor(Color.Transparent)
.width('100%')
}
}