例如在页面做个按钮并带有点击效果需要html,css,js共同完成
html(按钮标签)-css(按钮样式)-js(控制按钮点击操作)
当学完了ArkTs即可以同时完成html+css+js的所有功能了
只需要ArkTs这一种语言就够了
如下,使用ArkTs完成创建按钮并设置按钮样式和点击操作的代码
@State times:number = 0 //定义变量 @State监视这个变量,类似vue的属性监听,或者也可当做响应式变量,即如果不加@State,则页面不会重新渲染,变量的改变不会被监听到
build(){ // build方法代表构建页面的方法
Row(){ //将按钮声明为一行
Button(`点我${this.times}次`) // 在页面渲染出按钮元素
.backGroundColor('#36D') // 设置按钮背景颜色
.onClick(()=>this.times++) // 按钮点击事件
}
.width('100%') // 将当前行的宽设置为100%
.justifyContent(FlexAlign.Center) // 对齐模式:居中
}
这里补充下间距
padding(20) 上下左右都是20
padding({left:20}) 只有左边是20
margin也是同理
TypeScript在JavaScript的基础上加入了静态类型检查功能,因此每一个变量都有固定的数
据类型。
let msg: string = ‘hello world’
let: 声明变量的关键字,const则代表常量
msg: 变量名,自定义
string: 变量的数据类型
常见类型
// string: 字符串,可以用单引号或双引号
let msg: string = 'hello world'
// number: 数值,整数、浮点数都可以
let age: number = 21
// boolean:布尔
let finished: boolean = true
// any: 不确定类型,可以是任意类型
let a:any = 'jack!
a= 21
// union; 联合类型,可以是多个指定类型中的一种
let u: string|number|boolean = 'rose'
u = 18
// Object:对象
let p = {name: 'Jack', age: 21}
console.log(p.name)
console.log(p['name'])
// Array: 数组,元素可以是任意其它类型
let names: Array<string> = ['Jack','Rose']
let ages: number[] = [21, 18]
console.log(names[0])
TypeScript与大多数开发语言类似,支持基于if-else和switch的条件控制。
if-else:
//定义数字
let num:number = 21
// 判断是否是偶数
if(num % 2 === 0){
console.log(num +' 是偶数')
}else{
console.log(num +' 是奇数')
}
// 判断是否是正数
if(num > 0) [
console.log(num+' 是正数')
else if(num < 0){
console.log(num+' 是负数')
}else {
console.log(num+' 为0')
}
注意: 在TypeScript中,空字符串、数字0、null、undefined都被认为是false,其它值则为true
switch-case:
let grade: string='A'
switch (grade) {
case 'A': {
console.log('优秀')
break
}
case 'B': {
console.log('合格')
break
}
case 'c': {
console.log('不合格')
break
}
default: {
console.Log('非法输入')
break
}
}
TypeScript支持for和while循环,并且为一些内置类型如Array等提供了快捷迭代语法。
// 普通for
for(let i = 1; i <= 10; i++){
console.log('点赞'+i+'次')
}
// while
let i = 1;
while(i <= 10){
console.log('点赞'+i+'次')
i++;
}
//定义数组
let names: string[] = ['Jack','Rose']
// for in 送代器,遍历得到数组角标
for (const i in names) {
console.log(i ++ names[i])
}
// for of 送代器,直接得到元素
for (const name of names) {
console.log(name)
}
TypeScript通常利用function关键字声明函数,并且支持可选参数、默认参数、箭头函数等特殊语法。
//无返回值函数,返回值void可以省略
function sayHello(name : string): void{
console.log('你好,' + name +'!')
}
sayHello('Jack')
// 有返回值函数
function sum(x: number, y: number): number {
return x + y
}
let result,= sum(21, 18)
console.log('21 + 18 =' + result)
// 箭头函数
let sayHi = (name: string) =>{
console.log('你好,' + name +'!')
}
sayHi(' Rose')
// 可选参数,在参数名后加 ?,表示该参数是可选的
function sayHello(name?: string){
// 判断name是否有值,如果无值则给一个默认值
name = name ? name :'陌生人'
console.log('你好,' + name +'!')
}
sayHello('Jack')
sayHello()
// 参数默认值,在参数后面赋值,表示参数默认值
// 如果调用者没有传参,则使用默认值
function sayHello(name: string = '陌生人'){
console.log('你好,' + name +'!')
}
sayHello('Jack')
sayHello()
TypeScript具备面向对象编程的基本语法,例如interface、class、enum等。也具备封装
继承、多态等面向对象基本特征。
//定义教举
enum Msg{
HI ='Hi'
HELLO = 'Hello!'
}
// 定义接口,抽象方法接收枚举参数
interface A {
say(msg: Msg):void
}
//实现接口
class B implements A {
say(msg: Msg): void {
console.log(msg +',I am B')
}
}
// 初始化对象
let a:A = new B()
//调用方法,传递枚举参数
a.say(Msg.HI)
// 定义矩形类
class Rectangle {
//成员变量
private width: number
private length: number// 构造函数
constructor(width: number, length: number) {
this.width = width
this.length = length
}
// 成员方法
public area(): number{
return this.width * this.length
}
//定义正方形
class Square extends Rectangle{
constructor(side: number) {//调用父类构造
super(side, side)
}
}
let s = new Square(10)
console.log("正方形面积为: "+ s.area())
应用复杂时,我们可以把通用功能抽取到单独的ts文件中,每个文件都是一个模块 (module)模块可以相互加载,提高代码复用性
//定义矩形类,并通过export 导出
export class Rectangle {
//成员变量
public width: number
public length: number
//构造函数
constructor(width: number, length: number){
this.width = width
this.length = length
}
}
// 定义工具方法,求矩形面积,并通过export导出
export function area(rec: Rectangle): number{
return rec.width * rec.length
}
使用
//通过import语法导入,from后面写文件的地址
import {Rectangle, area] from '../rectangle
// 创建Rectangle对象
let r = new Rectangle(10, 20)
// 调用area方法
console.log('面积为: + area(r))
1.打开DevEco Studio开发工具
2.点击create Project
3.这里演示选择Empty Ability即可
4.点击Next
5.修改配置(下方有配置解释,根据项目需求进行更改)
6.点击finish即可创建项目
创建项目的配置介绍
Project name: 项目名
Bundle name: 包名,需要遵循com.jwz.项目名 这种规范,需要保证包名唯一
Save location: 项目保存路径
Compile SDK: SDK的版本(选择默认的就行)
Model: 选择默认的Stage就行
Enable Super Visual: (这里不用动)
Language: 开发语言需要勾选ArkTs(默认也是勾选)
Compatible SDK: 这个版本和上面一样,默认就行
Device type: 设备类型:Phone(手机) Tablet(平板),这两个可以都勾选
项目结构如下:
各个文件介绍:
点击软件右边的Previewer生成一个预览设备(首次展开会弹出欢迎界面,叉掉在进来就可以了)
这样,预览设备上面的页面就是我们index.ets文件代码编译出来的页面了
// @Entry @Component @State 装饰器: 用来装饰类结构、方法、变量
@Entry // 标记当前组件是入口组件,当页面跳转到新页面,则新页面也属于入口组件,当页面内引用别的自定义组件,则此自定义组件非入口组件
@Component //标记自定义组件
struct Index { // struct 自定义组件:可复用的UI单元
// @State 在开头讲过了,标记该变量是状态变量,值变化时会总发UI刷新
@State message: string = 'Hello World'
build() { // build UI描述: 其内部以声明式方式描达UI结构
// Row Column Text等,都是内置组件: ArkUI提供的组件
//组件分类:
//容器组件:用来完成页面布局,例如 Row、Column
//基础组件:自带样式和功能的页面元素,例如 Text
Row() {
Column() {
Text(this.message)
.fontSize(50)
.fontWeight(FontWeight.Bold) //字体加粗
.onClick(()=>{ // 事件方法:设置组件的事件回调
// 处理相关事件
console.log('111') // 这个打印语句在下面控制台点击log那里可以看到
this.message = 'hello 萧寂173' //修改内容
})
}
.width('100%')
}
.height('100%')
// fontSize fontWeight width height 属性方法: 设置组件的UI样式
}
}
效果图
Image:图片显示组件
1 声明Image组件并设置图片源:
Image(src: string|PixlMap|Resource)
1-1.string格式,通常用来加载网络图片,需要申请网络访问权限: ohos.permission.INTERNET(注意,在模拟器可以正常访问网络图片,在真实设备上需要申请网络访问权限)
Image('https://xxx .png')
1-2.PixelMap格式,可以加载像素图,常用在图片编辑中
Image(pixelMapobject) // 直接操作像素,使用比较繁琐,需要构建其对象再传给image
1-3.Resource格式,加载本地图片,推荐使用
Image($r( 'app.media,mate60' )) //访问的是entry.src.main.resources.base.media 文件夹内的图片,app.为固定格式,可以省略文件后缀名png
Image($rawfile( 'mate60.png')) //访问的是entry.src.main.resources.rawfile文件夹内的图片,直接指定图片名,这里后缀名不能省略
2.添加图片属性(width,height,borderRadius为组件通用属性,interpolation只能在图片组件使用)
Image($r( 'app.media,icon' )).width(100) // 宽度
.height(120) // 高度
.borderRadius(10) // 边框圆角
.interpolation(ImageInterpolation.High) // 图片插值,当低分辨率图片放大会有很多锯齿,设置插值会把这些锯齿弥补起来,看起来清晰度会清晰,有高中低三个选择,这里选择了高
3.完整代码
@Entry
@Component
struct Index {
@State message: string = 'Hello World'
build() {
Row() {
Column() {
Image('https://img-home.csdnimg.cn/images/20230825101811.png').width(250) // 可以用百分比,也可以用数字(默认带vp参数,虚拟像素,会根据设备像素密度进行换算,可以保证同一个元素在不同设备上视觉大小是统一的,类似于rpx,rem)
Image($r('app.media.icon')).width(250).interpolation(ImageInterpolation.High)
}
.width('100%')
}
.height('100%')
}
}
网络授权
申请网络访问权限文档:
在main/module.json5文件的module下面加入下面属性和值即可
"requestPermissions": [
{
"name": "ohos.permission.INTERNET"
}
],
Text:文本显示组件
1.声明Text组件并设置文本内容
Text(content?: string|Resource)
1-1:string格式,直接填写文本内容
Text('图片宽度')
1-2:Resource格式,读取本地资源文件
// 限定词目录存放的是国家,语言,设备等信息
// app.为固定前缀,string代表限定词目录(zh_CN和en_US)下的string.json(即国际化语言),width_label为string.json内name属性所对应的值
// 注意 同级的base目录下有个element文件夹,里面也有个string.json,这个文件是默认文件,当限定词在中英文里面都没有匹配的会来这里找(这里是默认的,不限于中英文),因此如果需要添加限定词,则这三个文件夹都需要改动
Text($r('app.string.width_label'))
2.添加文本属性
Text('注册账号')
.lineHeight(32) // 行高
.fontsize(20) // 字体大小
.fontColor('#ff1876f8') // 字体颜色.
.fontWeight(FontWeight.Medium) // 字体粗细
3.完整代码
@Entry
@Component
struct Index {
@State message: string = 'Hello World'
build() {
Row() {
Column() {
Text($r('app.string.module_desc'))
.fontSize(25) // 设置字体大小
.fontWeight(FontWeight.Bold) // 字体加粗
}
.width('100%')
}
.height('100%')
}
}
TextInput:文本输入框
1.声明TextInput组件:
TextInput( {placeholder?: ResourceStr, text?: ResourceStr})
1-1:placeHoder:输入框无输入时的提示文本
TextInput({placeholder:请输入账号或手机号"})
1-2:text:输入框当前的文本内容
TextInput({text:'itcast'})
2.添加属性和事件
TextInput({text:'当前输入文本' })
.width(150) // 宽
.height(30) //高
.backgroundColor('#FFF') // 背景色
.type(InputType.Password) // 输入框类型
.onChange(value=>{ // 输入框改变时触发,value为输入框的值
console.log(value)
})
3.完整代码
@Entry
@Component
struct Index {
@State message: string = 'Hello World'
build() {
Row() {
Column() {
TextInput({text:'当前输入文本' })
.width(150) // 宽
.height(30) //高
.backgroundColor('#FFF') // 背景色
.type(InputType.Password) // 输入框类型
.onChange(value=>{
console.log(value)
})
}
.width('100%')
}
.height('100%')
}
}
Button:按钮组件
1.声明Button组件,label是按钮文字:
Button(label?: ResourceStr)
1-1:文字型按钮
Button('点我')
1-2:自定义按钮,在Button内嵌套其它组件
Button(){
Image($r('app.media.search' )) .width(20) .margin(10)
}
2.添加属性和事件
Button('点我')
.width(100)
.height(30)
.type(ButtonType.Normal) // 按纽类型
.onClick(() => {
// 处理点击事件
}
slider:滑动条组件
Silder (options?: SliderOptions )
Slider({
min: 0,// 最小值
max: 10,// 最大值
value: 30,// 当前值
step: 0.1,// 滑动步长
style: SliderStyle.OutSet,// 决定滑块在里面还是外面
direction: Axis.Horizontal, // Vertical(垂直) 滑块方向(水平和垂直)
reverse: false // 是否反向滑动
})
.width('90%') // 需要字符串百分比
.trackThickness(5) // 决定滑块粗细
.showTips(true) // 是否展示value百分比提示
.blockColor('#36d') // 滑块背景色
.onChange(value=>{
// value就是当前滑块值
})
列就是Column容器,行就是Row容器
属性方法名 | 说明 | 参数 |
---|---|---|
justifyContent | 设置子元素在主轴方向的对齐格式 | FlexAlign枚举 |
alignItems | 设置子元素在交叉轴方向的对齐格式 | Row容器使用VerticalAlign枚举,Column容器使用HorizontalAlign枚举 |
案例代码
@Entry
@Component
struct Index {
@State imagewidth: number = 30 // 定义图片宽度
build() {
Column({space:20}){ // space 间距
Text('item1')
Text('item2')
Text('item3')
Text('item4')
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center) // 对齐方式
.alignItems(HorizontalAlign.Center) // 交叉轴对齐方式
.layoutWeight(1) // 剩余空间分配(默认都是0,使用自己设置的宽高,设置为1之后,除了其他元素自身设置的宽高以外,其他的给设置为1的元素均匀分配,类似于html的flex:1)
}
}
Divider() // 放到两个组件之间,会出现一条线,作用等同于html的hr标签
Blank() // 在容器内部,其他组件都占满了一定空间,在两个组件之间加入blank可以直接将两个组件撑到最边上
列表(List)是一种复杂容器,具备下列特点:
1.列表项(ListItem)数量过多超出屏幕后,会自动提供滚动功能
2.列表项 (ListItem)既可以纵向排列,也可以横向排列
@Entry
@Component
struct Index {
build() {
List({space:80}){
ForEach([1,2,3,4,5,6,7,8,9,10,11,12,13],item=>{
ListItem(){
// 列表项内容,只能包含一个根组件
// 超出屏幕会显示滚动条
Text("ListItem")
}
})
}
.width('100%')
.listDirection(Axis.Vertical) // 列表方向,Horizontal:水平,默认纵向(Vertical:垂直)
}
}
简单来说每个页面都是一个自定义组件,都有@component和@Entry修饰,但是@Entry标记当前自定义组件为入口组件,也就是页面组件,不能进行复用,则如果需要可复用的组件只需要把@Entry删除即可让组件可以进行复用
在同一个组件内部
// 子组件
@Component
struct Header{
@State message:string=''; // 接收父组件参数
build(){
Text(this.message) // 使用参数
}
}
// 父组件
@Entry
@Component
struct Index {
build() {
Row() {
Header({message:'123'}) // 传值给子组件
}
.height('100%')
}
}
定义在单独文件内,供全局使用
在ets下新建个components文件夹
在components下新建Header.ets文件,内容如下
// 子组件
@Component
export struct Header{
@State message:string='';
build(){
Text(this.message)
}
}
在任意需要的父组件内使用
import {Header} from '../components/Header'
// 父组件
@Entry
@Component
struct Index {
build() {
Row() {
Header({message:'1234'})
}
.height('100%')
}
}
与自定义组件类似,但是这是一个函数,可以将组件中重复的代码封装到一个函数中,在需要的地方直接调用,不需要return返回
@Builder function xj(aaa:string){
Text(aaa) // 页面会显示'111222'
}
// 父组件
@Entry
@Component
struct Index {
build() {
Row() {
xj('111222')
}
.height('100%')
}
}
// 父组件
@Entry
@Component
struct Index {
build() {
Row() {
// 局部定义需要添加this关键字才能调用
this.xj('111222')
}
.height('100%')
}
// 在局部定义不需要function关键字
@Builder xj(aaa:string){
Text(aaa) // 页面会显示'111222'
}
}
// 定义全局公共样式函数
@Styles function xj(){
.height('100%')
}
@Entry
@Component
struct Index {
build() {
Row() {
Text('你好')
}
.xj() // 使用公共样式函数
}
}
@Entry
@Component
struct Index {
// 定义局部样式函数
@Styles xj(){
.height('100%')
}
build() {
Row() {
Text('你好')
}
.xj() // 使用局部样式函数,这个不用加this
}
}
// 定义组件独有的样式函数
// 使用Extend继承,参数为继承的组件
@Extend(Text) function xj(){
.fontColor('#f36')
.fontSize(18)
}
@Entry
@Component
struct Index {
build() {
Row() {
Text('你好')
.xj() //使用
}
}
}
@Entry
@Component
struct Index {
@State items:any[]=[
{id:1,name:'华为Mate60',image:'1.jpg',price:6999,discount:1},
{id:2,name:'华为X50',image:'2.jpg',price:8799,discount:0},
{id:3,name:'小米14',image:'3.jpg',price:4379,discount:0},
{id:4,name:'OPPOFind12',image:'4.jpg',price:3299,discount:1},
{id:5,name:'荣耀100',image:'5.jpg',price:3799,discount:1}
]
build() {
Column(){
ForEach(
this.items,
(item,index)=>{
Row(){
if(item.discount){ // 条件控制,为0代表下架,也就是条件渲染
Text(index.toString()) // 数字要转成字符串,否则不显示
Image(item.image) // 因为类型是any,所以这里没提示,很正常
.width(100)
Column(){
Text(item.name)
Text(item.price.toString()) // 数字要转成字符串,否则不显示
}
.height(100)
}else{
Text('此商品已下架')
}
}
},
(item,index)=>{
// foreact的第三个参数也可以不写,内部在循环时做好了key的处理
return item.id.toString() // 返回值为字符串,表示唯一性,类似于key
}
)
}
}
}
使用@State监控对象类型
// 定义类,类里面为对象属性
class Person{
name:string
age:number
constructor(name:string,age:number) {
this.name=name
this.age =age
}
}
@Entry
@Component
struct Index {
@State p:Person = new Person('Jack',21)
// 下面这种是数组的写法
// @State p:Array<Person> = [new Person('Jack',21),new Person('Jack1',22),new Person('Jack2',23)]
build() {
Row() {
Text(`姓名: ${this.p.name},年龄: ${this.p.age}`)
.onClick(()=>{
this.p.age+=1
})
}
}
}
对象内部嵌套对象写法
// 定义类,类里面为对象属性
class Person{
name:string
age:number
gf:Person
// 第三个可有可无,需要用?判断
constructor(name:string,age:number,gf?:Person) {
this.name=name
this.age =age
this.gf = gf
}
}
@Entry
@Component
struct Index {
@State p:Person = new Person('Jack',21,new Person('萧寂',25))
build() {
Row() {
Text(`姓名: ${this.p.gf.name},年龄: ${this.p.gf.age}`)
.onClick(()=>{
this.p.gf.age+=1 // 发现并不能增加,因为嵌套类型以及数组中的对象属性无法触发视图更新,后面会讲到解决方法
})
}
}
}
在声明式UI中,是以状态驱动视图更新
状态(State): 指驱动视图更新的数据(被装饰器标记的变量)
视图 (View):基于UI描述渲染得到用户界面
@State装饰器标记的变量必须初始化,不能为空值
@State支持Object、class、string、number、boolean、enum类型以及这些类型的数组
嵌套类型以及数组中的对象属性无法触发视图更新
这个装饰器前面讲过,这里简单描述即可
小案例实现摇杆功能