轮播图、样式组件以及、网格布局的概念

154 阅读11分钟

轮播图、样式组件以及、网格布局的概念以及应用场景使用方法

1.轮播图(Swiper)

1.1概念

Swiper组件是一个容器组件,当制作一个比如说音乐播放器首页的顶部轮播时,使用轮播图组件是最佳时刻,

Snipaste_2024-08-17_14-47-32.png

1.2用法

以Swiper为容器,内容作为Swiper的子组件就可以,设置Swiper的尺寸会保持和Swiper的组件拉伸到保持一致,设置内容的尺寸会将Swper的组件进行兼容

Swiper() {
  // 显示轮播的内容 
  // (设置尺寸,进行兼容swiper)
}
// 设置尺寸(内容拉伸、优先级高)
.width('100%')
.height(100)

1.3常用属性

loop:boolean:是否开启循环,默认值为true
autoPlay:boolean:是否自动播放,默认值为flase
interval:number:使用自动播放的时间间隔,单位为毫秒,默认值为3000
vertical:boolean:是否为纵向滑动,默认值为false
Swiper() {
  Text('0')
    .textAlign(TextAlign.Center)
    .backgroundColor(Color.Red)
    .fontColor(Color.White)
    .fontSize(50)
  Text('1')
    .textAlign(TextAlign.Center)
    .backgroundColor(Color.Green)
    .fontColor(Color.White)
    .fontSize(50)
  Text('2')
    .textAlign(TextAlign.Center)
    .backgroundColor(Color.Blue)
    .fontColor(Color.White)
    .fontSize(50)
}
.width('100%')
.height(200)
.autoPlay(true)
.interval(1000)
.vertical(true)
根据上述代码实现的内容有纵向滑动、自动播放、播放间隔

image.png

1.4设置导航点

默认的导航点一般情况下都不满足当下的开发环境,导航点一共有两种显示方式,一种是原点,另一种是数字,确定好排版,调整位置以及外观

indicator:Dotlndicator|Digitlndicator|boolean
Dotlndicator:园点指示器的样式
Digitlndicator:数字指示样式
boolean:启用导航指示器,默认值为true

1.5设置导航点位置、大小等

lefttoprighbottom,设置导航点Swper组件的距离,默认值为0

item '+' Width/Height:length:设置Swiper组件的圆点指示器的宽度/高度默认值为6,单位为vp

selectedItem '+' Width/Height:设置Swiper选中组件的圆点指示器的宽度/高度,默认值为6,单位为vp

color/selectColor:设置Swiper组件/Swiper选中组件圆点指示器的颜色,默认值'#182431'(10%透明度)/'#007D'

.width('100%')
.height(160)
.indicator(
  Indicator.dot() // 圆点
    .selectedColor(Color.White) // 选中颜色
    .selectedItemWidth(30) // 选中宽度
    .selectedItemHeight(4) // 选中高度
    .itemWidth(10) // 默认宽度
    .itemHeight(4) // 默认宽度
)

image.png

1.6页面切换方式

页面切换需要配合点击事件,对象进行操作如:向左滑动1,向右滑动1,经常性制作一个button

shouw '+' Next/Previous() : void : 翻至下一页/上一页翻页带动效切换过程,翻页时长可以通过Swiper组件duration和curve指定。

@Entry
@Component
struct SwiperDemo {
  // 1.创建控制器对象
  controller: SwiperController = new SwiperController()

  build() {
    Column({ space: 10 }) {
      // 2. 设置给 Swiper
      Swiper(this.controller) {
      }
      Row() {
        Button('上一页')
          .onClick(() => {
            // 3.调用控制器的方式实现切换效果
            this.controller.showPrevious()
          })
        Button('下一页')
          .onClick(() => {
            this.controller.showNext()
          })
      }
    }
    .width('100%')
    .height('100%')

    // .padding(20)
  }
}

2.样式组件(@Extend,@Styles,@Build)

