鸿蒙---应用开发首页
上次用登录模板搞了个登录的界面,登录成功后,该跳转到应用首页了。 因为页面都是再同一个UIAbility里,所以跳转直接用的路由导航router
router.replaceUrl({
url: 'pages/MainPage'
});
首页页面搭建:
- 底部导航栏
build() { Column() { Tabs({ barPosition: BarPosition.End, // 导航栏再底部显示 index: 0, controller: this.controller // 控制器 可切换item }) { TabContent(){ HomeComponent() // 首页 }.tabBar(this.TabBuilder(0)) // tab组件 TabContent(){ ListComponent() // 列表页 }.tabBar(this.TabBuilder(1)) TabContent(){ MinePageContentComponent() // 我的 }.tabBar(this.TabBuilder(2)) } .vertical(false) // 横向tabs .scrollable(true) // 页面可滚动切换 .barMode(BarMode.Fixed) // 导航栏不可滚动 .onChange((index) => { console.log('tab index:: ' + index) this.currentIndex = index }) .backgroundColor('#F1F3F5') }.width('100%'); } @Builder TabBuilder(index: number) { Column() { Image(this.currentIndex == index ? this.iconPressMedia[index] : this.iconMedia[index]) .width('28vp') .height('28vp') .margin({bottom: 4}) .objectFit(ImageFit.Contain) Text(this.tabText[index]) .fontColor(this.currentIndex == index ? this.selectedColor : this.fontColor) .fontSize('16fp') .fontWeight(500) .lineHeight(20) }.width('100%') }
首页轮播图
Swiper(this.controller) {
ForEach(this.bannerList,(item,index) => { // 循环渲染
Image(item)
.width('100%')
.height('100%')
.objectFit(ImageFit.Fill)
},(item,index) => { return `${index}` })
}
.width('100%')
.aspectRatio(1.5) // 横纵比
.autoPlay(true) // 自动轮播
.loop(true) // 循环
.cachedCount(1) // 预加载组件个数
懒加载列表
-
创建数据源
export class BasicDataSource<T> implements IDataSource { private listeners: DataChangeListener[] = []; private originDataArray: T[] = []; unregisterDataChangeListener(listener: DataChangeListener): void { const pos = this.listeners.indexOf(listener); if (pos >= 0) { console.info('remove listener'); this.listeners.splice(pos, 1); } } // 框架测调用 数据源变化通过listener通知 registerDataChangeListener(listener: DataChangeListener): void { if (this.listeners.indexOf(listener) < 0) { console.info('add listener'); this.listeners.push(listener); } } // 获取子项 getData(index: number): T { return this.originDataArray[index]; } // 总共子项 totalCount(): number { return originDataArray.length; } // 重新加载数据时调用 notifyDataReload(): void { this.listeners.forEach(listener => { listener.onDataReloaded(); }) } // 添加数据时调用 notifyDataAdd(index: number): void { this.listeners.forEach(listener => { listener.onDataAdd(index); }) } // 改变数据时调用 notifyDataChange(index: number): void { this.listeners.forEach(listener => { listener.onDataChange(index); }) } // 删除数据时调用 notifyDataDelete(index: number): void { this.listeners.forEach(listener => { listener.onDataDelete(index); }) } } -
LazyForEach 渲染
LazyForEach(this.dataSource,(item,index) => { ListItem() { this.buildKeyItem() // 构建子项item } },(item) => item.toString()) // 子项唯一key -
数据源变化
调用dataSource的notify等方法通知LazyForEach更新UI
当构建子项需要index作为参数时,需要注意当数据源发生变化时(增删),后续index也会相应的变化,即需要重新构建变化项
-
@ObjectLink和@Observed
使用@ObjectLink和@Observed单独刷新数据源子属性变化的组件,避免其它组件更新时造成闪烁
用@Observed标注数据bean,用@ObjectLink标注数据源
@Observed class StringData { message: string; imgSrc: Resource; constructor(message: string, imgSrc: Resource) { his.message = message; this.imgSrc = imgSrc; } } @Component struct ChildComponent { @ObjectLink data: StringData build() { Column() { Text(this.data.message).fontSize(50) .onAppear(() => { console.info("appear:" + this.data.message) }) Image(this.data.imgSrc) .width(500) .height(200) }.margin({ left: 10, right: 10 }) } }
注:@ObjectLink装饰的成员变量仅能监听到其子属性的变化,再深入嵌套的属性便无法观测到了
用户授权权限
-
配置权限
reason:原因 abilities:使用该权限的Ability when:inuse-前台使用 always-前后台
{ "name": "ohos.permission.READ_MEDIA", "reason": "$string:read_image_permission", "usedScene": { "abilities": [ "EntryAbility" ], "when": "inuse" } } -
检查权限
async function checkAccessToken(permission: Permissions) : Promise<abilityAccessCtrl.GrantStatus> { let atManager = abilityAccessCtrl.createAtManager() let grantStatus : abilityAccessCtrl.GrantStatus let tokenId: number try { let bundleInfo : bundleManager.BundleInfo = await bundleManager.getBundleInfoForSelf(bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_APPLICATION) let appInfo: bundleManager.ApplicationInfo = bundleInfo.appInfo tokenId = appInfo.accessTokenId } catch(err) { console.error(`getBundleInfoForSelf failed, code is ${err.code}, message is ${err.message}`); } try { grantStatus = await atManager.checkAccessToken(tokenId, permission) } catch (err) { console.error(`checkAccessToken failed, code is ${err.code}, message is ${err.message}`); } return grantStatus } -
请求权限
async reqPermissionFromUser() { let context = getContext(this) as common.UIAbilityContext let atManager = abilityAccessCtrl.createAtManager() let permission : Array<Permissions> = ["ohos.permission.READ_MEDIA"] let grantStatus = await checkAccessToken(permission[0]) if (grantStatus == abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED) { console.log("request permission::","grand image video permission") this.readFile() // 同意权限后的操作 } else { console.log("request permission::","denied permission") // 请求权限 atManager.requestPermissionsFromUser(context,permission).then((data) => { let grant = data.authResults[0] if (grant === 0) { // 同意 console.log("request permission::","grand image video permission") this.readFile() } else { console.log("request permission::","denied permission") return } }).catch(err => { console.error(`requestPermissionsFromUser failed, code is ${err.code}, message is ${err.message}`); }) } }
弹窗
- 警告弹窗
showWarnDialog() { AlertDialog.show({ title: '测试', message: '内容', alignment: DialogAlignment.Center, // 弹窗显示位置 默认居中 // confirm: {} 一个按钮 primaryButton: { value: 'confirm', fontColor: '#00ff00', action: () => { console.log('dismiss dialog') } }, secondaryButton: { value: 'cancel', fontColor: '#ff0000', action: () => { console.log('dismiss dialog') } }, autoCancel: false, // 点击遮罩是否关闭弹窗 gridCount: 4 }) } - 列表选择弹窗
showListDialog() { ActionSheet.show({ title: '标题', message: '', sheets: [ { title: '选项1', action: () => { console.log('click one') } }, { title: '选项2', action: () => { console.log('click two') } }, { title: '选项3', action: () => { console.log('click three') } } ] }) } - 自定义弹窗
private controller: CustomDialogController showCustomDialog() { // 定义struct并用@CustomDialog标注 注意组件中需要有一个CustomDialogController属性 this.controller = new CustomDialogController({ builder: CustomDialogExample() }) this.controller.open() // 调用close关闭弹窗 } - 日期选择器
showDatePicker() { DatePickerDialog.show({ start: new Date('2023-10-10'), end: new Date('2024-10-10'), selected: new Date('2023-12-23'), lunar: false, // 是否显示农历 onAccept: (result) => { // 确认 console.log(result.year+'-'+result.month+'-'+result.day) // month+1 }, onCancel: () => { } }) } - 时间选择器
showTimePicker() { TimePickerDialog.show({ selected: new Date('2023-12-23 11:02'), useMilitaryTime: false, // 是否24小时制 onAccept: (result) => { console.log(result.hour+':'+result.minute) // 返回时间24h制 }, onCancel: () => { } }) } - 文本选择器
showTextPicker() { TextPickerDialog.show({ range: ['test1','test2','test3','test4'], selected: 1, onAccept: (value) => { console.log(value.value + ':::' + value.index) }, onCancel: () => { } }) }