第一天总结
组件说明:
一个页面可以理解成一个组件
页面构成:
1.@Entery 或者 @preview
2.@Componet
3.stcuct 组件的名称
{
4.构建函数
build(){
注意点:
1-可以没有内容
2-有内容必须是一个容器组件
}
}
页面创建会在main-page.json5形成路由,有映射关系,如果删除页面建议删除对应的联系,否则报错(不要复制文件,不会自动帮我们创建路由)
组件构成:
和页面差不多,可以没有@entry修饰符
组件可以到处,在其他页面导入使用
开发页面:
口诀:头顶天,脚踩地!(从上到下,从左到右,从外往里,从大到小)
布局:线性布局(要么横着排列要么竖着排列)
Row 横向排列 纵向居中 内容超出不会显示滚动条
Column 垂直排列 横向居中 内容超出不会显示滚动条
主轴:排列方向的轴
可以设置主轴的对齐方式:justifyContent(FlexAlign.Center)可以起始位置 居中 结束位置 对齐
第二天总结
堆叠布局(Z向盖楼)Stack内 后一个元素永远压在前一个元素上面,可以通过translate调整元素自身位置
Grid(这里{space : 10} 可以设置子组件之间的间距){ GridItem(){} }
.columnTamplate(‘1fr 1fr’)可以划分几列和比例
.rowTamplate 划分几行和比例
.columnGap(10) 列间距
.rowGap 行间距
滚动需求:
scroller :Scroller =new Scroller() //滑动控制器
bulid(this.scroller){
//有且只有一个子组件
//要想滚动 必须让内容大于scroll大小
//默认纵向滚动,如果需要横向,需要手动设置
}
事件绑定:
给元素添加事件方法:onChange(内容改变时) onSubmit(提交内容时) onClick(点击时)onFocus(聚焦) onBlur(失去焦点)
元素(组件){}.onchange(()=>{
//做一些事
提示:1.轻提示(需要引入包,自动关闭)2.弹窗提示(需要引入包,自动关闭)3.弹窗提示(不需要引入包,点击关闭)
PromptAction.showToast({message:‘’})
PromptAction.showDialog({message:‘’})
AlertDialog.show({message:''})
})
第三天总结
关于函数:
箭头函数:声明:()=>{} 没有自己的this ,this就是上级作用域的上下文
组件内函数:声明:函数名(){ }
调用:this.函数名() 看在哪里调用,谁调用的,this就指向谁(不确定,所以最好用箭头函数包裹,不要考虑改变this指向)
改变区域事件:
.onAreaChange((onldValue:Are,newVlaue:Area)=>{
onldValue.height//元素的大小宽高
}))
数据:
声明数据:变量名:类型=默认值
只会在第一次渲染页面时生效,后续更改数据不会引起页面更新
@state修饰符修饰的数据,可以在改变后进行页面的更新
简单数据类型(string,number,boolean)和引用数据类型自身及第一层变化时才生效
如果是超出第一层的变化是监听不到的,解决方案:重新复制第一层对象即可!
重新创建对象可以new Class() -> class 可以通过interface 生成 (interface2Class)->只需要定义数据的结构,然后生成对应的类即可!
双向数据绑定:1.试图驱动数据(监听改变值的事件,更新数据)2.数据变化更新视图(@state)
优化:不需要在事件中手动改变值了,用$$添加在需要改变的值前面即可!
第四天总结
样式语法:
1.样式分类:通用的样式+组件自带的
2.样式的值:固定的值,或者枚举,让值变得有意义
PX/VP/FP/LPX
PX 固定大小像素
VP虚拟像素 1VP=3PX
FP和VP一样 只不过改了系统字体,用fp的会变化(当前不支持只改字体)
lpx 类似于REM 可以设置屏幕基准值,然后给定值适配(老的项目迁移 或 单类型设备的开发)
Image
1.自建目录(assets)-> /aseets/a.png
2.用网络图片 Image('httpL//xxx') 模拟器和真机需要开网络权限
3.资源图片: media $r(‘app.media.a’)
4.rawfile: $r rawfile(a.png)
5.系统内置图标: $r(‘sys.media.icxxx’).fillColor(XX) svg 是可以填充色的
6.下载SVG(icongont(阿里巴巴图标库)/华为官方图标库)
Color+String
先声明在使用
$r('app.media.color')
国际化:创建中英文俩个字段,切换语言时,文字会自动切换
@styles
function MyStyles(){
1.不支持导入导出,只能在当前文件使用
2.只支持通用样式
3.不支持传参
}
@extend(Button)
function MyButtonStyles(可以传参){
1.不支持导入导出,只能在当前文件使用
2.只支持给某个组件添加,其他组件不能用
}
方法.styleStates
{
1.normal-> 初始状态:如果没有或者样式上不包含,出发其他状态就回不去了!
2.pressed->按压下的状态
3.disabled->禁用,会在原来的颜色上蒙层
4.focused->聚焦:预览器和模拟器tab切换才能出发
5.selected->选中下的样式,不常用
}
第五天总结
条件渲染:
if(){}else if(){} /else{}
visibility (Visbility.visiable)显示
Hidden 隐藏但是占位
Node 隐藏不占位
循环渲染:
ForEach(要循环的数据,(item:类型)=》{
结构体
},()=》{返回唯一标识的字符串},宁愿不写,也不要乱写)
组件传值:
进入页面/离开页面 aboutToApper() aboutToDisapper()
定时器:setInterval clearInterval 需要定义接收值
延时器:setTimeout ClearTimeout
第六天总结
Builder && BuilderParam
1.有没有状态更新?
有,不要用全局builder,没有,推荐用全局Builder
2.需不需要子组件更新?
需要就用$$接收
eg:
响应式条件:1.有@state 2.字面量对象传入
@state
FormData:类型={}
@Builder
MyBuilder($$:类型){
TextInput(text :$$.data)
}
这个有点问题 TextInput(text :$$.xxx) 换成 $$this.data.xxx
BuildParam 类似于vue的插槽,只能子组件内使用
声明一个默认的结构体,可以利用builder赋默认值
调用子组件时传入对应的builder 可以替换默认的结构体
第七天总结
buildParam
@builder
content(){
}
Child({
1.传输局
list:this.list
2.传结构
content:this.content
})
如果只有一个buildparam 并且不需要参数,可以使用尾随闭包的写法
Chld({
1.传数据
list:this.list
}){
this.content()
}
修饰符
@state prop Link provide Consume
数据传递方式:
父子单向:父变子变,字不会自己变 就用Prop prop可以有默认值 也可以没有
父子双向:父变子变,子变父也变,就用Link Link 不能有默认值
后代通信:
提供:Provide(起别名)
接受:Consume(t提供的名字)
接受的别名
Watch('update')
userName:string =''
update(){
this.userName???如何变化了
}
自定义弹层@CustomDialog 设置Height:110%可以不留白
1.创建弹层
类似组件
2.注册弹层
controller :CustomDialogController = new CustonDialogController({
builder:结构体,
customStyle:是否使用自定义样式
})
第八天总结
1.函数组件(结构组件)-》自定义构建函数
2.状态组件-》comp
组件-》自定义构建函数
传值(调用函数的时候传递)
传结构(不存在,自定义构建函数就是最小单元值)
组件-》组件
父子组件-》单向Prop-》自己可以改 不会同步父组件
双向Link->不能有默认值,自己改父组件也会同步
场景:如果父组件state 修饰的是一个数组对象,但是要把对象传递给子组件,子组件还得修改这个对象,需要用Observed 修饰对象的类,子组件ObjectLink 接收传递的对象,不能修改这个对象的地址,只能修改属性
跨带组件-》双向
提供:Provide
接收:Consume
3.页面之间
4.UIAbility 执剑
5.线程之间
6.进程之间
7.应用之间
8.设备之间
第九天总结
LocalStorage
页面级UI级存储状态 需要创建 注入可以在Ability 共享至各个页面 会有垃圾回收机制回收
AppStorage
应用级存储状态 可以直接存 直接读取
两种写法
修饰符:@StorageLink @StorageProp 需要给默认值
API:AppStorage.setOrCreat('key',value) AppStorage.get('key',默认值)
PersistenStorage
持久化应用关闭仍然存在 ,需要先声明,而且不能再Ability 使用 存储大小2KB 需要搭配 AppStorage
preferences
持久化:不需要声明,可以直接写入和读取。可以用头多个仓库,每个仓库可以有多个key, 每个key可以存8kb
一般使用都是封装成工具类使用 参考项目代码
环境变量:了解 比如深色 暗色模式
权限
向系统申请:internet
向用户申请:
1.moudle.jsons配置 name ,reason userScen{ ability:[] ,when:''}
2.ability 申请 mannager=abilityUIXXXX manager.requestPermissionsFromUser([''])
网络请求
http
const req=http.creatHttp()
const res=req.request("http.xxxx")
res.result 就是请求的结果 是一个字符串
三方库
axios
const result = axios.get<object.AxiosResponse<object,null>>('http.xxxxx')
result 是包装后的数据 result.data是真实接口的数据
同步异步
口诀:耗时任务加await 所在函数加async
页面跳转路由
//pushUrl 和 replaceUrl() replaceUrl会替换 上个page不会留在栈内
router.pushUrl({
url:'pages/day11/FullScreenDemo'
},
//是否单例 会在栈内唯一
router.RouterMode.Single
)
带参数跳转 这里是个对象
定义接受类型class
新page中获取
模块间跳转
包的分类
hap 可以有ability 可以有页面 可以有组件 可以有组件
hsp 共享包 可以实现按需打包
har 静态资源包 - 可以实现资源共享
app 上架包
如果是性能优先 建议使用har包
如果是体积优先 建议使用hsp包
跟原来项目关联
//跳转到其他包
//路径写法 @bundle:包名/模块名/页面路径/ets
router.pushUrl({
url:'@bundle:com.example.heimaday11/library/ets/pages/Index'
})
如果是ability包也一样关联
页面跳转到ability包 :
.onClick(() => {
let want: Want = {
deviceId:'',
bundleName: "com.example.heimaday11",
moduleName: "feature",
abilityName: "FeatureAbility",
parameters: {
id: 123456
}
};
(getContext() as common.UIAbilityContext).startAbility(want)
})
这里是有参数的所以需要接受
在page接受使用
做一个类似支付的需求
.onClick(async () => {
let want: Want = {
deviceId: '',
bundleName: "com.example.heimaday11",
moduleName: "feature",
abilityName: "FeatureAbility",
parameters: {
id: Date.now()
}
};
await (getContext() as common.UIAbilityContext).startAbilityForResult(want,
(err: BusinessError, result: common.AbilityResult) => {
console.log("zqf", JSON.stringify(result))
})
})
Button("支付成功").onClick(() => {
//结束自己
(getContext() as common.UIAbilityContext).terminateSelfWithResult({
resultCode: 200,
want: {
deviceId: '',
bundleName: "com.example.heimaday11",
moduleName: "entry",
abilityName: "EntryAbility",
parameters: {
payResult: true
}
}
})
} )
尽量不要用视频里的弹窗 加个耗时操作就可以了
.onClick(async () => {
let want: Want = {
deviceId: '',
bundleName: "com.example.heimaday11",
moduleName: "feature",
abilityName: "FeatureAbility",
parameters: {
id: Date.now()
}
};
await (getContext() as common.UIAbilityContext).startAbilityForResult(want,
(err: BusinessError, result: common.AbilityResult) => {
console.log("zqf","payResult :"+result.want?.parameters?.payResult)
//弹窗百分百出
setTimeout(()=>{
promptAction.showToast({
message:"payResult :"+result.want?.parameters?.payResult
})
},1000)
})
})
跳转到其他应用
Button('跳转其他应用')
.onClick(() => {
let want: Want = {
deviceId: '',
bundleName: "com.example.meituanapp",
moduleName: "entry",
abilityName: "EntryAbility",
};
(getContext() as common.UIAbilityContext).startAbility(want)
})
生命周期
//如果没有销毁只会执行一次
aboutToAppear(): void {
}
//页面销毁
aboutToDisappear(): void {
}
//@Entry修饰的页面多了3个周期
onPageShow(): void {
//页面展示的时候进行触发
// eg:路由跳转返回 应用前台后台切换
}
onPageHide(): void {
}
onBackPress(): boolean | void {
}
获取屏幕宽高
方法一:
aboutToAppear(): void {
this.screenWidth = px2vp(display.getDefaultDisplaySync().width)
}
方法二:
底层布局
.onAreaChange((_old, _new) => {
this.screenWidth = _new.width as number
})
手势
//单条写法
// .gesture(
// LongPressGesture.onAction(()=>{})
// .onActionEnd(()=>{})
// .onActionCancel(()=>{})
// )
//多条写法
// https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-combined-gestures
.gesture(GestureGroup(GestureMode.Parallel,
LongPressGesture()
.onAction(() => {
this.isShow = true
})
.onActionEnd(() => {
this.isShow = false
}),
PanGesture().onActionUpdate((event) => {
if (event.fingerList[0].globalX < this.screenWidth / 2) {
this.selectType = SelectType.LEFT
} else {
this.selectType = SelectType.RIGHT
}
}).onActionEnd(() => {
this.selectType = SelectType.NONE
}),
))
文件下载
import { request } from '@kit.BasicServicesKit'
@Entry
@Component
struct DownLoadDemo {
imageUrl: string = 'https://ww1.sinaimg.cn/mw690/458a9af8gy1hwxa47lqi8j20u01t4tfw.jpg'
@State newPath: string = ""
build() {
Column() {
Image(this.imageUrl)
.width(200)
.aspectRatio(1)
Button("下载").onClick(async (event: ClickEvent) => {
let filePath: string = getContext().cacheDir + '/' + Date.now() + '.jpg'
const task = await request.downloadFile(getContext(), {
url: this.imageUrl,
filePath: filePath
})
task.on('complete', () => {
this.newPath = filePath
})
})
if (this.newPath !== '') {
Image('file://'+this.newPath)
.width(200)
.aspectRatio(1)
}
}.width('100%').height('100%')
}
}
画板+保存沙箱
import { Data } from '../10MeiTuan/api/MyData'
import { fileIo } from '@kit.CoreFileKit'
import image from '@ohos.multimedia.image'
import buffer from '@ohos.buffer'
@Entry
@Component
struct HuaBanDemo {
// 画笔 加入抗锯齿
myPen: CanvasRenderingContext2D = new CanvasRenderingContext2D(new RenderingContextSettings(true))
//画布的宽高
@State
canvasWidth: number = 0
@State
canvasHeight: number = 0
@State
imagePath: string = ''
build() {
Column() {
Canvas(this.myPen)
.width('100%')
.height(300)
.backgroundColor(Color.Pink)
.onReady(() => {
//画笔配置
this.myPen.lineWidth = 2
this.myPen.strokeStyle = '#f00'
})
.onTouch((event: TouchEvent) => {
if (event.type === TouchType.Down) {
this.myPen.beginPath()
this.myPen.moveTo(event.touches[0].x, event.touches[0].y)
} else if (event.type === TouchType.Move) {
this.myPen.lineTo(event.touches[0].x, event.touches[0].y)
this.myPen.stroke()
} else if (event.type === TouchType.Up) {
this.myPen.closePath()
}
})
.onAreaChange((_old, _new) => {
this.canvasWidth = _new.width as number
this.canvasHeight = _new.height as number
})
Button('清空画布')
.onClick(() => {
this.myPen.clearRect(0, 0, this.canvasWidth, this.canvasHeight)
})
Button('保存').onClick((event: ClickEvent) => {
//jpeg 黑底 jpg透明底
// this.imagePath = this.myPen.toDataURL('image/jpg')
//保存到沙箱
let img = this.myPen.toDataURL('image/jpg')
const filePath = getContext().tempDir + '/' + Date.now() + '.jpg'
const file = fileIo.openSync(filePath, fileIo.OpenMode.CREATE | fileIo.OpenMode.READ_WRITE)
const base64Image = img.split(';base64,').pop()
const imageBuffer = buffer.from(base64Image, 'base64')
fileIo.writeSync(file.fd, imageBuffer.buffer)
fileIo.closeSync(file)
this.imagePath = 'file://' + filePath
})
if (this.imagePath) {
Image(this.imagePath)
.width('100%')
}
}
.height('100%')
.width('100%')
}
}