制作一个页面时,有很多组件基本格式一样,为了美观性,减少重复的代码量,把应该重复使用的代码进行封装,传递参数,就可以提高编码效率和美观性。

2.1.@Extend

可以扩展组件的样式、事件实现重复使用的效果,比如:

Swiper() {
  Text('0')
    .textAlign(TextAlign.Center)
    .backgroundColor(Color.Red)
    .fontColor(Color.White)
    .fontSize(30)
    .onClick(() => {
        AlertDialog.show({
          message: '轮播图 1'
        })
      })
  Text('1')
    .textAlign(TextAlign.Center)
    .backgroundColor(Color.Green)
    .fontColor(Color.White)
    .fontSize(30)
    .onClick(() => {
        AlertDialog.show({
          message: '轮播图 2'
        })
      })
  Text('2')
    .textAlign(TextAlign.Center)
    .backgroundColor(Color.Blue)
    .fontColor(Color.White)
    .fontSize(30)
    .onClick(() => {
        AlertDialog.show({
          message: '轮播图 3'
        })
      })
}

这种情况就非常适合封装

语法:

//定义在全局
@Extend(组件名)
function functionName(){
    .属性()
    .事件(()=>{})
}
//使用
组件(){}
.functionName(参数...)

注意:

① 扩展的组件和使用的组件同名,比如 @Extend(Text) ,那么后续通过 Text 才可以点出该扩展

② function 的名字不能与组件本身拥有的属性同名,自身属性优先级更高

例如:Text组件上本身自带有一个fontStyle属性,我们就不能再@Extend(Text) function fontStyle()一个同名方法了

例子:

@Entry
@Component
struct Extends_demo {
  @State message: string = '样式&结构重复使用'

  build() {
    Column({ space: 10 }) {
      Text(this.message)
        .fontSize(30)
      Swiper() {
        Text('0')
          .textExtend(Color.Red, '轮播图 1')
        Text('1')
          .textExtend(Color.Green, '轮播图 2')
        Text('2')
          .textExtend(Color.Blue, '轮播图 3')
      }
      .width('100%')
      .height(160)
    }
    .width('100%')
    .height('100%')
  }
}

@Extend(Text)
function textExtend(backgroudColor: ResourceColor, info: string) {
  .textAlign(TextAlign.Center)
  .backgroundColor(backgroudColor)
  .fontColor(Color.White)
  .fontSize(30)
  .onClick(() => {
    AlertDialog.show({
      message: info
    })
  })
}

2.2.@Styles

不同于@Extend用来给某个组件进行扩展,@Styles可以抽取通用时间和属性@Styles扩展通用样式和事件给多个和组件使用

语法:

//全局定义
@Styles function functionName(){
    .通用事件属性
    .通用事件(()=>{})
}

@Component
sturc FancyUse{
//组件内定义
    @Styles fancy(){
        .通用属性
        .通用事件(()=>{})
    }
}
//使用
组件().fancy()
组件().functionName()

例子:

@Styles
function globalSize() {
  .width(100)
  .height(100)
}


@Entry
@Component
struct Day01_03_Styles {
  @State message: string = '@styles';
  @State bgColor: ResourceColor = Color.Gray

  build() {
    Column({ space: 10 }) {
      Text(this.message)
        .width(100)
        .height(100)
        .backgroundColor(this.bgColor)
        .onClick(() => {
          this.bgColor = Color.Orange
        })

      Column() {
      }
      .globalSize()
      .sizeAndColorFancy()
    

      Button('按钮')
        .width(100)
        .height(100)
        .backgroundColor(this.bgColor)
        .onClick(() => {
          this.bgColor = Color.Orange
        })
    }
    .width('100%')
    .height('100%')
  }

  @Styles
  sizeAndColorFancy() {
    .backgroundColor(this.bgColor)
    .onClick(() => {
      this.bgColor = Color.Orange
    })
  }
}

2.3.@Builder

🌟🌟可以抽取结构,又叫叫自定义构建函数,(非常常用,前面俩个都可以不用,就用这个也可以)

语法:

// 自定义 全局 构建函数
@Builder function MyGlobalBuilderFunction(param1,param2...) {
  // 结构、属性、事件放这里
}
// 使用
MyGlobalBuilderFunction(param1,param2...)

// 自定义 组件内 构建函数
@Builder MyBuilderFunction( param1,param2...) {
  // 结构、属性、事件放这里
}
// 使用
this.MyBuilderFunction(param1,param2...)

//例如:
@Builder
function GlobalTextItem(title: string) {
  Text(title)
    .fontSize(30)
    .onClick(() => {
      // 逻辑略
    })
}
@Builder
GlobalTextItem(title: string) {
  Text(title)
    .fontSize(30)
    .onClick(() => {
      // 逻辑略
    })
}

例子:

// 全局 Builder
@Builder
function navItem(icon: ResourceStr, text: string) {
  Column({ space: 10 }) {
    Image(icon)
      .width('80%');
    Text(text);
  }
  .width('25%')
  .onClick(() => {
    AlertDialog.show({
      message: '点了' + text
    })
  })
}

@Entry
@Component
struct Day01_04_Builder {
  @State message: string = '@Builder';

  build() {
    Column({ space: 20 }) {
      Text(this.message)
        .fontSize(30)

      Row() {
        // 这两个使用全局的
        navItem($r('app.media.ic_reuse_01'), '阿里拍卖')
        navItem($r('app.media.ic_reuse_02'), '菜鸟')
        // 这两个使用本地的
        this.navItem($r('app.media.ic_reuse_03'), '芭芭农场')
        this.navItem($r('app.media.ic_reuse_04'), '阿里药房')
      }
    }
    .width('100%')
    .height('100%')
  }

  // 本地 Builder
  @Builder
  navItem(icon: ResourceStr, text: string) {
    Column({ space: 10 }) {
      Image(icon)
        .width('80%');
      Text(text);
    }
    .width('25%')
    .onClick(() => {
      AlertDialog.show({
        message: '点了' + text
      })
    })
  }
}

2.4.## 2.4. @Extend、@Styles、@Builder 对比

@Etend:适合抽取特定组件样式、事件:可以传递参数

@Styles:适合抽取公共组件样式、事件:不可以传递参数

@Builder:适合抽取结构、样式、事件:可以传递参数

3.Grid网格布局

页面是由一网格布局组成的,特定情况下需要行列合并,就可以使用Grid/Griditem实现

3.1.固定行列

结构

//整体结构和List相同
Grid() {
  GridItem(){
    // 展示的内容放在这里
  }
  GridItem(){
    // 展示的内容放在这里
  }
}

常用属性

colums/rows '+' Template : string : 设置当前网格布局列/行的数量不设置默认是1,例如:'1fr 1fr 1fr' 是将父组件分3列,将父组件允许的宽分为4等份,第一列占1份,第二列占1份,第三列占2份。

colunns/rows '+' Gap : length : 设置列与列/行与行的间距。

列子:

@Entry
@Component
struct Index {
  @Builder gridItemBuilder(text:string){
    GridItem(){
      Text(text)
    }
    .backgroundColor('#0094ff')
  }

  build() {
    Column(){
      Grid(){
        this.gridItemBuilder('1')
        this.gridItemBuilder('2')
        this.gridItemBuilder('3')
        this.gridItemBuilder('4')
        this.gridItemBuilder('5')
        this.gridItemBuilder('6')
        this.gridItemBuilder('7')
        this.gridItemBuilder('8')
        this.gridItemBuilder('9')
      }
      .width('100%')
      .height(300)
      .backgroundColor(Color.Gray)
      // 1. columnsTemplate+rowsTemplate可以固定几行几列来显示
      .columnsTemplate('1fr 1fr 1fr')
      .rowsTemplate('1fr 1fr 1fr')
      .columnsGap(5)
      .rowsGap(5)
    }
    .width('100%')
    .height('100%')
    
  }
}

🌟🌟3.2.淘宝二楼案例

interface TaoBaoItemContent {
  title: string
  icon: string
}

@Entry
@Component
struct TaobaoPage {
  contentList: TaoBaoItemContent[] = [
    { title: '淘金币', icon: 'app.media.ic_taobao_01' },
    { title: '一起摇现金', icon: 'app.media.ic_taobao_02' },
    { title: '闲鱼', icon: 'app.media.ic_taobao_03' },
    { title: '中通快递', icon: 'app.media.ic_taobao_04' },
    { title: '芭芭农场', icon: 'app.media.ic_taobao_05' },
    { title: '淘宝珍库', icon: 'app.media.ic_taobao_06' },
    { title: '阿里拍卖', icon: 'app.media.ic_taobao_07' },
    { title: '阿里药房', icon: 'app.media.ic_taobao_08' },
    { title: '小黑盒', icon: 'app.media.ic_taobao_09' },
    { title: '菜鸟', icon: 'app.media.ic_taobao_10' },
    { title: 'U先试用', icon: 'app.media.ic_taobao_11' },
    { title: '有好价', icon: 'app.media.ic_taobao_12' },
    { title: '极有家', icon: 'app.media.ic_taobao_13' },
    { title: '天猫榜单', icon: 'app.media.ic_taobao_14' },
    { title: '天天特卖', icon: 'app.media.ic_taobao_15' },
    { title: '每日好店', icon: 'app.media.ic_taobao_16' },
    { title: '全球购', icon: 'app.media.ic_taobao_17' },
    { title: '我的爱车', icon: 'app.media.ic_taobao_18' },
    { title: '造点新货', icon: 'app.media.ic_taobao_19' },
    { title: '首单优惠', icon: 'app.media.ic_taobao_20' },
    { title: '潮Woo', icon: 'app.media.ic_taobao_21' },
    { title: '亲宝贝', icon: 'app.media.ic_taobao_22' },
    { title: '领券中心', icon: 'app.media.ic_taobao_23' },
    { title: '天猫奢品', icon: 'app.media.ic_taobao_24' },
    { title: 'iFashion', icon: 'app.media.ic_taobao_25' }
  ]

  build() {
    Column() {
      Column() {
        // 返回区域
        this.BackBuilder()

        // 搜索区域
        this.SearchBuilder()

        // 网格区域
        this.GridBuilder()
      }
    }
    .width('100%')
    .height('100%')
    .linearGradient({
      colors: [
        ['#271b41', 0],
        ['#481736', 1],
      ]
    })
  }

  @Builder
  BackBuilder() {
    Row() {
      Image($r('app.media.ic_taobao_back'))
        .fillColor(Color.White)
        .width(30)
      Text('最近使用')
        .fontSize(20)
        .fontColor(Color.White)
    }
    .width('100%')
    .padding({ top: 40 })
  }

  @Builder
  SearchBuilder() {
    Stack() {
      Text()
        .width('100%')
        .height(40)
        .backgroundColor(Color.White)
        .opacity(.3)
        .borderRadius(20)
      Row({ space: 10 }) {
        Image($r('app.media.ic_taobao_search'))
          .width(25)
          .fillColor(Color.White)
        Text('搜索')
          .fontSize(15)
          .fontColor(Color.White)
      }
      .padding({ left: 10 })
      .width('100%')
    }
    .padding(10)
  }

  @Builder
  GridBuilder() {
    Grid() {
      ForEach(this.contentList, (item: TaoBaoItemContent, index: number) => {
        this.GridItemBuilder(item)
      })
    }
    .rowsTemplate('1fr 1fr 1fr 1fr 1fr') // 如果不写高度超出后会出现滚动
    .columnsTemplate('1fr 1fr 1fr 1fr 1fr')
    .width('100%')
    .height(360)
  }

  @Builder
  GridItemBuilder(item: TaoBaoItemContent) {
    GridItem() {
      Column({ space: 10 }) {
        Image($r(item.icon))
          .width(40)
          .borderRadius(20)
        Text(item.title)
          .fontColor(Color.White)
          .fontSize(14)
      }
    }
  }
}

3.3.合并行列

日常开发中很常见这种布局,由不同大小不均匀分的情况有很多,比如一些购物平台

image.png

row '+' Start/End : nember : 指定当前元素起始/终点行号。

column '+' Start/End : number : 指定当前元素起始/终点列号。

image.png

列子

@Entry
@Component
struct GridPage03 {
  nums: number[] = Array.from({ length: 12 })

  build() {
    Grid() {
      ForEach(this.nums, (item: number, index: number) => {
        if (index === 2) {
          GridItem() {
            Text(index + '')
              .fontColor(Color.White)
              .fontSize(30)
          }
          .backgroundColor('#9dc3e6')
          .columnStart(3)
          .columnEnd(4)
        } else if (index === 3) {
          GridItem() {
            Text(index + '')
              .fontColor(Color.White)
              .fontSize(30)
          }
          .backgroundColor('#9dc3e6')
          .rowStart(2)
          .rowEnd(3)
        } else {
          GridItem() {
            Text(index + '')
              .fontColor(Color.White)
              .fontSize(30)
          }
          .backgroundColor('#9dc3e6')
        }

      })
    }
    .columnsTemplate('1fr 1fr 1fr 1fr')
    .rowsTemplate('1fr 1fr 1fr')
    .width('100%')
    .height(260)
    .rowsGap(10)
    .columnsGap(10)
    .padding(10)
  }
}

3.4.设置滚动

日常开发中,可以滚动的网格布局也经常出现,比如文件管理、购物、视频列表

设置方式:

水平/垂直 滚动:设置的是rows/colums '+' Template,Grid的滚动方向为水平/垂直方向。

例子:

@Entry
@Component
struct Day01_09_Grid05 {
  // 长度为 10 每一项都为 undefined 的数组
  list: string[] = Array.from({ length: 30 })

  build() {
    Column() {
      Grid() {
        ForEach(this.list, (item: string, index) => {
          GridItem() {
            Text((index + 1).toString())
              .newExtend()
          }
          .padding(5)
          .height('30%')
          .width('25%')
        })

      }
      .columnsTemplate('1fr 1fr 1fr') // 竖向滚动
      // .rowsTemplate('1fr 1fr 1fr') // 横向滚动
      .rowsGap(10)
      .width('100%')
      .height(300)
      .border({ width: 1 })
      .padding(5)

    }
    .width('100%')
    .height('100%')
  }
}

@Extend(Text)
function newExtend() {
  .backgroundColor('#0094ff')
  .width('100%')
  .height('100%')
  .fontSize(30)
  .fontColor(Color.White)
  .textAlign(TextAlign.Center)
}

3.5.代码控制滚动

  1. 创建 Scroller 对象
  2. 设置给 Grid
  3. 调用 Scroller 对象的 scrollPage 方法
/*// 创建 Scroller 对象
scroller: Scroller = new Scroller()

// 设置给 Grid
 Grid(this.scroller) {
   // ...
 }

// 通过代码控制
this.scroller.scrollPage({
  next:true // 下一页
  next:false // 上一页
})*/


@Entry
@Component
struct GridDemo {
  nums: number[] = Array.from({ length: 200 })
  scroller: Scroller = new Scroller()

  build() {
    Column() {
      Grid(this.scroller) {
        ForEach(this.nums, (item: number, index: number) => {
          GridItem() {
            Text(index + 1 + '')
          }
          .backgroundColor('#0094ff')
          .width('25%')
        })
      }
      .padding(10)
      .height(450)
      .rowsGap(10)
      .columnsGap(10)
      .rowsTemplate('1fr 1fr 1fr 1fr')

      Row() {
        Button('上一页')
          .width(100)
          .onClick(() => {
            this.scroller.scrollPage({
              next: false
            })
          })
        Button('下一页')
          .width(100)
          .onClick(() => {
            this.scroller.scrollPage({
              next: true
            })
          })
      }
      .width('100%')
      .justifyContent(FlexAlign.SpaceAround)
    }
  }
}

3.6.自定义滚动条

如果默认的滚动条外观无法满足要求,我们还可以进行自定义

  1. 使用提供的属性调整(可调整属性有限)
  2. 使用 ScrollBar 组件自定义(可定制性高)

scrollBar:BarState : 设置滚动条状态。

默认值:BarState.auto

BarState.off 关闭

BarState.on 常驻

BarState.auto 按需显示

scrollBarColor : string | number | Color :设置滚动条的颜色。 scrollBarWidth :string | number : 设置滚动条的宽度。宽度设置后,滚动条正常状态和按压状态宽度均为滚动条的宽度值。

默认值:4

单位:vp

      .columnsTemplate('1fr 1fr 1fr') // 竖向滚动
      .rowsGap(10)
      .width('100%')
      .height(300)
      .border({ width: 1 })
      .padding(5)
      .scrollBarWidth(20) // 宽度
      .scrollBarColor(Color.Orange) // 滚颜色
      .scrollBar(BarState.Off) // 关闭

使用 ScrollBar 组件自定义 如果上一节实现的效果无法满足需求,那么可以使用 ScrollBar 组件进行自定义

scroller : Scroller : 是 :可滚动组件的控制器。用于与可滚动组件进行绑定。

direction :ScrollBarDirection : 否 : 滚动条的方向,控制可滚动组件对应方向的滚动。

默认值:ScrollBarDirection.Vertical

state : BarState : 否 :滚动条状态。

默认值:BarState.Auto

 @Entry
@Component
struct Day01_11_Grid07 {
  // 长度为 30 每一项都为 undefined 的数组
  list: string[] = Array.from({ length: 30 })
  scroller: Scroller = new Scroller()

  build() {
    Column() {
      Grid(this.scroller) {
        ForEach(this.list, (item: string, index) => {
          GridItem() {
            this.ItemBuilder(index)
          }
          .padding(5)
          .height('30%')
          .width('25%')
        })

      }
      .rowsTemplate('1fr 1fr 1fr') // 竖向滚动
      .rowsGap(10)
      .width('100%')
      .height(300)
      .border({ width: 1 })
      .padding(5)
      .scrollBar(BarState.Off) // 关闭

      // 自定义滚动条
      ScrollBar({
        scroller: this.scroller,
        direction: ScrollBarDirection.Horizontal
      }) {
        Text()
          .width(40)
          .height(20)
          .backgroundColor(Color.Orange)
      }
      .width(200)
      .height(20)
      .backgroundColor(Color.Red)
    }
    .width('100%')
    .height('100%')
  }

  @Builder
  ItemBuilder(index: number) {
    Text((index + 1).toString())
      .backgroundColor('#0094ff')
      .width('100%')
      .height('100%')
      .fontSize(30)
      .fontColor(Color.White)
      .textAlign(TextAlign.Center)
  }
}

4.总结

  1. Swiper 轮播图
    1. 自动轮播,无限循环
    2. 自定义导航点
  1. Grid:网格
    1. 均分行列,跨行,跨列
    2. 自定义滚动条
      1. 创建对象scroller
      2. scroller 关联给 Grid、ScrollBar
      3. 通过 ScrollBar 自定义滚动条外观
  1. 提取公共样式 结构
    1. @Extend,给特定元素扩展(可以传参,只能定义到全局)
    2. @Styles: 通用属性、事件(不能传参,本地,全局都可以定义)
    3. @Builder: 结构、样式、事件(可以传参,本地,全局都可以定义))