HarmonyOs 应用基础--模态转场-List 组件-AlphabetIndexer

目录

十一.模态转场、List 组件、AlphabetIndexer

1. 模态转场

1.1. 半模态转场

1.1.1. 核心用法

1.1.2. 显示状态双向绑定

1.1.3. 可选属性sheetOptions

 1.2. 案例-分享弹框

 1.2.1. 基础模版:

1.2.2. 参考代码:

 1.3. 全屏模态

2. List组件

 2.1. 基本使用

2.2. 常用属性

2.2.1. 滚动条状态

2.2.2. 分割线样式

 2.3. 分组展示

2.4. 案例-通讯录

 2.4.1. 需求 1-数据渲染

2.4.2. 需求2-随机颜色

2.5. 粘性标题

2.6. 控制滚动

2.7. List事件

3. AlphabetIndexer组件

3.1. 核心用法:

3.2. 外观设置

3.2.1. 设置文字外观

3.2.2. 弹窗提示

3.3. 事件

3.4. 案例-用户列表联动

 3.4.1. 基础模版:

 3.4.2. 参考代码:

4. 案例-贝壳找房

4.1. 全屏模态

4.2. 渲染历史和热门城市

4.3. 渲染城市列表

4.4. 渲染侧边导航

4.5. 城市及导航的联动效果


十一.模态转场、List 组件、AlphabetIndexer

今日核心:

  1. 容器组件:模态\半模态转场、list 组件进阶、AlphabetIndexer 组件

1. 模态转场

今天首先来学习模态转场,就是页面中弹出,全屏&半屏的弹框:

 

1.1. 半模态转场

1.1.1. 核心用法

半模态顾名思义,就是半屏的模态效果(图 2)

名称

参数

参数描述

bindSheet

isShow: boolean,

builder: CustomBuilder,

options?: SheetOptions

给组件绑定半模态页面,点击后显示模态页面。

isShow: 是否显示半模态页面。

从API version 10开始,该参数支持$$双向绑定变量

builder: 配置半模态页面内容。

options: 配置半模态页面的可选属性。

核心步骤:

  1. 定义状态变量,boolean
  2. 通过 Builder 定义结构
  3. 绑定半模态 bindSheet
  4. 修改状态变量,控制显示

 参考代码:

@Entry
@Component
struct Page01_bindSheet {
  // 1. 定义状态变量,boolean
  @State isShow: boolean = false

  build() {
    Column() {
      Text('半模态转场')
        .fontSize(20)
        .fontWeight(FontWeight.Bold)

      Button('显示半模态')
        // 3. 绑定半模态 bindSheet
        .bindSheet(this.isShow, this.sheetBuilder())
        .onClick(() => {
          // 4. 修改状态变量,控制显示
          this.isShow = true
        })
    }
    .width('100%')
    .height('100%')
    .padding(20)
  }

  // 2.通过 Builder 定义结构
  @Builder
  sheetBuilder() {
    Column() {

    }
    .backgroundColor(Color.Pink)
    .width('100%')
    .height('100%')
  }
}
1.1.2. 显示状态双向绑定

上一节的代码有个小问题,如果通过那个关闭按钮来关闭模态会出现无法开启的情况

官方给出了解决方案:

说明

在非双向绑定情况下,以拖拽方式关闭半模态页面不会改变isShow参数的值。

为了使isShow参数值与半模态界面的状态同步,建议使用$$双向绑定isShow参数。

HarmonyOs DevEco Studio小技巧7--双向绑定报错怎么解决_ecodev 怎么双向绑定类字段-CSDN博客

// 其余代码略
.bindSheet($$this.isShow, this.sheetBuilder())

名称

参数

参数描述

bindSheet

isShow: boolean,

builder: CustomBuilder,

options?: SheetOptions

给组件绑定半模态页面,点击后显示模态页面。

isShow: 是否显示半模态页面。

从API version 10开始,该参数支持$$双向绑定变量

builder: 配置半模态页面内容。

options: 配置半模态页面的可选属性。

 参考代码:

@Entry
@Component
struct Page02_bindSheet_TwoWay {
  // 1. 定义状态变量,boolean
  @State isShow: boolean = false

  build() {
    Column() {
      Text('半模态转场' + this.isShow)
        .fontSize(20)
        .fontWeight(FontWeight.Bold)

      Button('显示半模态')// 3. 绑定半模态 bindSheet
        .bindSheet($$this.isShow, this.sheetBuilder())
        .onClick(() => {
          // 4. 修改状态变量,控制显示
          this.isShow = true
        })
    }
    .width('100%')
    .height('100%')
    .padding(20)
  }

  // 2.通过 Builder 定义结构
  @Builder
  sheetBuilder() {
    Column() {

    }
    .backgroundColor(Color.Pink)
    .width('100%')
    .height('100%')
  }
}

双向绑定示意图:

  1. 半模态拖拽关闭,以及点击 x 关闭时均会修改isShow的值为 false

 

注意:

  • 有不少组件也支持双向绑定的组件,也可以使用语法$$进行进行绑定
1.1.3. 可选属性sheetOptions

通过第三个可选参数SheetOptions,可以对半模态的内容进行调整

其中常用属性已经标红

名称

类型

必填

描述

height

SheetSize

| Length

半模态高度,默认是LARGE。

说明:

底部弹窗竖屏时,当设置detents时,该属性设置无效。

dragBar

boolean

是否显示控制条,默认显示。

说明:

半模态面板的dentents属性设置多个不同高度并且设置生效时,默认显示控制条。否则不显示控制条。

showClose

boolean | Resource

是否显示关闭图标,默认显示。

detents

[(SheetSize | Length), ( SheetSize | Length)?, (SheetSize | Length)?]

半模态页面的切换高度档位。

说明:

底部弹窗竖屏生效,元组中第一个高度为初始高度。

。。。。剩余属性参考文档

试一试:

  • 效果 1:弹出半模态,高度固定
  • 效果 2:弹出半模态,通过控制条切换高度档位

 基础模版:

@Entry
@Component
struct Page01_bindSheet {
  @State isShow: boolean = true

  build() {
    Button('显示半模态')
      .bindSheet($$this.isShow, this.sheetBuilder())
  }

  @Builder
  sheetBuilder() {
    Text('我是弹出来的内容')
      .backgroundColor(Color.Pink)
  }
}

 参考代码:

@Entry
@Component
struct Page01_bindSheet {
  @State isShow: boolean = true

  build() {    
    Button('显示半模态')
      .bindSheet($$this.isShow, this.sheetBuilder(), {
        height: SheetSize.LARGE, // 设置弹底式窗口的高度为大
        // height: SheetSize.MEDIUM, // 设置弹底式窗口的高度为中
        // height: SheetSize.FIT_CONTENT, // 设置弹底式窗口的高度为根据内容自适应
        detents: [SheetSize.LARGE, SheetSize.MEDIUM, SheetSize.FIT_CONTENT], // 设置弹底式窗口的停靠点高度,包括大、中和根据内容自适应
        // 💥dragBar需要和档位detents配合使用, 否则不显示
        dragBar: false, // 禁用弹底式窗口的拖拽栏
        showClose: false // 禁用显示关闭按钮
      })


  }

  // 通过 Builder 定义结构
  @Builder
  sheetBuilder() {
    Text('我是弹出来的内容')
      .backgroundColor(Color.Pink)
  }
}

 1.2. 案例-分享弹框

 

需求:

  1. 将提供的默认结构提取到 Builder
  2. 设置模态框
    1. 高度为 适应内容
    2. 不使用半模态的关闭按钮
  1. 使用自己的 x 关闭半模态

核心步骤:

  1. 将提供的默认结构提取到 Builder 中
    1. 选中要提取的结构,右键
  1. 设置模态框
    1. 定义状态变量
    2. 绑定半模态 bindSheet,设置布尔值,builder,选项
    3. 选项:
      1. 高度适应内容
      2. 不显示默认关闭按钮
  1. 使用自己的 x 关闭半模态
    1. 绑定点击事件,修改状态变量
 1.2.1. 基础模版:
@Entry
@Component
struct Page04_bindSheetDemo_Share {
  // 半模态 内部图片 Builder
  @Builder
  itemBuilder(src: ResourceStr, title: string) {
    Column() {
      Image(src)
        .width(50)
        .height(50)
      Text(title)
        .fontSize(14)
        .margin({ top: 10 })
    }
  }

  build() {
    Column() {
      Button(`显示分享半模态`)
        .fontSize(20)

      // 半模态的内容
      Column() {
        // 顶部区域
        Stack({ alignContent: Alignment.End }) {
          Text('分享给好友')
            .width('100%')
            .textAlign(TextAlign.Center)
            .fontSize(20)
          // 自定义的关闭
          Image($r('app.media.ic_public_cancel'))
            .width(25)
            .fillColor('#c0c0c0')
        }
        .width('100%')

        // 底部的列表
        Row() {
          this.itemBuilder($r('app.media.ic_share_sina'), '微信')
          this.itemBuilder($r('app.media.ic_share_url'), '朋友圈')
          this.itemBuilder($r('app.media.ic_share_wechat'), '微博')
          this.itemBuilder($r('app.media.ic_share_pyq'), '复制链接')
        }
        .width('100%')
        .margin({ top: 30 })
        .justifyContent(FlexAlign.SpaceAround)
      }
      .padding(15)

    }
    .justifyContent(FlexAlign.Center)
    .width('100%')
    .height('100%')
  }
}
1.2.2. 参考代码:
@Entry
@Component
struct Page04_bindSheetDemo_Share {
  @State isShow: boolean = false

  // 半模态 内部图片 Builder
  @Builder
  itemBuilder(src: ResourceStr, title: string) {
    Column() {
      Image(src)
        .width(50)
        .height(50)
      Text(title)
        .fontSize(14)
        .margin({ top: 10 })
    }
  }

  build() {
    Column() {
      Button(`显示分享半模态`)
        .fontSize(20)
        .bindSheet($$this.isShow, this.sheetBuilder(), {
          height: SheetSize.FIT_CONTENT, // 适应内容高度
          showClose: false // 不显示默认的关闭按钮
        })
        .onClick(() => {
          this.isShow = true
        })


    }
    .justifyContent(FlexAlign.Center)
    .width('100%')
    .height('100%')
  }

  @Builder
  sheetBuilder() {
    // 半模态的内容
    Column() {
      // 顶部区域
      Stack({ alignContent: Alignment.End }) {
        Text('分享给好友')
          .width('100%')
          .textAlign(TextAlign.Center)
          .fontSize(20)
        // 自定义的关闭
        Image($r('app.media.ic_public_cancel'))
          .width(25)
          .fillColor('#c0c0c0')
          .onClick(() => {
            this.isShow = false
          })
      }
      .width('100%')

      // 底部的列表
      Row() {
        this.itemBuilder($r('app.media.ic_share_sina'), '微信')
        this.itemBuilder($r('app.media.ic_share_url'), '朋友圈')
        this.itemBuilder($r('app.media.ic_share_wechat'), '微博')
        this.itemBuilder($r('app.media.ic_share_pyq'), '复制链接')
      }
      .width('100%')
      .margin({ top: 30 })
      .justifyContent(FlexAlign.SpaceAround)
    }
    .padding(15)
  }
}

注意:

  1. 半模态中的图片如果不设置高度,默认不会加载,导致半模态默认不显示图片
  2. 解决方法:给图片或图片的容器加个高度即可

 1.3. 全屏模态

 

名称

参数

参数描述

bindContentCover

isShow: boolean,

builder: CustomBuilder,

options?: ContentCoverOptions

给组件绑定全屏模态页面,点击后显示模态页面。模态页面内容自定义,显示方式可设置无动画过渡,上下切换过渡以及透明渐变过渡方式。

isShow: 是否显示全屏模态页面。

从API version 10开始,该参数支持$$双向绑定变量

builder: 配置全屏模态页面内容。

options: 配置全屏模态页面的可选属性(详细可以参考文档,用的不多)

核心步骤:

  1. 定义状态变量,boolean
  2. 通过 Builder 定义结构
  3. 绑定全屏模态 bindContentCover
  4. 修改状态变量,控制显示
@Entry
@Component
struct Page05_bindContentCover {
  // 1. 定义状态变量,boolean
  @State isShow: boolean = false

  // 2.通过 Builder 定义结构
  @Builder
  myBuilder() {
    Column() {
      Button('关闭')
        .onClick(() => {
          this.isShow = false
        })
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#0094ff')
    .justifyContent(FlexAlign.Center)
  }

  build() {
    Column() {
      Button(`显示全屏模态${this.isShow}`)
        .fontSize(20)
        .margin(10)// 绑定全屏模态 bindContentCover
        .bindContentCover(this.isShow, this.myBuilder())
        .onClick(() => {
          // 修改状态变量,控制显示
          this.isShow = true
        })
    }
    .justifyContent(FlexAlign.Center)
    .width('100%')
    .height('100%')
  }
}

2. List组件

List 组件的基本用法,可以用它来展示列表,并且实现列表滚动,日常开发的时候还可以用它来实现更为复杂的效果。

链接

列表是一种复杂的容器,当列表项达到一定数量,超过List容器组件大小时,可以自动提供滚动功能。它适合用于呈现同类数据类型或数据类型集,例如图片和文本。在列表中显示数据集合是许多应用程序中的常见要求(如通讯录、音乐列表、购物清单等)。

List 表示列表容器,ListItem表示单个列表项,可以包含单个子组件。

 2.1. 基本使用

List() {
	listItem() {}
  .....
}

 示例代码

@Entry
@Component
struct Index {
  build() {
    Column() {
      List() {
        
        ListItem() {
          Text('文字')
            .width("100%")
            .height(40)
            .backgroundColor(Color.Pink)
        }        
  
      }
        .width('100%')
        .height(200)
        .backgroundColor('#ccc')
    }
  }
}

2.2. 常用属性

2.2.1. 滚动条状态

属性:scrollBar()

参数:枚举 BarState

  • Off: 不显示
  • On:常驻显示
  • Auto:按需显示(触摸时显示,2s 后消失)
List() {
  listItem()
  ......
}
	.scrollBar(BarState.Off)
2.2.2. 分割线样式

列表默认没有分割线

属性:divider()

参数:{ strokeWidth: 数字, color?: 'color', startMargin?: 数字, endMargin?: 数字 }

List() {
  ListItem()
  ......
}
  .divider({
    strokeWidth: 1,        // 设置分割线的宽度为1
    color: Color.Orange,    // 设置分割线的颜色为橙色
    startMargin: 10,        // 设置分割线起点的边距为10
    endMargin: 10           // 设置分割线终点的边距为10
  })

 2.3. 分组展示

 

核心用法:

  1. List作为顶级容器
  2. ListItemGroup 作为分组容器
  3. ListItem作为 List 或者ListItemGroup的子组件

ListItemGroup组件参数,以对象形式传入

ListItemGroup(参数){}.属性()

参数名

参数类型

必填

参数描述

header

CustomBuilder

设置ListItemGroup头部组件。

footer

CustomBuilder

设置ListItemGroup尾部组件。

space

number | string

列表项间距。只作用于ListItem与ListItem之间,不作用于header与ListItem、footer与ListItem之间。

ListItemGroup组件属性,和 List 是一样的

名称

参数类型

描述

divider

{

strokeWidth: Length,

color?: ResourceColor,

startMargin?: Length,

endMargin?: Length

} | null

用于设置ListItem分割线样式,默认无分割线。

strokeWidth: 分割线的线宽。

color: 分割线的颜色。

startMargin: 分割线距离列表侧边起始端的距离。

endMargin: 分割线距离列表侧边结束端的距离。

strokeWidth, startMargin和endMargin不支持设置百分比。

@Entry
@Component
struct ListItemGroup_01 {
  @Builder
  headerBuilder() {
    Text('我是头部')
  }

  @Builder
  footerBuilder() {
    Text('我是底部')
  }

  build() {
    List() {
      ListItemGroup({
        header: this.headerBuilder(),
        footer: this.footerBuilder(),
        space: 20
      }) {
        ListItem() {
          Text('我是内容')
            .backgroundColor(Color.Orange)
        }

        ListItem() {
          Text('我是内容')
            .backgroundColor(Color.Orange)
        }
      }
      .divider({ strokeWidth: 1, color: Color.Orange,startMargin:40 })
    }
  }
}

试一试:

  1. 测试参数及属性的使用
  2. 实现如下效果

 

参考代码: 

@Entry
@Component
struct ContactsList {
  build() {
    List() {
      ListItemGroup({ header: this.itemHead('A'), space: 20 }) {
        // 循环渲染分组A的ListItem
        this.contactBuilder('艾佳')
        this.contactBuilder('安安')
        this.contactBuilder('艾米丽')
      }
      .divider({
        startMargin: 60,
        strokeWidth: 1,
        color: '#ccc'
      })

      ListItemGroup({ header: this.itemHead('B'), space: 20 }) {
        // 循环渲染分组B的ListItem
        this.contactBuilder('白客')
        this.contactBuilder('白夜')
        this.contactBuilder('博明')
      }
      .divider({
        startMargin: 60,
        strokeWidth: 1,
        color: '#ccc'
      })
    }

  }

  @Builder
  itemHead(text: string) {
    // 列表分组的头部组件,对应联系人分组A、B等位置的组件
    Text(text)
      .fontSize(20)
      .backgroundColor('#fff1f3f5')
      .width('100%')
      .padding(5)
  }

  @Builder
  contactBuilder(name: string) {
    ListItem() {
      Row({ space: 10 }) {
        Image($r('app.media.ic_public_lianxiren'))
          .width(40)
          .fillColor('#e4b99a')
        Text(name)
      }
    }
  }
}

2.4. 案例-通讯录

List 组件一般基于数据渲染出列表,咱们基于数据来渲染出完整的列表,并且设置随机头像颜色

需求:

  1. 基于数据渲染列表
  2. 随机头像颜色
 2.4.1. 需求 1-数据渲染

思考:

  1. 数据长什么样子?

 基础模版:

interface ContactContent {
  initial: string
  nameList: string[]
}

@Entry
@Component
struct Page07_ListDemo_Contact {
  contacts: ContactContent[] = [
    { initial: 'A', nameList: ['阿猫', '阿狗', '阿虎', '阿龙', '阿鹰', '阿狼', '阿豹', '阿狮', '阿象', '阿鲸'] },
    { initial: 'B', nameList: ['白兔', '白鸽', '白鹤', '白鹭', '白狐', '白狼', '白虎', '白鹿', '白蛇', '白马'] },
    { initial: 'C', nameList: ['春花', '春风', '春雨', '春草', '春柳', '春燕', '春莺', '春蝶', '春蓝', '春绿'] },
    { initial: 'D', nameList: ['冬雪', '冬梅', '冬松', '冬竹', '冬云', '冬霜', '冬月', '冬夜', '冬青', '冬红'] },
    { initial: 'E', nameList: ['饿狼', '饿虎', '饿鹰', '饿豹', '饿熊', '饿蛇', '饿鱼', '饿虾', '饿蟹', '饿蚌'] },
    { initial: 'F', nameList: ['飞鸟', '飞鱼', '飞虫', '飞蜂', '飞蝶', '飞蛾', '飞蝉', '飞蝗', '飞鼠', '飞猫'] },
    { initial: 'G', nameList: ['孤狼', '孤鹰', '孤虎', '孤豹', '孤蛇', '孤鲨', '孤鲸', '孤鹿', '孤雁', '孤鸿'] },
    { initial: 'H', nameList: ['海鸥', '海龟', '海豚', '海星', '海马', '海葵', '海参', '海胆', '海螺', '海贝'] },
    { initial: 'I', nameList: ['火焰', '火球', '火箭', '火山', '火车', '火柴', '火把', '火鸟'] },
    { initial: 'J', nameList: ['金鱼', '金狮', '金刚', '金鹿', '金蛇', '金鹰', '金豹', '金虎', '金狐', '金猫'] },
    { initial: 'K', nameList: ['孔雀', '恐龙', '开心', '开怀', '开朗', '开拓', '开口', '开花', '开眼', '开天'] },
    { initial: 'L', nameList: ['老虎', '老鹰', '老鼠', '老狼', '老狗', '老猫', '老熊', '老鹿', '老龟', '老蛇'] },
    { initial: 'M', nameList: ['玫瑰', '牡丹', '梅花', '茉莉', '木兰', '棉花', '蜜蜂', '蚂蚁', '马蜂', '蟒蛇'] },
    { initial: 'N', nameList: ['南山', '南极', '南海', '南京', '南阳', '南风', '南瓜', '南竹', '南花', '南鸟'] },
    {
      initial: 'O',
      nameList: ['熊猫', '欧鹭', '欧洲', '欧阳', '欧文', '欧若拉', '欧米茄', '欧罗巴', '欧菲莉亚', '欧瑞斯']
    },
    { initial: 'P', nameList: ['苹果', '葡萄', '琵琶', '枇杷', '菩提', '瓢虫', '瓢泼', '飘零', '飘渺', '飘飘然'] },
    { initial: 'Q', nameList: ['七喜', '强风', '奇迹', '乾坤', '奇才', '晴天', '青竹', '秋水', '轻舞', '清泉'] },
    { initial: 'R', nameList: ['瑞雪', '瑞兽', '瑞光', '瑞云', '瑞彩', '瑞气', '瑞香', '瑞草', '瑞莲', '瑞竹'] },
    { initial: 'S', nameList: ['三羊', '三狗', '三猫', '三鱼', '三角', '三鹿', '三鹰', '三蛇', '三狐', '三豹'] },
    { initial: 'T', nameList: ['太阳', '天空', '田园', '太极', '太湖', '天鹅', '太空', '天使', '坦克', '甜橙'] },
    { initial: 'U', nameList: ['乌鸦', '乌鹊', '乌鱼', '乌龟', '乌云', '乌梅', '乌木', '乌金', '乌黑', '乌青'] },
    { initial: 'V', nameList: ['五虎', '五狼', '五鹰', '五豹', '五熊', '五蛇', '五鲨', '五鲸', '五鹿', '五马'] },
    { initial: 'W', nameList: ['悟空', '微笑', '温暖', '无畏', '温柔', '舞蹈', '问心', '悟道', '未来', '文学'] },
    { initial: 'X', nameList: ['西风', '西洋', '西子', '西施', '西岳', '西湖', '西柚', '西竹', '西花', '西鸟'] },
    { initial: 'Y', nameList: ['夜猫', '夜鹰', '夜莺', '夜空', '夜色', '夜月', '夜影', '夜翼', '夜狐', '夜狼'] },
    { initial: 'Z', nameList: ['珍珠', '紫薇', '紫霞', '紫竹', '紫云', '紫燕', '紫鸢', '紫藤', '紫荆', '紫罗兰'] },
  ]

  build() {
    Column() {
      Stack({ alignContent: Alignment.End }) {
        Text('通讯录')
          .width('100%')
          .textAlign(TextAlign.Center)
          .fontSize(20)
        Image($r('app.media.ic_public_add'))
          .width(20)
      }
      .width('100%')
      .padding(15)
      .backgroundColor('#fff1f3f5')

      List() {
        // 顶部区域
        ListItem() {
          Row() {
            Image($r('app.media.ic_public_search'))
              .width(20)
              .fillColor(Color.Gray)
            Text('搜索')
              .fontColor(Color.Gray)
          }
          .backgroundColor(Color.White)
          .width('100%')
          .height(40)
          .borderRadius(5)
          .justifyContent(FlexAlign.Center)
        }
        .padding(10)
        .width('100%')
        .backgroundColor('#fff1f3f5')

        // 分组的联系人信息
        ListItemGroup({ header: this.itemHead('A'), space: 10 }) {
          // 循环渲染分组A的ListItem
          ListItem() {
            Row({ space: 10 }) {
              Image($r('app.media.ic_public_lianxiren'))
                .width(40)
                .fillColor('#0094ff')
              Text('昵称')
            }
          }
        }
        .divider({
          startMargin: 60,
          strokeWidth: 1,
          color: '#ccc'
        })
      }
    }

  }

  @Builder
  itemHead(text: string) {
    // 列表分组的头部组件,对应联系人分组A、B等位置的组件
    Text(text)
      .fontSize(20)
      .backgroundColor('#fff1f3f5')
      .width('100%')
      .padding(5)
  }
}

核心步骤:

  1. 基于数据渲染列表
    1. 看清楚数据,分两次渲染
    2. 外层循环,渲染ListItemGroup
    3. 内层循环,渲染 ListItem

 参考代码;

 

interface ContactContent {
  initial: string
  nameList: string[]
}

@Entry
@Component
struct Page07_ListDemo_Contact {
  contacts: ContactContent[] = [
    { initial: 'A', nameList: ['阿猫', '阿狗', '阿虎', '阿龙', '阿鹰', '阿狼', '阿豹', '阿狮', '阿象', '阿鲸'] },
    { initial: 'B', nameList: ['白兔', '白鸽', '白鹤', '白鹭', '白狐', '白狼', '白虎', '白鹿', '白蛇', '白马'] },
    { initial: 'C', nameList: ['春花', '春风', '春雨', '春草', '春柳', '春燕', '春莺', '春蝶', '春蓝', '春绿'] },
    { initial: 'D', nameList: ['冬雪', '冬梅', '冬松', '冬竹', '冬云', '冬霜', '冬月', '冬夜', '冬青', '冬红'] },
    { initial: 'E', nameList: ['饿狼', '饿虎', '饿鹰', '饿豹', '饿熊', '饿蛇', '饿鱼', '饿虾', '饿蟹', '饿蚌'] },
    { initial: 'F', nameList: ['飞鸟', '飞鱼', '飞虫', '飞蜂', '飞蝶', '飞蛾', '飞蝉', '飞蝗', '飞鼠', '飞猫'] },
    { initial: 'G', nameList: ['孤狼', '孤鹰', '孤虎', '孤豹', '孤蛇', '孤鲨', '孤鲸', '孤鹿', '孤雁', '孤鸿'] },
    { initial: 'H', nameList: ['海鸥', '海龟', '海豚', '海星', '海马', '海葵', '海参', '海胆', '海螺', '海贝'] },
    { initial: 'I', nameList: ['火焰', '火球', '火箭', '火山', '火车', '火柴', '火把', '火鸟'] },
    { initial: 'J', nameList: ['金鱼', '金狮', '金刚', '金鹿', '金蛇', '金鹰', '金豹', '金虎', '金狐', '金猫'] },
    { initial: 'K', nameList: ['孔雀', '恐龙', '开心', '开怀', '开朗', '开拓', '开口', '开花', '开眼', '开天'] },
    { initial: 'L', nameList: ['老虎', '老鹰', '老鼠', '老狼', '老狗', '老猫', '老熊', '老鹿', '老龟', '老蛇'] },
    { initial: 'M', nameList: ['玫瑰', '牡丹', '梅花', '茉莉', '木兰', '棉花', '蜜蜂', '蚂蚁', '马蜂', '蟒蛇'] },
    { initial: 'N', nameList: ['南山', '南极', '南海', '南京', '南阳', '南风', '南瓜', '南竹', '南花', '南鸟'] },
    {
      initial: 'O',
      nameList: ['熊猫', '欧鹭', '欧洲', '欧阳', '欧文', '欧若拉', '欧米茄', '欧罗巴', '欧菲莉亚', '欧瑞斯']
    },
    { initial: 'P', nameList: ['苹果', '葡萄', '琵琶', '枇杷', '菩提', '瓢虫', '瓢泼', '飘零', '飘渺', '飘飘然'] },
    { initial: 'Q', nameList: ['七喜', '强风', '奇迹', '乾坤', '奇才', '晴天', '青竹', '秋水', '轻舞', '清泉'] },
    { initial: 'R', nameList: ['瑞雪', '瑞兽', '瑞光', '瑞云', '瑞彩', '瑞气', '瑞香', '瑞草', '瑞莲', '瑞竹'] },
    { initial: 'S', nameList: ['三羊', '三狗', '三猫', '三鱼', '三角', '三鹿', '三鹰', '三蛇', '三狐', '三豹'] },
    { initial: 'T', nameList: ['太阳', '天空', '田园', '太极', '太湖', '天鹅', '太空', '天使', '坦克', '甜橙'] },
    { initial: 'U', nameList: ['乌鸦', '乌鹊', '乌鱼', '乌龟', '乌云', '乌梅', '乌木', '乌金', '乌黑', '乌青'] },
    { initial: 'V', nameList: ['五虎', '五狼', '五鹰', '五豹', '五熊', '五蛇', '五鲨', '五鲸', '五鹿', '五马'] },
    { initial: 'W', nameList: ['悟空', '微笑', '温暖', '无畏', '温柔', '舞蹈', '问心', '悟道', '未来', '文学'] },
    { initial: 'X', nameList: ['西风', '西洋', '西子', '西施', '西岳', '西湖', '西柚', '西竹', '西花', '西鸟'] },
    { initial: 'Y', nameList: ['夜猫', '夜鹰', '夜莺', '夜空', '夜色', '夜月', '夜影', '夜翼', '夜狐', '夜狼'] },
    { initial: 'Z', nameList: ['珍珠', '紫薇', '紫霞', '紫竹', '紫云', '紫燕', '紫鸢', '紫藤', '紫荆', '紫罗兰'] },
  ]

  build() {
    Column() {
      Stack({ alignContent: Alignment.End }) {
        Text('通讯录')
          .width('100%')
          .textAlign(TextAlign.Center)
          .fontSize(20)
        Image($r('app.media.ic_public_add'))
          .width(20)
      }
      .width('100%')
      .padding(15)
      .backgroundColor('#fff1f3f5')

      List() {
        // 顶部区域
        ListItem() {
          Row() {
            Image($r('app.media.ic_public_search'))
              .width(20)
              .fillColor(Color.Gray)
            Text('搜索')
              .fontColor(Color.Gray)
          }
          .backgroundColor(Color.White)
          .width('100%')
          .height(40)
          .borderRadius(5)
          .justifyContent(FlexAlign.Center)
        }
        .padding(10)
        .width('100%')
        .backgroundColor('#fff1f3f5')

        ForEach(this.contacts, (item: ContactContent, index: number) => {
          // 分组的联系人信息
          ListItemGroup({ header: this.itemHead(item.initial), space: 10 }) {
            ForEach(item.nameList, (it: string, i: number) => {
              // 循环渲染分组A的ListItem
              ListItem() {
                Row({ space: 10 }) {
                  Image($r('app.media.ic_public_lianxiren'))
                    .width(40)
                    .fillColor('#0094ff')
                  Text(it)
                }
              }
            })

          }
          .divider({
            startMargin: 60,
            strokeWidth: 1,
            color: '#ccc'
          })
        })

      }
    }

  }

  @Builder
  itemHead(text: string) {
    // 列表分组的头部组件,对应联系人分组A、B等位置的组件
    Text(text)
      .fontSize(20)
      .backgroundColor('#fff1f3f5')
      .width('100%')
      .padding(5)
  }
}
2.4.2. 需求2-随机颜色

目前的代码 每个联系人的头像颜色都是一样的,咱们来实现为每个头像设置随机颜色

需求:

getRandomColor(){
  // 逻辑略
  return `rgba(随机数,随机数,随机数,0.5)`
}

this.getRandomColor() 
// 返回随机颜色字符串,比如 rgba('随机数,随机数,随机数,0.5')
// 其中随机数的值为 0-255

参考答案: 

interface ContactContent {
  initial: string
  nameList: string[]
}

@Entry
@Component
struct Page07_ListDemo_Contact {
  contacts: ContactContent[] = [
    { initial: 'A', nameList: ['阿猫', '阿狗', '阿虎', '阿龙', '阿鹰', '阿狼', '阿豹', '阿狮', '阿象', '阿鲸'] },
    { initial: 'B', nameList: ['白兔', '白鸽', '白鹤', '白鹭', '白狐', '白狼', '白虎', '白鹿', '白蛇', '白马'] },
    { initial: 'C', nameList: ['春花', '春风', '春雨', '春草', '春柳', '春燕', '春莺', '春蝶', '春蓝', '春绿'] },
    { initial: 'D', nameList: ['冬雪', '冬梅', '冬松', '冬竹', '冬云', '冬霜', '冬月', '冬夜', '冬青', '冬红'] },
    { initial: 'E', nameList: ['饿狼', '饿虎', '饿鹰', '饿豹', '饿熊', '饿蛇', '饿鱼', '饿虾', '饿蟹', '饿蚌'] },
    { initial: 'F', nameList: ['飞鸟', '飞鱼', '飞虫', '飞蜂', '飞蝶', '飞蛾', '飞蝉', '飞蝗', '飞鼠', '飞猫'] },
    { initial: 'G', nameList: ['孤狼', '孤鹰', '孤虎', '孤豹', '孤蛇', '孤鲨', '孤鲸', '孤鹿', '孤雁', '孤鸿'] },
    { initial: 'H', nameList: ['海鸥', '海龟', '海豚', '海星', '海马', '海葵', '海参', '海胆', '海螺', '海贝'] },
    { initial: 'I', nameList: ['火焰', '火球', '火箭', '火山', '火车', '火柴', '火把', '火鸟'] },
    { initial: 'J', nameList: ['金鱼', '金狮', '金刚', '金鹿', '金蛇', '金鹰', '金豹', '金虎', '金狐', '金猫'] },
    { initial: 'K', nameList: ['孔雀', '恐龙', '开心', '开怀', '开朗', '开拓', '开口', '开花', '开眼', '开天'] },
    { initial: 'L', nameList: ['老虎', '老鹰', '老鼠', '老狼', '老狗', '老猫', '老熊', '老鹿', '老龟', '老蛇'] },
    { initial: 'M', nameList: ['玫瑰', '牡丹', '梅花', '茉莉', '木兰', '棉花', '蜜蜂', '蚂蚁', '马蜂', '蟒蛇'] },
    { initial: 'N', nameList: ['南山', '南极', '南海', '南京', '南阳', '南风', '南瓜', '南竹', '南花', '南鸟'] },
    {
      initial: 'O',
      nameList: ['熊猫', '欧鹭', '欧洲', '欧阳', '欧文', '欧若拉', '欧米茄', '欧罗巴', '欧菲莉亚', '欧瑞斯']
    },
    { initial: 'P', nameList: ['苹果', '葡萄', '琵琶', '枇杷', '菩提', '瓢虫', '瓢泼', '飘零', '飘渺', '飘飘然'] },
    { initial: 'Q', nameList: ['七喜', '强风', '奇迹', '乾坤', '奇才', '晴天', '青竹', '秋水', '轻舞', '清泉'] },
    { initial: 'R', nameList: ['瑞雪', '瑞兽', '瑞光', '瑞云', '瑞彩', '瑞气', '瑞香', '瑞草', '瑞莲', '瑞竹'] },
    { initial: 'S', nameList: ['三羊', '三狗', '三猫', '三鱼', '三角', '三鹿', '三鹰', '三蛇', '三狐', '三豹'] },
    { initial: 'T', nameList: ['太阳', '天空', '田园', '太极', '太湖', '天鹅', '太空', '天使', '坦克', '甜橙'] },
    { initial: 'U', nameList: ['乌鸦', '乌鹊', '乌鱼', '乌龟', '乌云', '乌梅', '乌木', '乌金', '乌黑', '乌青'] },
    { initial: 'V', nameList: ['五虎', '五狼', '五鹰', '五豹', '五熊', '五蛇', '五鲨', '五鲸', '五鹿', '五马'] },
    { initial: 'W', nameList: ['悟空', '微笑', '温暖', '无畏', '温柔', '舞蹈', '问心', '悟道', '未来', '文学'] },
    { initial: 'X', nameList: ['西风', '西洋', '西子', '西施', '西岳', '西湖', '西柚', '西竹', '西花', '西鸟'] },
    { initial: 'Y', nameList: ['夜猫', '夜鹰', '夜莺', '夜空', '夜色', '夜月', '夜影', '夜翼', '夜狐', '夜狼'] },
    { initial: 'Z', nameList: ['珍珠', '紫薇', '紫霞', '紫竹', '紫云', '紫燕', '紫鸢', '紫藤', '紫荆', '紫罗兰'] },
  ]

  // 随机颜色函数
  getRandomColor(): ResourceColor {
    // 生成 0-255 的随机数
    const r = Math.floor(Math.random() * 256);
    const g = Math.floor(Math.random() * 256);
    const b = Math.floor(Math.random() * 256);
    // 拼接成随机的颜色,半透明并返回
    return `rgba(${r}, ${g}, ${b}, 0.5)`;
  }

  build() {
    Column() {
      Stack({ alignContent: Alignment.End }) {
        Text('通讯录')
          .width('100%')
          .textAlign(TextAlign.Center)
          .fontSize(20)
        Image($r('app.media.ic_public_add'))
          .width(20)
      }
      .width('100%')
      .padding(15)
      .backgroundColor('#fff1f3f5')

      List() {
        // 顶部区域
        ListItem() {
          Row() {
            Image($r('app.media.ic_public_search'))
              .width(20)
              .fillColor(Color.Gray)
            Text('搜索')
              .fontColor(Color.Gray)
          }
          .backgroundColor(Color.White)
          .width('100%')
          .height(40)
          .borderRadius(5)
          .justifyContent(FlexAlign.Center)
        }
        .padding(10)
        .width('100%')
        .backgroundColor('#fff1f3f5')

        ForEach(this.contacts, (item: ContactContent, index: number) => {
          // 分组的联系人信息
          ListItemGroup({ header: this.itemHead(item.initial), space: 10 }) {
            ForEach(item.nameList, (it: string, i: number) => {
              // 循环渲染分组A的ListItem
              ListItem() {
                Row({ space: 10 }) {
                  Image($r('app.media.ic_public_lianxiren'))
                    .width(40)
                    .fillColor(this.getRandomColor())
                  Text(it)
                }
              }
            })

          }
          .divider({
            startMargin: 60,
            strokeWidth: 1,
            color: '#ccc'
          })
        })

      }
    }

  }

  @Builder
  itemHead(text: string) {
    // 列表分组的头部组件,对应联系人分组A、B等位置的组件
    Text(text)
      .fontSize(20)
      .backgroundColor('#fff1f3f5')
      .width('100%')
      .padding(5)
  }
}

2.5. 粘性标题

通过List组件的sticky属性,即可实现粘性标题

 

 

sticky

StickyStyle

配合ListItemGroup组件使用,设置ListItemGroup中header和footer是否要吸顶或吸底。

默认值:StickyStyle.None

该接口支持在ArkTS卡片中使用。

说明:

sticky属性可以设置为 StickyStyle.Header | StickyStyle.Footer 以同时支持header吸顶和footer吸底。

List(){
  
}
  // .sticky(StickyStyle.None) // 不吸附 默认值
  // .sticky(StickyStyle.Header) // 头部吸附
  // .sticky(StickyStyle. Footer) //  底部吸附,如果有的话

试一试:

  • 使用 sticky属性将上一节的案例设置 粘性标题

 

2.6. 控制滚动

如果列表很长,需要快速滚动到列表底部或返回列表顶部,就可以使用控制器来控制滚动

核心步骤:

  1. 创建控制器(Scroller)对象,不需要添加@State
  2. 设置给 List 组件
  3. 调用控制器对象的方法,实现滚动
// 1. 创建控制器(ListScroller)对象
scroller: Scroller = new Scroller()
// 2. 设置给 List 组件
List({ space: 20, scroller: this.scroller }) {
  // ...
}


Button() {
  // ...
}
.onClick(() => {
  // 3. 调用控制器对象的方法,实现滚动
  this.scroller.scrollToIndex(0)
})

scrollToIndex方法参数:

参数名

参数类型

必填

参数描述

index

number

要滑动到的目标元素所在的ListItemGroup在当前容器中的索引值。

smooth

boolean

设置滑动到列表项在列表中的索引值时是否有动效,true表示有动效,false表示没有动效。

默认值:false。

align

ScrollAlign

指定滑动到的元素与当前容器的对齐方式。

默认值:ScrollAlign.START。

试一试:

  1. 调整 案例-通讯录的代码,实现点击标题返回顶部

核心步骤:

  1. 创建控制器(ListScroller)对象,不需要添加@State
  2. 设置给 List 组件
  3. 调用控制器对象的方法,实现滚动

 参考代码:

interface ContactContent {
  initial: string
  nameList: string[]
}

@Entry
@Component
struct Page07_ListDemo_Contact {
  contacts: ContactContent[] = [
    { initial: 'A', nameList: ['阿猫', '阿狗', '阿虎', '阿龙', '阿鹰', '阿狼', '阿豹', '阿狮', '阿象', '阿鲸'] },
    { initial: 'B', nameList: ['白兔', '白鸽', '白鹤', '白鹭', '白狐', '白狼', '白虎', '白鹿', '白蛇', '白马'] },
    { initial: 'C', nameList: ['春花', '春风', '春雨', '春草', '春柳', '春燕', '春莺', '春蝶', '春蓝', '春绿'] },
    { initial: 'D', nameList: ['冬雪', '冬梅', '冬松', '冬竹', '冬云', '冬霜', '冬月', '冬夜', '冬青', '冬红'] },
    { initial: 'E', nameList: ['饿狼', '饿虎', '饿鹰', '饿豹', '饿熊', '饿蛇', '饿鱼', '饿虾', '饿蟹', '饿蚌'] },
    { initial: 'F', nameList: ['飞鸟', '飞鱼', '飞虫', '飞蜂', '飞蝶', '飞蛾', '飞蝉', '飞蝗', '飞鼠', '飞猫'] },
    { initial: 'G', nameList: ['孤狼', '孤鹰', '孤虎', '孤豹', '孤蛇', '孤鲨', '孤鲸', '孤鹿', '孤雁', '孤鸿'] },
    { initial: 'H', nameList: ['海鸥', '海龟', '海豚', '海星', '海马', '海葵', '海参', '海胆', '海螺', '海贝'] },
    { initial: 'I', nameList: ['火焰', '火球', '火箭', '火山', '火车', '火柴', '火把', '火鸟'] },
    { initial: 'J', nameList: ['金鱼', '金狮', '金刚', '金鹿', '金蛇', '金鹰', '金豹', '金虎', '金狐', '金猫'] },
    { initial: 'K', nameList: ['孔雀', '恐龙', '开心', '开怀', '开朗', '开拓', '开口', '开花', '开眼', '开天'] },
    { initial: 'L', nameList: ['老虎', '老鹰', '老鼠', '老狼', '老狗', '老猫', '老熊', '老鹿', '老龟', '老蛇'] },
    { initial: 'M', nameList: ['玫瑰', '牡丹', '梅花', '茉莉', '木兰', '棉花', '蜜蜂', '蚂蚁', '马蜂', '蟒蛇'] },
    { initial: 'N', nameList: ['南山', '南极', '南海', '南京', '南阳', '南风', '南瓜', '南竹', '南花', '南鸟'] },
    {
      initial: 'O',
      nameList: ['熊猫', '欧鹭', '欧洲', '欧阳', '欧文', '欧若拉', '欧米茄', '欧罗巴', '欧菲莉亚', '欧瑞斯']
    },
    { initial: 'P', nameList: ['苹果', '葡萄', '琵琶', '枇杷', '菩提', '瓢虫', '瓢泼', '飘零', '飘渺', '飘飘然'] },
    { initial: 'Q', nameList: ['七喜', '强风', '奇迹', '乾坤', '奇才', '晴天', '青竹', '秋水', '轻舞', '清泉'] },
    { initial: 'R', nameList: ['瑞雪', '瑞兽', '瑞光', '瑞云', '瑞彩', '瑞气', '瑞香', '瑞草', '瑞莲', '瑞竹'] },
    { initial: 'S', nameList: ['三羊', '三狗', '三猫', '三鱼', '三角', '三鹿', '三鹰', '三蛇', '三狐', '三豹'] },
    { initial: 'T', nameList: ['太阳', '天空', '田园', '太极', '太湖', '天鹅', '太空', '天使', '坦克', '甜橙'] },
    { initial: 'U', nameList: ['乌鸦', '乌鹊', '乌鱼', '乌龟', '乌云', '乌梅', '乌木', '乌金', '乌黑', '乌青'] },
    { initial: 'V', nameList: ['五虎', '五狼', '五鹰', '五豹', '五熊', '五蛇', '五鲨', '五鲸', '五鹿', '五马'] },
    { initial: 'W', nameList: ['悟空', '微笑', '温暖', '无畏', '温柔', '舞蹈', '问心', '悟道', '未来', '文学'] },
    { initial: 'X', nameList: ['西风', '西洋', '西子', '西施', '西岳', '西湖', '西柚', '西竹', '西花', '西鸟'] },
    { initial: 'Y', nameList: ['夜猫', '夜鹰', '夜莺', '夜空', '夜色', '夜月', '夜影', '夜翼', '夜狐', '夜狼'] },
    { initial: 'Z', nameList: ['珍珠', '紫薇', '紫霞', '紫竹', '紫云', '紫燕', '紫鸢', '紫藤', '紫荆', '紫罗兰'] },
  ]

  // 随机颜色函数
  getRandomColor(): ResourceColor {
    // 生成 0-255 的随机数
    const r = Math.floor(Math.random() * 256);
    const g = Math.floor(Math.random() * 256);
    const b = Math.floor(Math.random() * 256);
    // 拼接成随机的颜色,半透明并返回
    return `rgba(${r}, ${g}, ${b}, 0.5)`;
  }

  // 1.控制器对象
  scroller: Scroller = new Scroller()

  build() {
    Column() {
      Stack({ alignContent: Alignment.End }) {
        Text('通讯录')
          .width('100%')
          .textAlign(TextAlign.Center)
          .fontSize(20)
          .onClick(() => {
            // 3.调用方法控制滚动
            this.scroller.scrollToIndex(0, true)
          })
        Image($r('app.media.ic_public_add'))
          .width(20)
      }
      .width('100%')
      .padding(15)
      .backgroundColor('#fff1f3f5')

      // 2. 设置给 List 组件
      List({ scroller: this.scroller }) {
        // 顶部区域
        ListItem() {
          Row() {
            Image($r('app.media.ic_public_search'))
              .width(20)
              .fillColor(Color.Gray)
            Text('搜索')
              .fontColor(Color.Gray)
          }
          .backgroundColor(Color.White)
          .width('100%')
          .height(40)
          .borderRadius(5)
          .justifyContent(FlexAlign.Center)
        }
        .padding(10)
        .width('100%')
        .backgroundColor('#fff1f3f5')

        ForEach(this.contacts, (item: ContactContent, index: number) => {
          // 分组的联系人信息
          ListItemGroup({ header: this.itemHead(item.initial), space: 10 }) {
            ForEach(item.nameList, (it: string, i: number) => {
              // 循环渲染分组A的ListItem
              ListItem() {
                Row({ space: 10 }) {
                  Image($r('app.media.ic_public_lianxiren'))
                    .width(40)
                    .fillColor(this.getRandomColor())
                  Text(it)
                }
              }
            })

          }
          .divider({
            startMargin: 60,
            strokeWidth: 1,
            color: '#ccc'
          })
        })

      }
    }

  }

  @Builder
  itemHead(text: string) {
    // 列表分组的头部组件,对应联系人分组A、B等位置的组件
    Text(text)
      .fontSize(20)
      .backgroundColor('#fff1f3f5')
      .width('100%')
      .padding(5)
  }
}

2.7. List事件

List 组件提供了很多事件供咱们使用,目前掌握一个即可onScrollIndex,其他的事件后续结合业务逻辑再进行补充

名称

功能描述

onScrollIndex(event: (start: number, end: number, center10+: number) => void)

有子组件划入或划出List显示区域时触发。从API version 10开始,List显示区域中间位置子组件变化时也会触发。

计算索引值时,ListItemGroup作为一个整体占一个索引值,不计算ListItemGroup内部ListItem的索引值。

- start: List显示区域内第一个子组件的索引值。

- end: List显示区域内最后一个子组件的索引值。

- center: List显示区域内中间位置子组件的索引值。

    List(){
      // ...
    }
    .onScrollIndex((index: number) => {
      console.log('index:', index)
    })

3. AlphabetIndexer组件

通过 AlphabetIndexer 组件可以与容器组件结合,实现导航联动,以及快速定位的效果

 

3.1. 核心用法:

AlphabetIndexer不是容器组件,属于功能类的组件,使用时,需要设置如下 2 个参数

参数名

参数类型

必填

参数描述

arrayValue

Array<string>

字母索引字符串数组,不可设置为空。

selected

number

初始选中项索引值,若超出索引值范围,则取默认值0。

从API version 10开始,该参数支持$$双向绑定变量。

 参考代码

@Entry
@Component
struct Page08_AlphabetIndexer {
  alphabets: string[] = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
    'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']
  @State selectedIndex: number = 0

  build() {
    Stack({ alignContent: Alignment.End }) {
      Text('选中的索引为:' + this.selectedIndex)
        .width('100%')
        .textAlign(TextAlign.Center)
        .onClick(() => {
          this.selectedIndex = 10
        })
      // 字母表索引组件
      // arrayValue 索引项
      // selected 选中索引 ,支持双向绑定
      AlphabetIndexer({ arrayValue: this.alphabets, selected: $$this.selectedIndex })

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

注意:

  • 通过双向绑定变量。可以实现修改变量值,同步更新选中的索引

3.2. 外观设置

3.2.1. 设置文字外观

选中AlphabetIndexer,如果默认的颜色效果无法满足要求,可以通过如下属性来进行设置

名称

参数类型

描述

color

ResourceColor

设置文字颜色。
默认值:0x99000000。

itemSize

number

设置每个字母的区域大小

font

Font

设置每个字母的字体样式

selectedFont

Font

设置选中字母的字体样式

selectedColor

ResourceColor

设置选中项文字颜色。
默认值:0xFF254FF7。

selectedBackgroundColor

ResourceColor

设置选中项背景颜色。
默认值:0x1F0A59F7。

@Entry
@Component
struct Page08_AlphabetIndexer {
  alphabets: string[] = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
    'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']
  @State selecteIndex: number = 0

  build() {
    Stack({ alignContent: Alignment.End }) {
      Text('选中的索引为:' + this.selecteIndex)
        .width('100%')
        .textAlign(TextAlign.Center)
        .onClick(() => {
          this.selecteIndex = 10
        })
      // 字母表索引组件
      // arrayValue 索引项
      // selected 选中索引 ,支持双向绑定
      AlphabetIndexer({ arrayValue: this.alphabets, selected: $$this.selecteIndex })
        .color(Color.Orange)// 文字颜色
        .selectedColor(Color.Green)// 选中文字颜色
        .selectedBackgroundColor(Color.Black) // 选中背景颜色

    }
    .width('100%')
    .height('100%')
  }
}
3.2.2. 弹窗提示

如果默认的文字高亮效果无法满足要求,还可以通过弹框来显示,对应属性如下

@Entry
@Component
struct Page08_AlphabetIndexer {
  alphabets: string[] = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
    'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']
  @State selecteIndex: number = 0

  build() {
    Stack({ alignContent: Alignment.End }) {
      Text('选中的索引为:' + this.selecteIndex)
        .width('100%')
        .textAlign(TextAlign.Center)
        .onClick(() => {
          this.selecteIndex = 10
        })
      // 字母表索引组件
      // arrayValue 索引项
      // selected 选中索引 ,支持双向绑定
      AlphabetIndexer({ arrayValue: this.alphabets, selected: $$this.selecteIndex })
        .color(Color.Orange)// 文字颜色
        .selectedColor(Color.Green)// 选中文字颜色
        .selectedBackgroundColor(Color.Black)// 选中背景颜色
        .usingPopup(true)// 显示弹窗
        .popupColor(Color.Orange)// 弹窗文字颜色
        .popupBackground(Color.Pink) // 弹窗背景色

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

3.3. 事件

支持的常用事件是onSelect,会在选中不同项时触发

名称

功能描述

onSelect(callback: (index: number) => void)8+

索引条选中回调,返回值为当前选中索引。

.... 其他事件后续用到再补充,目前掌握这一个即可

@Entry
@Component
struct Page08_AlphabetIndexer {
  alphabets: string[] = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
    'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']
  @State selectedIndex: number = 0

  build() {
    Stack({ alignContent: Alignment.End }) {
      Text('选中的索引为:' + this.selectedIndex)
        .width('100%')
        .textAlign(TextAlign.Center)
        .onClick(() => {
          this.selectedIndex = 10
        })
      // 字母表索引组件
      // arrayValue 索引项
      // selected 选中索引 ,支持双向绑定
      AlphabetIndexer({ arrayValue: this.alphabets, selected: $$this.selectedIndex })
        .color(Color.Orange)// 文字颜色
        .selectedColor(Color.Green)// 选中文字颜色
        .selectedBackgroundColor(Color.Black)// 选中背景颜色
        .usingPopup(true)// 显示弹窗
        .popupColor(Color.Orange)// 弹窗文字颜色
        .popupBackground(Color.Pink)// 弹窗背景色
        .onSelect((index: number) => {
          // this.selectedIndex = index
          console.log('选中的索引是:', index)
        })

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

3.4. 案例-用户列表联动

和上一节 List 的用户列表进行联动,实现选中切换的联动效果

需求:

  1. 整合 AlphabetIndexer 到联系人案例中
  2. 滚动 List,同步选中对应的AlphabetIndexer
  3. 选中AlphabetIndexer的区域,同步滚动List

核心步骤:

  1. 整合 AlphabetIndexer 到联系人案例中
    1. 整合数组,定义 状态变量 记录索引
    2. 整合AlphabetIndexer组件,设置 导航数组 和 索引
    3. 使用 Stack 作为容器,调整位置
  1. 滚动 List,同步选中对应的AlphabetIndexer
    1. List组件注册 onScrollIndex 事件,获取索引值
    2. 将上一步获取到的索引值设置给 记录索引的状态变量
  1. 选中AlphabetIndexer的区域,同步滚动List
    1. AlphabetIndexer组件注册onSelect事件,获取选中的索引值
    2. 调用 List 组件的控制器的 scrollToIndex方法,结合获取到的索引进行滚动
 3.4.1. 基础模版:
interface ContactData {
  initial: string
  nameList: string[]
}

@Entry
@Component
struct Page09_ContactAndAlpha {
  // contacts,alphabets 不需要修改,只是用来渲染,可以不添加@State
  contacts: ContactData[] = [
    { initial: 'A', nameList: ['阿猫', '阿狗', '阿虎', '阿龙', '阿鹰', '阿狼', '阿豹', '阿狮', '阿象', '阿鲸'] },
    { initial: 'B', nameList: ['白兔', '白鸽', '白鹤', '白鹭', '白狐', '白狼', '白虎', '白鹿', '白蛇', '白马'] },
    { initial: 'C', nameList: ['春花', '春风', '春雨', '春草', '春柳', '春燕', '春莺', '春蝶', '春蓝', '春绿'] },
    { initial: 'D', nameList: ['冬雪', '冬梅', '冬松', '冬竹', '冬云', '冬霜', '冬月', '冬夜', '冬青', '冬红'] },
    { initial: 'E', nameList: ['饿狼', '饿虎', '饿鹰', '饿豹', '饿熊', '饿蛇', '饿鱼', '饿虾', '饿蟹', '饿蚌'] },
    { initial: 'F', nameList: ['飞鸟', '飞鱼', '飞虫', '飞蜂', '飞蝶', '飞蛾', '飞蝉', '飞蝗', '飞鼠', '飞猫'] },
    { initial: 'G', nameList: ['孤狼', '孤鹰', '孤虎', '孤豹', '孤蛇', '孤鲨', '孤鲸', '孤鹿', '孤雁', '孤鸿'] },
    { initial: 'H', nameList: ['海鸥', '海龟', '海豚', '海星', '海马', '海葵', '海参', '海胆', '海螺', '海贝'] },
    { initial: 'I', nameList: ['火焰', '火球', '火箭', '火山', '火车', '火柴', '火把', '火鸟'] },
    { initial: 'J', nameList: ['金鱼', '金狮', '金刚', '金鹿', '金蛇', '金鹰', '金豹', '金虎', '金狐', '金猫'] },
    { initial: 'K', nameList: ['孔雀', '恐龙', '开心', '开怀', '开朗', '开拓', '开口', '开花', '开眼', '开天'] },
    { initial: 'L', nameList: ['老虎', '老鹰', '老鼠', '老狼', '老狗', '老猫', '老熊', '老鹿', '老龟', '老蛇'] },
    { initial: 'M', nameList: ['玫瑰', '牡丹', '梅花', '茉莉', '木兰', '棉花', '蜜蜂', '蚂蚁', '马蜂', '蟒蛇'] },
    { initial: 'N', nameList: ['南山', '南极', '南海', '南京', '南阳', '南风', '南瓜', '南竹', '南花', '南鸟'] },
    {
      initial: 'O',
      nameList: ['熊猫', '欧鹭', '欧洲', '欧阳', '欧文', '欧若拉', '欧米茄', '欧罗巴', '欧菲莉亚', '欧瑞斯']
    },
    { initial: 'P', nameList: ['苹果', '葡萄', '琵琶', '枇杷', '菩提', '瓢虫', '瓢泼', '飘零', '飘渺', '飘飘然'] },
    { initial: 'Q', nameList: ['七喜', '强风', '奇迹', '乾坤', '奇才', '晴天', '青竹', '秋水', '轻舞', '清泉'] },
    { initial: 'R', nameList: ['瑞雪', '瑞兽', '瑞光', '瑞云', '瑞彩', '瑞气', '瑞香', '瑞草', '瑞莲', '瑞竹'] },
    { initial: 'S', nameList: ['三羊', '三狗', '三猫', '三鱼', '三角', '三鹿', '三鹰', '三蛇', '三狐', '三豹'] },
    { initial: 'T', nameList: ['太阳', '天空', '田园', '太极', '太湖', '天鹅', '太空', '天使', '坦克', '甜橙'] },
    { initial: 'U', nameList: ['乌鸦', '乌鹊', '乌鱼', '乌龟', '乌云', '乌梅', '乌木', '乌金', '乌黑', '乌青'] },
    { initial: 'V', nameList: ['五虎', '五狼', '五鹰', '五豹', '五熊', '五蛇', '五鲨', '五鲸', '五鹿', '五马'] },
    { initial: 'W', nameList: ['悟空', '微笑', '温暖', '无畏', '温柔', '舞蹈', '问心', '悟道', '未来', '文学'] },
    { initial: 'X', nameList: ['西风', '西洋', '西子', '西施', '西岳', '西湖', '西柚', '西竹', '西花', '西鸟'] },
    { initial: 'Y', nameList: ['夜猫', '夜鹰', '夜莺', '夜空', '夜色', '夜月', '夜影', '夜翼', '夜狐', '夜狼'] },
    { initial: 'Z', nameList: ['珍珠', '紫薇', '紫霞', '紫竹', '紫云', '紫燕', '紫鸢', '紫藤', '紫荆', '紫罗兰'] },
  ]
  alphabets: string[] = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
    'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']

  // 随机颜色函数
  getRandomColor(): ResourceColor {
    // 生成 0-255 的随机数
    const r = Math.floor(Math.random() * 256);
    const g = Math.floor(Math.random() * 256);
    const b = Math.floor(Math.random() * 256);
    // 拼接成随机的颜色,半透明并返回
    return `rgba(${r}, ${g}, ${b}, 0.5)`;
  }

  // 控制器对象
  scroller: Scroller = new Scroller()

  build() {
    Column() {
      Stack({ alignContent: Alignment.End }) {
        Text('通讯录')
          .width('100%')
          .textAlign(TextAlign.Center)
          .fontSize(20)
          .onClick(() => {
            this.scroller.scrollToIndex(0, true)
          })
        Image($r('app.media.ic_public_add'))
          .width(20)
      }
      .width('100%')
      .padding(15)
      .backgroundColor('#fff1f3f5')

      Stack({ alignContent: Alignment.End }) {
        List({ scroller: this.scroller }) {
          // 顶部区域
          ListItem() {
            Row() {
              Image($r('app.media.ic_public_search'))
                .width(20)
                .fillColor(Color.Gray)
              Text('搜索')
                .fontColor(Color.Gray)
            }
            .backgroundColor(Color.White)
            .width('100%')
            .height(40)
            .borderRadius(5)
            .justifyContent(FlexAlign.Center)
          }
          .padding(10)
          .width('100%')
          .backgroundColor('#fff1f3f5')

          ForEach(this.contacts, (item: ContactData, index: number) => {
            // 分组的联系人信息
            ListItemGroup({ header: this.itemHead(item.initial), space: 10 }) {
              ForEach(item.nameList, (it: string, i: number) => {
                // 循环渲染分组A的ListItem
                ListItem() {
                  Row({ space: 10 }) {
                    Image($r('app.media.ic_public_lianxiren'))
                      .width(40)
                      .fillColor(this.getRandomColor())
                    Text(it)
                  }
                }
              })

            }
            .divider({
              startMargin: 60,
              strokeWidth: 1,
              color: '#ccc'
            })
          })

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

        AlphabetIndexer({ arrayValue: this.alphabets, selected: 0 })
      }
      .layoutWeight(1)

    }
  }

  @Builder
  itemHead(text: string) {
    // 列表分组的头部组件,对应联系人分组A、B等位置的组件
    Text(text)
      .fontSize(20)
      .backgroundColor('#fff1f3f5')
      .width('100%')
      .padding(5)
  }
}
 3.4.2. 参考代码:
interface ContactData {
  initial: string
  nameList: string[]
}

@Entry
@Component
struct Page09_ContactAndAlpha {
  // contacts,alphabets 不需要修改,只是用来渲染,可以不添加@State
  contacts: ContactData[] = [
    { initial: 'A', nameList: ['阿猫', '阿狗', '阿虎', '阿龙', '阿鹰', '阿狼', '阿豹', '阿狮', '阿象', '阿鲸'] },
    { initial: 'B', nameList: ['白兔', '白鸽', '白鹤', '白鹭', '白狐', '白狼', '白虎', '白鹿', '白蛇', '白马'] },
    { initial: 'C', nameList: ['春花', '春风', '春雨', '春草', '春柳', '春燕', '春莺', '春蝶', '春蓝', '春绿'] },
    { initial: 'D', nameList: ['冬雪', '冬梅', '冬松', '冬竹', '冬云', '冬霜', '冬月', '冬夜', '冬青', '冬红'] },
    { initial: 'E', nameList: ['饿狼', '饿虎', '饿鹰', '饿豹', '饿熊', '饿蛇', '饿鱼', '饿虾', '饿蟹', '饿蚌'] },
    { initial: 'F', nameList: ['飞鸟', '飞鱼', '飞虫', '飞蜂', '飞蝶', '飞蛾', '飞蝉', '飞蝗', '飞鼠', '飞猫'] },
    { initial: 'G', nameList: ['孤狼', '孤鹰', '孤虎', '孤豹', '孤蛇', '孤鲨', '孤鲸', '孤鹿', '孤雁', '孤鸿'] },
    { initial: 'H', nameList: ['海鸥', '海龟', '海豚', '海星', '海马', '海葵', '海参', '海胆', '海螺', '海贝'] },
    { initial: 'I', nameList: ['火焰', '火球', '火箭', '火山', '火车', '火柴', '火把', '火鸟'] },
    { initial: 'J', nameList: ['金鱼', '金狮', '金刚', '金鹿', '金蛇', '金鹰', '金豹', '金虎', '金狐', '金猫'] },
    { initial: 'K', nameList: ['孔雀', '恐龙', '开心', '开怀', '开朗', '开拓', '开口', '开花', '开眼', '开天'] },
    { initial: 'L', nameList: ['老虎', '老鹰', '老鼠', '老狼', '老狗', '老猫', '老熊', '老鹿', '老龟', '老蛇'] },
    { initial: 'M', nameList: ['玫瑰', '牡丹', '梅花', '茉莉', '木兰', '棉花', '蜜蜂', '蚂蚁', '马蜂', '蟒蛇'] },
    { initial: 'N', nameList: ['南山', '南极', '南海', '南京', '南阳', '南风', '南瓜', '南竹', '南花', '南鸟'] },
    {
      initial: 'O',
      nameList: ['熊猫', '欧鹭', '欧洲', '欧阳', '欧文', '欧若拉', '欧米茄', '欧罗巴', '欧菲莉亚', '欧瑞斯']
    },
    { initial: 'P', nameList: ['苹果', '葡萄', '琵琶', '枇杷', '菩提', '瓢虫', '瓢泼', '飘零', '飘渺', '飘飘然'] },
    { initial: 'Q', nameList: ['七喜', '强风', '奇迹', '乾坤', '奇才', '晴天', '青竹', '秋水', '轻舞', '清泉'] },
    { initial: 'R', nameList: ['瑞雪', '瑞兽', '瑞光', '瑞云', '瑞彩', '瑞气', '瑞香', '瑞草', '瑞莲', '瑞竹'] },
    { initial: 'S', nameList: ['三羊', '三狗', '三猫', '三鱼', '三角', '三鹿', '三鹰', '三蛇', '三狐', '三豹'] },
    { initial: 'T', nameList: ['太阳', '天空', '田园', '太极', '太湖', '天鹅', '太空', '天使', '坦克', '甜橙'] },
    { initial: 'U', nameList: ['乌鸦', '乌鹊', '乌鱼', '乌龟', '乌云', '乌梅', '乌木', '乌金', '乌黑', '乌青'] },
    { initial: 'V', nameList: ['五虎', '五狼', '五鹰', '五豹', '五熊', '五蛇', '五鲨', '五鲸', '五鹿', '五马'] },
    { initial: 'W', nameList: ['悟空', '微笑', '温暖', '无畏', '温柔', '舞蹈', '问心', '悟道', '未来', '文学'] },
    { initial: 'X', nameList: ['西风', '西洋', '西子', '西施', '西岳', '西湖', '西柚', '西竹', '西花', '西鸟'] },
    { initial: 'Y', nameList: ['夜猫', '夜鹰', '夜莺', '夜空', '夜色', '夜月', '夜影', '夜翼', '夜狐', '夜狼'] },
    { initial: 'Z', nameList: ['珍珠', '紫薇', '紫霞', '紫竹', '紫云', '紫燕', '紫鸢', '紫藤', '紫荆', '紫罗兰'] },
  ]
  alphabets: string[] = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
    'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']
  // 选中的索引,需要更改,且需要同步更新到 UI 上,故添加@State
  @State selectedIndex: number = 0

  // 随机颜色函数
  getRandomColor(): ResourceColor {
    // 生成 0-255 的随机数
    const r = Math.floor(Math.random() * 256);
    const g = Math.floor(Math.random() * 256);
    const b = Math.floor(Math.random() * 256);
    // 拼接成随机的颜色,半透明并返回
    return `rgba(${r}, ${g}, ${b}, 0.5)`;
  }

  // 控制器对象
  scroller: Scroller = new Scroller()

  build() {
    Stack({ alignContent: Alignment.End }) {
      Column() {
        Stack({ alignContent: Alignment.End }) {
          Text('通讯录')
            .width('100%')
            .textAlign(TextAlign.Center)
            .fontSize(20)
            .onClick(() => {
              this.scroller.scrollToIndex(0, true)
            })
          Image($r('app.media.ic_public_add'))
            .width(20)
        }
        .width('100%')
        .padding(15)
        .backgroundColor('#fff1f3f5')

        List({ scroller: this.scroller }) {
          // 顶部区域
          ListItem() {
            Row() {
              Image($r('app.media.ic_public_search'))
                .width(20)
                .fillColor(Color.Gray)
              Text('搜索')
                .fontColor(Color.Gray)
            }
            .backgroundColor(Color.White)
            .width('100%')
            .height(40)
            .borderRadius(5)
            .justifyContent(FlexAlign.Center)
          }
          .padding(10)
          .width('100%')
          .backgroundColor('#fff1f3f5')

          ForEach(this.contacts, (item: ContactData, index: number) => {
            // 分组的联系人信息
            ListItemGroup({ header: this.itemHead(item.initial), space: 10 }) {
              ForEach(item.nameList, (it: string, i: number) => {
                // 循环渲染分组A的ListItem
                ListItem() {
                  Row({ space: 10 }) {
                    Image($r('app.media.ic_public_lianxiren'))
                      .width(40)
                      .fillColor(this.getRandomColor())
                    Text(it)
                  }
                }
              })

            }
            .divider({
              startMargin: 60,
              strokeWidth: 1,
              color: '#ccc'
            })
          })

        }
        .onScrollIndex((start: number) => {
          this.selectedIndex = start
        })
      }

      AlphabetIndexer({ arrayValue: this.alphabets, selected: $$this.selectedIndex })
        .usingPopup(true)
        .onSelect((index: number) => {
          this.scroller.scrollToIndex(index)
        })
    }

  }

  @Builder
  itemHead(text: string) {
    // 列表分组的头部组件,对应联系人分组A、B等位置的组件
    Text(text)
      .fontSize(20)
      .backgroundColor('#fff1f3f5')
      .width('100%')
      .padding(5)
  }
}

4. 案例-贝壳找房

基于今天学习的内容完成,贝壳找房案例

 

 基础模版:

interface BKCityContent {
  initial: string
  cityNameList: string[]
}

@Entry
@Component
struct Page10_Demo_BK {
  // 热门城市
  hotCitys: string[] = ['北京', '上海', '广州', '深圳', '天津', '杭州', '南京', '苏州', '成都', '武汉', '重庆', '西安', '香港', '澳门', '台北']
  // 历史城市
  historyCitys: string[] = ['北京', '上海', '广州', '深圳', '重庆']
  // 城市信息
  cityContentList: BKCityContent[] = [
    {
      initial: 'A',
      cityNameList: ['阿拉善', '鞍山', '安庆', '安阳', '阿坝', '安顺']
    },
    {
      initial: 'B',
      cityNameList: ['北京', '保定', '包头', '巴彦淖尔', '本溪', '白山']
    },
    {
      initial: 'C',
      cityNameList: ['成都', '重庆', '长春', '长沙', '承德', '沧州']
    },
    {
      initial: 'D',
      cityNameList: ['大连', '东莞', '大同', '丹东', '大庆', '大兴安岭']
    },
    {
      initial: 'E',
      cityNameList: ['鄂尔多斯', '鄂州', '恩施', '额尔古纳市', '二连浩特市', '恩施市']
    },
    {
      initial: 'F',
      cityNameList: ['福州', '佛山', '抚顺', '阜新', '阜阳', '抚州']
    },
    {
      initial: 'G',
      cityNameList: ['广州', '贵阳', '赣州', '桂林', '贵港', '广元']
    },
    {
      initial: 'H',
      cityNameList: ['杭州', '海口', '哈尔滨', '合肥', '呼和浩特', '邯郸']
    },
    {
      initial: 'J',
      cityNameList: ['济南', '晋城', '晋中', '锦州', '吉林', '鸡西']
    },
    {
      initial: 'K',
      cityNameList: ['昆明', '开封', '康定市', '昆山', '康保县', '宽城满族自治县']
    },
    {
      initial: 'L',
      cityNameList: ['兰州', '廊坊', '临汾', '吕梁', '辽阳', '辽源']
    },
    {
      initial: 'M',
      cityNameList: ['牡丹江', '马鞍山', '茂名', '梅州', '绵阳', '眉山']
    },
    {
      initial: 'N',
      cityNameList: ['南京', '宁波', '南昌', '南宁', '南通', '南平']
    },
    {
      initial: 'P',
      cityNameList: ['盘锦', '莆田', '萍乡', '平顶山', '濮阳', '攀枝花']
    },
    {
      initial: 'Q',
      cityNameList: ['青岛', '秦皇岛', '齐齐哈尔', '七台河', '衢州', '泉州']
    },
    {
      initial: 'R',
      cityNameList: ['日照', '日喀则', '饶阳县', '任丘市', '任泽区', '饶河县']
    },
    {
      initial: 'S',
      cityNameList: ['上海', '苏州', '深圳', '沈阳', '石家庄', '朔州']
    },
    {
      initial: 'T',
      cityNameList: ['天津', '太原', '唐山', '通辽', '铁岭', '通化']
    },
    {
      initial: 'W',
      cityNameList: ['无锡', '武汉', '乌海', '乌兰察布', '温州', '芜湖']
    },
    {
      initial: 'X',
      cityNameList: ['厦门', '西安', '西宁', '邢台', '忻州', '兴安盟']
    },
    {
      initial: 'Y',
      cityNameList: ['扬州', '阳泉', '运城', '营口', '延边', '伊春']
    },
    {
      initial: 'Z',
      cityNameList: ['郑州', '珠海', '张家口', '镇江', '舟山', '漳州']
    }
  ]
  // 右侧导航索引
  alphabets: string[] = ['#', '热', "A", "B", "C", "D", "E", "F", "G", "H", "J", "K", "L", "M", "N", "P", "Q", "R", "S", "T", "W", "X", "Y", "Z"]

  build() {
    Column() {
      Image($r('app.media.ic_BK_content'))
        .width('100%')
      // 全屏模态的内容(测试用,完成之后删除)
      this.ContentCoverBuilder()
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#f8f8f8')
  }

  @Builder
  ContentCoverBuilder() {
    Stack({ alignContent: Alignment.End }) {
      Column() {
        // 顶部
        this.TopBuilder();
        // 列表
        this.ListBuilder();
      }
      .backgroundColor(Color.White)

      // 导航 写这里
      this.AlphabetBuilder()
    }
  }

  @Builder
  AlphabetBuilder() {
    Text()
      .width(20)
      .height(400)
      .backgroundColor(Color.Orange)
  }

  @Builder
  ListBuilder() {
    List({ space: 30 }) {
      // 历史
      this.LocationListItemBuilder()
      // 热门
      this.HotListItemBuilder()

      // A-B的区域
      this.LetterListItemBuilder()
    }
    .divider({ startMargin: 20, endMargin: 20, color: '#f3f3f3', strokeWidth: 2 })
    .width('100%')
    .layoutWeight(1)
    .sticky(StickyStyle.Header)

  }

  @Builder
  LetterListItemBuilder() {
    // A-B的区域
    ListItemGroup({ header: this.ListItemGroupHeaderBuilder('A') }) {
      ListItem() {
        Text('阿拉善')
          .width('100%')
          .padding({ left: 20 })
      }
      .width('100%')
      .height(50)
      .backgroundColor(Color.White)
    }
    .padding({ bottom: 20 })
    .divider({ startMargin: 20, endMargin: 20, color: '#f3f3f3', strokeWidth: 2 })
  }

  @Builder
  ListItemGroupHeaderBuilder(title: string) {
    Text(title)
      .padding({ left: 20, bottom: 15, top: 20 })
      .fontSize(14)
      .fontColor(Color.Gray)
      .backgroundColor('#f8f8f8')
      .width('100%')
  }

  @Builder
  HotListItemBuilder() {
    // 热门
    ListItem() {
      Column({ space: 10 }) {
        Text('热门城市')
          .alignSelf(ItemAlign.Start)
          .fontColor(Color.Gray)
          .fontSize(14)
        Flex({ wrap: FlexWrap.Wrap }) {
          Text('北京')
            .height(25)
            .backgroundColor(Color.White)
            .width('25%')
            .margin({ bottom: 10 })
        }
        .padding({ left: 20, right: 20 })
      }
      .width('100%')
      .padding({ left: 20, right: 20, bottom: 10 })
    }
  }

  @Builder
  LocationListItemBuilder() {
    ListItem() {
      Column({ space: 15 }) {
        // 定位地址
        Row() {
          Text('北京')
          Text() {
            ImageSpan($r('app.media.ic_public_location_fill_blue'))
              .width(20)
            Span('开启定位')
          }
        }
        .width('100%')
        .padding({ top: 10, bottom: 10, right: 20, left: 20 })
        .justifyContent(FlexAlign.SpaceBetween)
        .backgroundColor(Color.White)

        // 历史
        Column({ space: 10 }) {
          Text('历史')
            .fontColor(Color.Gray)
            .alignSelf(ItemAlign.Start)
            .fontSize(14)
          Flex({ wrap: FlexWrap.Wrap }) {
            Text('北京')
              .height(25)
              .backgroundColor(Color.White)
              .width('25%')
              .margin({ bottom: 10 })
          }
          .padding({ left: 20, right: 20 })
        }
        .width('100%')
        .padding({ left: 20, right: 20 })
      }
    }
    .padding({ top: 20 })
  }

  @Builder
  TopBuilder() {
    Column() {
      // X + 输入框
      Row({ space: 20 }) {
        Image($r('app.media.ic_public_cancel'))
          .width(30)
          .fillColor(Color.Gray)

        Row({ space: 5 }) {
          Image($r('app.media.ic_public_search'))
            .width(18)
          Text('请输入城市名称')
            .layoutWeight(1)
        }
        .height(50)
        .border({ width: .5, color: Color.Gray, radius: 5 })
        .padding({ left: 5 })
        .layoutWeight(1)
        .shadow({
          radius: 20,
          color: '#f6f6f7'
        })
      }
      .padding({
        left: 15,
        right: 15,
        top: 15
      })

      // 国内城市
      Column() {
        Text('国内城市')
          .fontSize(15)
          .fontWeight(800)
          .padding(5)
        Row()
          .width(20)
          .height(2)
          .backgroundColor('#0094ff')
          .borderRadius(2)
      }

    }
    .width('100%')
    .backgroundColor(Color.White)
    .height(100)
    .border({
      width: { bottom: 4 },
      color: '#f6f6f7',
    })
  }
}

4.1. 全屏模态

首先来完成全屏模态

需求:

  1. 点击图片展示全屏模态
  2. 点击 x 关闭全屏模态

核心步骤:

  1. 点击图片展示全屏模态
    1. 定义状态变量,控制全屏模态显示
    2. 给图片绑定全屏模态,设置状态变量,及 builder
    3. 点击图片,修改状态变量为 true
  1. 点击 x 关闭全屏模态
    1. 给 x 绑定点击事件,状态变量改为 false

 参考代码:

interface BKCityContent {
  initial: string
  cityNameList: string[]
}

@Entry
@Component
struct Page10_Demo_BK {
  // 热门城市
  hotCitys: string[] = ['北京', '上海', '广州', '深圳', '天津', '杭州', '南京', '苏州', '成都', '武汉', '重庆', '西安', '香港', '澳门', '台北']
  // 历史城市
  historyCitys: string[] = ['北京', '上海', '广州', '深圳', '重庆']
  // 城市信息
  cityContentList: BKCityContent[] = [
    {
      initial: 'A',
      cityNameList: ['阿拉善', '鞍山', '安庆', '安阳', '阿坝', '安顺']
    },
    {
      initial: 'B',
      cityNameList: ['北京', '保定', '包头', '巴彦淖尔', '本溪', '白山']
    },
    {
      initial: 'C',
      cityNameList: ['成都', '重庆', '长春', '长沙', '承德', '沧州']
    },
    {
      initial: 'D',
      cityNameList: ['大连', '东莞', '大同', '丹东', '大庆', '大兴安岭']
    },
    {
      initial: 'E',
      cityNameList: ['鄂尔多斯', '鄂州', '恩施', '额尔古纳市', '二连浩特市', '恩施市']
    },
    {
      initial: 'F',
      cityNameList: ['福州', '佛山', '抚顺', '阜新', '阜阳', '抚州']
    },
    {
      initial: 'G',
      cityNameList: ['广州', '贵阳', '赣州', '桂林', '贵港', '广元']
    },
    {
      initial: 'H',
      cityNameList: ['杭州', '海口', '哈尔滨', '合肥', '呼和浩特', '邯郸']
    },
    {
      initial: 'J',
      cityNameList: ['济南', '晋城', '晋中', '锦州', '吉林', '鸡西']
    },
    {
      initial: 'K',
      cityNameList: ['昆明', '开封', '康定市', '昆山', '康保县', '宽城满族自治县']
    },
    {
      initial: 'L',
      cityNameList: ['兰州', '廊坊', '临汾', '吕梁', '辽阳', '辽源']
    },
    {
      initial: 'M',
      cityNameList: ['牡丹江', '马鞍山', '茂名', '梅州', '绵阳', '眉山']
    },
    {
      initial: 'N',
      cityNameList: ['南京', '宁波', '南昌', '南宁', '南通', '南平']
    },
    {
      initial: 'P',
      cityNameList: ['盘锦', '莆田', '萍乡', '平顶山', '濮阳', '攀枝花']
    },
    {
      initial: 'Q',
      cityNameList: ['青岛', '秦皇岛', '齐齐哈尔', '七台河', '衢州', '泉州']
    },
    {
      initial: 'R',
      cityNameList: ['日照', '日喀则', '饶阳县', '任丘市', '任泽区', '饶河县']
    },
    {
      initial: 'S',
      cityNameList: ['上海', '苏州', '深圳', '沈阳', '石家庄', '朔州']
    },
    {
      initial: 'T',
      cityNameList: ['天津', '太原', '唐山', '通辽', '铁岭', '通化']
    },
    {
      initial: 'W',
      cityNameList: ['无锡', '武汉', '乌海', '乌兰察布', '温州', '芜湖']
    },
    {
      initial: 'X',
      cityNameList: ['厦门', '西安', '西宁', '邢台', '忻州', '兴安盟']
    },
    {
      initial: 'Y',
      cityNameList: ['扬州', '阳泉', '运城', '营口', '延边', '伊春']
    },
    {
      initial: 'Z',
      cityNameList: ['郑州', '珠海', '张家口', '镇江', '舟山', '漳州']
    }
  ]
  // 右侧导航索引
  alphabets: string[] = ['#', '热', "A", "B", "C", "D", "E", "F", "G", "H", "J", "K", "L", "M", "N", "P", "Q", "R", "S", "T", "W", "X", "Y", "Z"]
  // 控制全屏模态的状态变量
  @State isShow: boolean = false

  build() {
    Column() {
      Image($r('app.media.ic_BK_content'))
        .width('100%')
        // 绑定全屏模态
        .bindContentCover(this.isShow, this.ContentCoverBuilder())
        // 点击显示全屏模态
        .onClick(() => {
          this.isShow = true
        })
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#f8f8f8')
  }

  @Builder
  ContentCoverBuilder() {
    Stack({ alignContent: Alignment.End }) {
      Column() {
        // 顶部
        this.TopBuilder();
        // 列表
        this.ListBuilder();
      }
      .backgroundColor(Color.White)

      // 导航 写这里
      this.AlphabetBuilder()
    }
  }

  @Builder
  AlphabetBuilder() {
    Text()
      .width(20)
      .height(400)
      .backgroundColor(Color.Orange)
  }

  @Builder
  ListBuilder() {
    List({ space: 30 }) {
      // 历史
      this.LocationListItemBuilder()
      // 热门
      this.HotListItemBuilder()

      // A-B的区域
      this.LetterListItemBuilder()
    }
    .divider({ startMargin: 20, endMargin: 20, color: '#f3f3f3', strokeWidth: 2 })
    .width('100%')
    .layoutWeight(1)
    .sticky(StickyStyle.Header)

  }

  @Builder
  LetterListItemBuilder() {
    // A-B的区域
    ListItemGroup({ header: this.ListItemGroupHeaderBuilder('A') }) {
      ListItem() {
        Text('阿拉善')
          .width('100%')
          .padding({ left: 20 })
      }
      .width('100%')
      .height(50)
      .backgroundColor(Color.White)
    }
    .padding({ bottom: 20 })
    .divider({ startMargin: 20, endMargin: 20, color: '#f3f3f3', strokeWidth: 2 })
  }

  @Builder
  ListItemGroupHeaderBuilder(title: string) {
    Text(title)
      .padding({ left: 20, bottom: 15, top: 20 })
      .fontSize(14)
      .fontColor(Color.Gray)
      .backgroundColor('#f8f8f8')
      .width('100%')
  }

  @Builder
  HotListItemBuilder() {
    // 热门
    ListItem() {
      Column({ space: 10 }) {
        Text('热门城市')
          .alignSelf(ItemAlign.Start)
          .fontColor(Color.Gray)
          .fontSize(14)
        Flex({ wrap: FlexWrap.Wrap }) {
          Text('北京')
            .height(25)
            .backgroundColor(Color.White)
            .width('25%')
            .margin({ bottom: 10 })
        }
        .padding({ left: 20, right: 20 })
      }
      .width('100%')
      .padding({ left: 20, right: 20, bottom: 10 })
    }
  }

  @Builder
  LocationListItemBuilder() {
    ListItem() {
      Column({ space: 15 }) {
        // 定位地址
        Row() {
          Text('北京')
          Text() {
            ImageSpan($r('app.media.ic_public_location_fill_blue'))
              .width(20)
            Span('开启定位')
          }
        }
        .width('100%')
        .padding({ top: 10, bottom: 10, right: 20, left: 20 })
        .justifyContent(FlexAlign.SpaceBetween)
        .backgroundColor(Color.White)

        // 历史
        Column({ space: 10 }) {
          Text('历史')
            .fontColor(Color.Gray)
            .alignSelf(ItemAlign.Start)
            .fontSize(14)
          Flex({ wrap: FlexWrap.Wrap }) {
            Text('北京')
              .height(25)
              .backgroundColor(Color.White)
              .width('25%')
              .margin({ bottom: 10 })
          }
          .padding({ left: 20, right: 20 })
        }
        .width('100%')
        .padding({ left: 20, right: 20 })
      }
    }
    .padding({ top: 20 })
  }

  @Builder
  TopBuilder() {
    Column() {
      // X + 输入框
      Row({ space: 20 }) {
        Image($r('app.media.ic_public_cancel'))
          .width(30)
          .fillColor(Color.Gray)
          // 点击关闭全屏模态
          .onClick(() => {
            this.isShow = false
          })

        Row({ space: 5 }) {
          Image($r('app.media.ic_public_search'))
            .width(18)
          Text('请输入城市名称')
            .layoutWeight(1)
        }
        .height(50)
        .border({ width: .5, color: Color.Gray, radius: 5 })
        .padding({ left: 5 })
        .layoutWeight(1)
        .shadow({
          radius: 20,
          color: '#f6f6f7'
        })
      }
      .padding({
        left: 15,
        right: 15,
        top: 15
      })

      // 国内城市
      Column() {
        Text('国内城市')
          .fontSize(15)
          .fontWeight(800)
          .padding(5)
        Row()
          .width(20)
          .height(2)
          .backgroundColor('#0094ff')
          .borderRadius(2)
      }

    }
    .width('100%')
    .backgroundColor(Color.White)
    .height(100)
    .border({
      width: { bottom: 4 },
      color: '#f6f6f7',
    })
  }
}

4.2. 渲染历史和热门城市

接下来将历史和热门城市渲染出来

核心步骤:

  1. 确认数据及结构
  2. ForEach出结果
interface BKCityContent {
  initial: string
  cityNameList: string[]
}

@Entry
@Component
struct Page10_Demo_BK {
  // 热门城市
  hotCitys: string[] = ['北京', '上海', '广州', '深圳', '天津', '杭州', '南京', '苏州', '成都', '武汉', '重庆', '西安', '香港', '澳门', '台北']
  // 历史城市
  historyCitys: string[] = ['北京', '上海', '广州', '深圳', '重庆']
  // 城市信息
  cityContentList: BKCityContent[] = [
    {
      initial: 'A',
      cityNameList: ['阿拉善', '鞍山', '安庆', '安阳', '阿坝', '安顺']
    },
    {
      initial: 'B',
      cityNameList: ['北京', '保定', '包头', '巴彦淖尔', '本溪', '白山']
    },
    {
      initial: 'C',
      cityNameList: ['成都', '重庆', '长春', '长沙', '承德', '沧州']
    },
    {
      initial: 'D',
      cityNameList: ['大连', '东莞', '大同', '丹东', '大庆', '大兴安岭']
    },
    {
      initial: 'E',
      cityNameList: ['鄂尔多斯', '鄂州', '恩施', '额尔古纳市', '二连浩特市', '恩施市']
    },
    {
      initial: 'F',
      cityNameList: ['福州', '佛山', '抚顺', '阜新', '阜阳', '抚州']
    },
    {
      initial: 'G',
      cityNameList: ['广州', '贵阳', '赣州', '桂林', '贵港', '广元']
    },
    {
      initial: 'H',
      cityNameList: ['杭州', '海口', '哈尔滨', '合肥', '呼和浩特', '邯郸']
    },
    {
      initial: 'J',
      cityNameList: ['济南', '晋城', '晋中', '锦州', '吉林', '鸡西']
    },
    {
      initial: 'K',
      cityNameList: ['昆明', '开封', '康定市', '昆山', '康保县', '宽城满族自治县']
    },
    {
      initial: 'L',
      cityNameList: ['兰州', '廊坊', '临汾', '吕梁', '辽阳', '辽源']
    },
    {
      initial: 'M',
      cityNameList: ['牡丹江', '马鞍山', '茂名', '梅州', '绵阳', '眉山']
    },
    {
      initial: 'N',
      cityNameList: ['南京', '宁波', '南昌', '南宁', '南通', '南平']
    },
    {
      initial: 'P',
      cityNameList: ['盘锦', '莆田', '萍乡', '平顶山', '濮阳', '攀枝花']
    },
    {
      initial: 'Q',
      cityNameList: ['青岛', '秦皇岛', '齐齐哈尔', '七台河', '衢州', '泉州']
    },
    {
      initial: 'R',
      cityNameList: ['日照', '日喀则', '饶阳县', '任丘市', '任泽区', '饶河县']
    },
    {
      initial: 'S',
      cityNameList: ['上海', '苏州', '深圳', '沈阳', '石家庄', '朔州']
    },
    {
      initial: 'T',
      cityNameList: ['天津', '太原', '唐山', '通辽', '铁岭', '通化']
    },
    {
      initial: 'W',
      cityNameList: ['无锡', '武汉', '乌海', '乌兰察布', '温州', '芜湖']
    },
    {
      initial: 'X',
      cityNameList: ['厦门', '西安', '西宁', '邢台', '忻州', '兴安盟']
    },
    {
      initial: 'Y',
      cityNameList: ['扬州', '阳泉', '运城', '营口', '延边', '伊春']
    },
    {
      initial: 'Z',
      cityNameList: ['郑州', '珠海', '张家口', '镇江', '舟山', '漳州']
    }
  ]
  // 右侧导航索引
  alphabets: string[] = ['#', '热', "A", "B", "C", "D", "E", "F", "G", "H", "J", "K", "L", "M", "N", "P", "Q", "R", "S", "T", "W", "X", "Y", "Z"]
  // 控制全屏模态的状态变量
  @State isShow: boolean = false

  build() {
    Column() {
      Image($r('app.media.ic_BK_content'))
        .width('100%')// 绑定全屏模态
        .bindContentCover(this.isShow, this.ContentCoverBuilder())// 点击显示全屏模态
        .onClick(() => {
          this.isShow = true
        })
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#f8f8f8')
  }

  @Builder
  ContentCoverBuilder() {
    Stack({ alignContent: Alignment.End }) {
      Column() {
        // 顶部
        this.TopBuilder();
        // 列表
        this.ListBuilder();
      }
      .backgroundColor(Color.White)

      // 导航 写这里
      this.AlphabetBuilder()
    }
  }

  @Builder
  AlphabetBuilder() {
    Text()
      .width(20)
      .height(400)
      .backgroundColor(Color.Orange)
  }

  @Builder
  ListBuilder() {
    List({ space: 30 }) {
      // 历史
      this.LocationListItemBuilder()
      // 热门
      this.HotListItemBuilder()

      // A-B的区域
      this.LetterListItemBuilder()
    }
    .divider({ startMargin: 20, endMargin: 20, color: '#f3f3f3', strokeWidth: 2 })
    .width('100%')
    .layoutWeight(1)
    .sticky(StickyStyle.Header)

  }

  @Builder
  LetterListItemBuilder() {
    // A-B的区域
    ListItemGroup({ header: this.ListItemGroupHeaderBuilder('A') }) {
      ListItem() {
        Text('阿拉善')
          .width('100%')
          .padding({ left: 20 })
      }
      .width('100%')
      .height(50)
      .backgroundColor(Color.White)
    }
    .padding({ bottom: 20 })
    .divider({ startMargin: 20, endMargin: 20, color: '#f3f3f3', strokeWidth: 2 })
  }

  @Builder
  ListItemGroupHeaderBuilder(title: string) {
    Text(title)
      .padding({ left: 20, bottom: 15, top: 20 })
      .fontSize(14)
      .fontColor(Color.Gray)
      .backgroundColor('#f8f8f8')
      .width('100%')
  }

  @Builder
  HotListItemBuilder() {
    // 热门
    ListItem() {
      Column({ space: 10 }) {
        Text('热门城市')
          .alignSelf(ItemAlign.Start)
          .fontColor(Color.Gray)
          .fontSize(14)
        Flex({ wrap: FlexWrap.Wrap }) {
          ForEach(this.hotCitys, (item: string, index: number) => {
            Text(item)
              .height(25)
              .backgroundColor(Color.White)
              .width('25%')
              .margin({ bottom: 10 })
          })

        }
        .padding({ left: 20, right: 20 })
      }
      .width('100%')
      .padding({ left: 20, right: 20, bottom: 10 })
    }
  }

  @Builder
  LocationListItemBuilder() {
    ListItem() {
      Column({ space: 15 }) {
        // 定位地址
        Row() {
          Text('北京')
          Text() {
            ImageSpan($r('app.media.ic_public_location_fill_blue'))
              .width(20)
            Span('开启定位')
          }
        }
        .width('100%')
        .padding({ top: 10, bottom: 10, right: 20, left: 20 })
        .justifyContent(FlexAlign.SpaceBetween)
        .backgroundColor(Color.White)

        // 历史
        Column({ space: 10 }) {
          Text('历史')
            .fontColor(Color.Gray)
            .alignSelf(ItemAlign.Start)
            .fontSize(14)
          Flex({ wrap: FlexWrap.Wrap }) {
            ForEach(this.historyCitys, (item: string, index: number) => {
              Text(item)
                .height(25)
                .backgroundColor(Color.White)
                .width('25%')
                .margin({ bottom: 10 })
            })

          }
          .padding({ left: 20, right: 20 })
        }
        .width('100%')
        .padding({ left: 20, right: 20 })
      }
    }
    .padding({ top: 20 })
  }

  @Builder
  TopBuilder() {
    Column() {
      // X + 输入框
      Row({ space: 20 }) {
        Image($r('app.media.ic_public_cancel'))
          .width(30)
          .fillColor(Color.Gray)// 点击关闭全屏模态
          .onClick(() => {
            this.isShow = false
          })

        Row({ space: 5 }) {
          Image($r('app.media.ic_public_search'))
            .width(18)
          Text('请输入城市名称')
            .layoutWeight(1)
        }
        .height(50)
        .border({ width: .5, color: Color.Gray, radius: 5 })
        .padding({ left: 5 })
        .layoutWeight(1)
        .shadow({
          radius: 20,
          color: '#f6f6f7'
        })
      }
      .padding({
        left: 15,
        right: 15,
        top: 15
      })

      // 国内城市
      Column() {
        Text('国内城市')
          .fontSize(15)
          .fontWeight(800)
          .padding(5)
        Row()
          .width(20)
          .height(2)
          .backgroundColor('#0094ff')
          .borderRadius(2)
      }

    }
    .width('100%')
    .backgroundColor(Color.White)
    .height(100)
    .border({
      width: { bottom: 4 },
      color: '#f6f6f7',
    })
  }
}

4.3. 渲染城市列表

 

核心步骤:

  1. 明确数据结构
  2. 通过嵌套循环完成渲染

 参考代码:

 

interface BKCityContent {
  initial: string
  cityNameList: string[]
}

@Entry
@Component
struct Page10_Demo_BK {
  // 热门城市
  hotCitys: string[] = ['北京', '上海', '广州', '深圳', '天津', '杭州', '南京', '苏州', '成都', '武汉', '重庆', '西安', '香港', '澳门', '台北']
  // 历史城市
  historyCitys: string[] = ['北京', '上海', '广州', '深圳', '重庆']
  // 城市信息
  cityContentList: BKCityContent[] = [
    {
      initial: 'A',
      cityNameList: ['阿拉善', '鞍山', '安庆', '安阳', '阿坝', '安顺']
    },
    {
      initial: 'B',
      cityNameList: ['北京', '保定', '包头', '巴彦淖尔', '本溪', '白山']
    },
    {
      initial: 'C',
      cityNameList: ['成都', '重庆', '长春', '长沙', '承德', '沧州']
    },
    {
      initial: 'D',
      cityNameList: ['大连', '东莞', '大同', '丹东', '大庆', '大兴安岭']
    },
    {
      initial: 'E',
      cityNameList: ['鄂尔多斯', '鄂州', '恩施', '额尔古纳市', '二连浩特市', '恩施市']
    },
    {
      initial: 'F',
      cityNameList: ['福州', '佛山', '抚顺', '阜新', '阜阳', '抚州']
    },
    {
      initial: 'G',
      cityNameList: ['广州', '贵阳', '赣州', '桂林', '贵港', '广元']
    },
    {
      initial: 'H',
      cityNameList: ['杭州', '海口', '哈尔滨', '合肥', '呼和浩特', '邯郸']
    },
    {
      initial: 'J',
      cityNameList: ['济南', '晋城', '晋中', '锦州', '吉林', '鸡西']
    },
    {
      initial: 'K',
      cityNameList: ['昆明', '开封', '康定市', '昆山', '康保县', '宽城满族自治县']
    },
    {
      initial: 'L',
      cityNameList: ['兰州', '廊坊', '临汾', '吕梁', '辽阳', '辽源']
    },
    {
      initial: 'M',
      cityNameList: ['牡丹江', '马鞍山', '茂名', '梅州', '绵阳', '眉山']
    },
    {
      initial: 'N',
      cityNameList: ['南京', '宁波', '南昌', '南宁', '南通', '南平']
    },
    {
      initial: 'P',
      cityNameList: ['盘锦', '莆田', '萍乡', '平顶山', '濮阳', '攀枝花']
    },
    {
      initial: 'Q',
      cityNameList: ['青岛', '秦皇岛', '齐齐哈尔', '七台河', '衢州', '泉州']
    },
    {
      initial: 'R',
      cityNameList: ['日照', '日喀则', '饶阳县', '任丘市', '任泽区', '饶河县']
    },
    {
      initial: 'S',
      cityNameList: ['上海', '苏州', '深圳', '沈阳', '石家庄', '朔州']
    },
    {
      initial: 'T',
      cityNameList: ['天津', '太原', '唐山', '通辽', '铁岭', '通化']
    },
    {
      initial: 'W',
      cityNameList: ['无锡', '武汉', '乌海', '乌兰察布', '温州', '芜湖']
    },
    {
      initial: 'X',
      cityNameList: ['厦门', '西安', '西宁', '邢台', '忻州', '兴安盟']
    },
    {
      initial: 'Y',
      cityNameList: ['扬州', '阳泉', '运城', '营口', '延边', '伊春']
    },
    {
      initial: 'Z',
      cityNameList: ['郑州', '珠海', '张家口', '镇江', '舟山', '漳州']
    }
  ]
  // 右侧导航索引
  alphabets: string[] = ['#', '热', "A", "B", "C", "D", "E", "F", "G", "H", "J", "K", "L", "M", "N", "P", "Q", "R", "S", "T", "W", "X", "Y", "Z"]
  // 控制全屏模态的状态变量
  @State isShow: boolean = false

  build() {
    Column() {
      Image($r('app.media.ic_BK_content'))
        .width('100%')// 绑定全屏模态
        .bindContentCover(this.isShow, this.ContentCoverBuilder())// 点击显示全屏模态
        .onClick(() => {
          this.isShow = true
        })
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#f8f8f8')
  }

  @Builder
  ContentCoverBuilder() {
    Stack({ alignContent: Alignment.End }) {
      Column() {
        // 顶部
        this.TopBuilder();
        // 列表
        this.ListBuilder();
      }
      .backgroundColor(Color.White)

      // 导航 写这里
      this.AlphabetBuilder()
    }
  }

  @Builder
  AlphabetBuilder() {
    Text()
      .width(20)
      .height(400)
      .backgroundColor(Color.Orange)
  }

  @Builder
  ListBuilder() {
    List({ space: 30 }) {
      // 历史
      this.LocationListItemBuilder()
      // 热门
      this.HotListItemBuilder()

      // A-B的区域
      this.LetterListItemBuilder()
    }
    .divider({ startMargin: 20, endMargin: 20, color: '#f3f3f3', strokeWidth: 2 })
    .width('100%')
    .layoutWeight(1)
    .sticky(StickyStyle.Header)

  }

  @Builder
  LetterListItemBuilder() {
    // 外层循环
    ForEach(this.cityContentList, (item: BKCityContent, index: number) => {
      // A-B的区域
      ListItemGroup({ header: this.ListItemGroupHeaderBuilder(item.initial) }) {
        // 内层循环
        ForEach(item.cityNameList, (it: string, index: number) => {
          ListItem() {
            Text(it)
              .width('100%')
              .padding({ left: 20 })
          }
          .width('100%')
          .height(50)
          .backgroundColor(Color.White)
        })

      }
      .padding({ bottom: 20 })
      .divider({ startMargin: 20, endMargin: 20, color: '#f3f3f3', strokeWidth: 2 })
    })

  }

  @Builder
  ListItemGroupHeaderBuilder(title: string) {
    Text(title)
      .padding({ left: 20, bottom: 15, top: 20 })
      .fontSize(14)
      .fontColor(Color.Gray)
      .backgroundColor('#f8f8f8')
      .width('100%')
  }

  @Builder
  HotListItemBuilder() {
    // 热门
    ListItem() {
      Column({ space: 10 }) {
        Text('热门城市')
          .alignSelf(ItemAlign.Start)
          .fontColor(Color.Gray)
          .fontSize(14)
        Flex({ wrap: FlexWrap.Wrap }) {
          ForEach(this.hotCitys, (item: string, index: number) => {
            Text(item)
              .height(25)
              .backgroundColor(Color.White)
              .width('25%')
              .margin({ bottom: 10 })
          })

        }
        .padding({ left: 20, right: 20 })
      }
      .width('100%')
      .padding({ left: 20, right: 20, bottom: 10 })
    }
  }

  @Builder
  LocationListItemBuilder() {
    ListItem() {
      Column({ space: 15 }) {
        // 定位地址
        Row() {
          Text('北京')
          Text() {
            ImageSpan($r('app.media.ic_public_location_fill_blue'))
              .width(20)
            Span('开启定位')
          }
        }
        .width('100%')
        .padding({ top: 10, bottom: 10, right: 20, left: 20 })
        .justifyContent(FlexAlign.SpaceBetween)
        .backgroundColor(Color.White)

        // 历史
        Column({ space: 10 }) {
          Text('历史')
            .fontColor(Color.Gray)
            .alignSelf(ItemAlign.Start)
            .fontSize(14)
          Flex({ wrap: FlexWrap.Wrap }) {
            ForEach(this.historyCitys, (item: string, index: number) => {
              Text(item)
                .height(25)
                .backgroundColor(Color.White)
                .width('25%')
                .margin({ bottom: 10 })
            })

          }
          .padding({ left: 20, right: 20 })
        }
        .width('100%')
        .padding({ left: 20, right: 20 })
      }
    }
    .padding({ top: 20 })
  }

  @Builder
  TopBuilder() {
    Column() {
      // X + 输入框
      Row({ space: 20 }) {
        Image($r('app.media.ic_public_cancel'))
          .width(30)
          .fillColor(Color.Gray)// 点击关闭全屏模态
          .onClick(() => {
            this.isShow = false
          })

        Row({ space: 5 }) {
          Image($r('app.media.ic_public_search'))
            .width(18)
          Text('请输入城市名称')
            .layoutWeight(1)
        }
        .height(50)
        .border({ width: .5, color: Color.Gray, radius: 5 })
        .padding({ left: 5 })
        .layoutWeight(1)
        .shadow({
          radius: 20,
          color: '#f6f6f7'
        })
      }
      .padding({
        left: 15,
        right: 15,
        top: 15
      })

      // 国内城市
      Column() {
        Text('国内城市')
          .fontSize(15)
          .fontWeight(800)
          .padding(5)
        Row()
          .width(20)
          .height(2)
          .backgroundColor('#0094ff')
          .borderRadius(2)
      }

    }
    .width('100%')
    .backgroundColor(Color.White)
    .height(100)
    .border({
      width: { bottom: 4 },
      color: '#f6f6f7',
    })
  }
}

4.4. 渲染侧边导航

然后渲染侧边的 AlphabetIndexer 组件

核心步骤:

  1. 确认数据及结构
  2. 设置 AlphabetIndexer 组件并通过双向绑定关联索引
interface BKCityContent {
  initial: string
  cityNameList: string[]
}

@Entry
@Component
struct Page10_Demo_BK {
  // 热门城市
  hotCitys: string[] = ['北京', '上海', '广州', '深圳', '天津', '杭州', '南京', '苏州', '成都', '武汉', '重庆', '西安', '香港', '澳门', '台北']
  // 历史城市
  historyCitys: string[] = ['北京', '上海', '广州', '深圳', '重庆']
  // 城市信息
  cityContentList: BKCityContent[] = [
    {
      initial: 'A',
      cityNameList: ['阿拉善', '鞍山', '安庆', '安阳', '阿坝', '安顺']
    },
    {
      initial: 'B',
      cityNameList: ['北京', '保定', '包头', '巴彦淖尔', '本溪', '白山']
    },
    {
      initial: 'C',
      cityNameList: ['成都', '重庆', '长春', '长沙', '承德', '沧州']
    },
    {
      initial: 'D',
      cityNameList: ['大连', '东莞', '大同', '丹东', '大庆', '大兴安岭']
    },
    {
      initial: 'E',
      cityNameList: ['鄂尔多斯', '鄂州', '恩施', '额尔古纳市', '二连浩特市', '恩施市']
    },
    {
      initial: 'F',
      cityNameList: ['福州', '佛山', '抚顺', '阜新', '阜阳', '抚州']
    },
    {
      initial: 'G',
      cityNameList: ['广州', '贵阳', '赣州', '桂林', '贵港', '广元']
    },
    {
      initial: 'H',
      cityNameList: ['杭州', '海口', '哈尔滨', '合肥', '呼和浩特', '邯郸']
    },
    {
      initial: 'J',
      cityNameList: ['济南', '晋城', '晋中', '锦州', '吉林', '鸡西']
    },
    {
      initial: 'K',
      cityNameList: ['昆明', '开封', '康定市', '昆山', '康保县', '宽城满族自治县']
    },
    {
      initial: 'L',
      cityNameList: ['兰州', '廊坊', '临汾', '吕梁', '辽阳', '辽源']
    },
    {
      initial: 'M',
      cityNameList: ['牡丹江', '马鞍山', '茂名', '梅州', '绵阳', '眉山']
    },
    {
      initial: 'N',
      cityNameList: ['南京', '宁波', '南昌', '南宁', '南通', '南平']
    },
    {
      initial: 'P',
      cityNameList: ['盘锦', '莆田', '萍乡', '平顶山', '濮阳', '攀枝花']
    },
    {
      initial: 'Q',
      cityNameList: ['青岛', '秦皇岛', '齐齐哈尔', '七台河', '衢州', '泉州']
    },
    {
      initial: 'R',
      cityNameList: ['日照', '日喀则', '饶阳县', '任丘市', '任泽区', '饶河县']
    },
    {
      initial: 'S',
      cityNameList: ['上海', '苏州', '深圳', '沈阳', '石家庄', '朔州']
    },
    {
      initial: 'T',
      cityNameList: ['天津', '太原', '唐山', '通辽', '铁岭', '通化']
    },
    {
      initial: 'W',
      cityNameList: ['无锡', '武汉', '乌海', '乌兰察布', '温州', '芜湖']
    },
    {
      initial: 'X',
      cityNameList: ['厦门', '西安', '西宁', '邢台', '忻州', '兴安盟']
    },
    {
      initial: 'Y',
      cityNameList: ['扬州', '阳泉', '运城', '营口', '延边', '伊春']
    },
    {
      initial: 'Z',
      cityNameList: ['郑州', '珠海', '张家口', '镇江', '舟山', '漳州']
    }
  ]
  // 右侧导航索引
  alphabets: string[] = ['#', '热', "A", "B", "C", "D", "E", "F", "G", "H", "J", "K", "L", "M", "N", "P", "Q", "R", "S", "T", "W", "X", "Y", "Z"]
  // 控制全屏模态的状态变量
  @State isShow: boolean = false
  // 选中的索引
  @State selectedIndex: number = 0

  build() {
    Column() {
      Image($r('app.media.ic_BK_content'))
        .width('100%')// 绑定全屏模态
        .bindContentCover(this.isShow, this.ContentCoverBuilder())// 点击显示全屏模态
        .onClick(() => {
          this.isShow = true
        })
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#f8f8f8')
  }

  @Builder
  ContentCoverBuilder() {
    Stack({ alignContent: Alignment.End }) {
      Column() {
        // 顶部
        this.TopBuilder();
        // 列表
        this.ListBuilder();
      }
      .backgroundColor(Color.White)

      // 导航 写这里
      this.AlphabetBuilder()
    }
  }

  @Builder
  AlphabetBuilder() {
    // 添加导航组件
    AlphabetIndexer({ arrayValue: this.alphabets, selected: $$this.selectedIndex })
  }

  @Builder
  ListBuilder() {
    List({ space: 30 }) {
      // 历史
      this.LocationListItemBuilder()
      // 热门
      this.HotListItemBuilder()

      // A-B的区域
      this.LetterListItemBuilder()
    }
    .divider({ startMargin: 20, endMargin: 20, color: '#f3f3f3', strokeWidth: 2 })
    .width('100%')
    .layoutWeight(1)
    .sticky(StickyStyle.Header)

  }

  @Builder
  LetterListItemBuilder() {
    // 外层循环
    ForEach(this.cityContentList, (item: BKCityContent, index: number) => {
      // A-B的区域
      ListItemGroup({ header: this.ListItemGroupHeaderBuilder(item.initial) }) {
        // 内层循环
        ForEach(item.cityNameList, (it: string, index: number) => {
          ListItem() {
            Text(it)
              .width('100%')
              .padding({ left: 20 })
          }
          .width('100%')
          .height(50)
          .backgroundColor(Color.White)
        })

      }
      .padding({ bottom: 20 })
      .divider({ startMargin: 20, endMargin: 20, color: '#f3f3f3', strokeWidth: 2 })
    })

  }

  @Builder
  ListItemGroupHeaderBuilder(title: string) {
    Text(title)
      .padding({ left: 20, bottom: 15, top: 20 })
      .fontSize(14)
      .fontColor(Color.Gray)
      .backgroundColor('#f8f8f8')
      .width('100%')
  }

  @Builder
  HotListItemBuilder() {
    // 热门
    ListItem() {
      Column({ space: 10 }) {
        Text('热门城市')
          .alignSelf(ItemAlign.Start)
          .fontColor(Color.Gray)
          .fontSize(14)
        Flex({ wrap: FlexWrap.Wrap }) {
          ForEach(this.hotCitys, (item: string, index: number) => {
            Text(item)
              .height(25)
              .backgroundColor(Color.White)
              .width('25%')
              .margin({ bottom: 10 })
          })

        }
        .padding({ left: 20, right: 20 })
      }
      .width('100%')
      .padding({ left: 20, right: 20, bottom: 10 })
    }
  }

  @Builder
  LocationListItemBuilder() {
    ListItem() {
      Column({ space: 15 }) {
        // 定位地址
        Row() {
          Text('北京')
          Text() {
            ImageSpan($r('app.media.ic_public_location_fill_blue'))
              .width(20)
            Span('开启定位')
          }
        }
        .width('100%')
        .padding({ top: 10, bottom: 10, right: 20, left: 20 })
        .justifyContent(FlexAlign.SpaceBetween)
        .backgroundColor(Color.White)

        // 历史
        Column({ space: 10 }) {
          Text('历史')
            .fontColor(Color.Gray)
            .alignSelf(ItemAlign.Start)
            .fontSize(14)
          Flex({ wrap: FlexWrap.Wrap }) {
            ForEach(this.historyCitys, (item: string, index: number) => {
              Text(item)
                .height(25)
                .backgroundColor(Color.White)
                .width('25%')
                .margin({ bottom: 10 })
            })

          }
          .padding({ left: 20, right: 20 })
        }
        .width('100%')
        .padding({ left: 20, right: 20 })
      }
    }
    .padding({ top: 20 })
  }

  @Builder
  TopBuilder() {
    Column() {
      // X + 输入框
      Row({ space: 20 }) {
        Image($r('app.media.ic_public_cancel'))
          .width(30)
          .fillColor(Color.Gray)// 点击关闭全屏模态
          .onClick(() => {
            this.isShow = false
          })

        Row({ space: 5 }) {
          Image($r('app.media.ic_public_search'))
            .width(18)
          Text('请输入城市名称')
            .layoutWeight(1)
        }
        .height(50)
        .border({ width: .5, color: Color.Gray, radius: 5 })
        .padding({ left: 5 })
        .layoutWeight(1)
        .shadow({
          radius: 20,
          color: '#f6f6f7'
        })
      }
      .padding({
        left: 15,
        right: 15,
        top: 15
      })

      // 国内城市
      Column() {
        Text('国内城市')
          .fontSize(15)
          .fontWeight(800)
          .padding(5)
        Row()
          .width(20)
          .height(2)
          .backgroundColor('#0094ff')
          .borderRadius(2)
      }

    }
    .width('100%')
    .backgroundColor(Color.White)
    .height(100)
    .border({
      width: { bottom: 4 },
      color: '#f6f6f7',
    })
  }
}

4.5. 城市及导航的联动效果

最后完成联动效果

 

核心步骤:

  1. 滚动 List,同步选中对应的AlphabetIndexer
    1. List 组件注册 onScrollIndex 事件,事件中获取索引
    2. 将获取到的索引设置给 AlphabetIndexer 绑定的索引值
  1. 选中AlphabetIndexer的区域,同步滚动List
    1. 创建控制器对象
    2. 控制器对象设置给 List 组件
    3. AlphabetIndexer 组件添加 onSelect 事件
      1. 获取索引值
      2. 调用控制器对象的 scrollToIndex方法,结合索引滚动列表

 参考代码

interface BKCityContent {
  initial: string
  cityNameList: string[]
}

@Entry
@Component
struct Page10_Demo_BK {
  // 热门城市
  hotCitys: string[] = ['北京', '上海', '广州', '深圳', '天津', '杭州', '南京', '苏州', '成都', '武汉', '重庆', '西安', '香港', '澳门', '台北']
  // 历史城市
  historyCitys: string[] = ['北京', '上海', '广州', '深圳', '重庆']
  // 城市信息
  cityContentList: BKCityContent[] = [
    {
      initial: 'A',
      cityNameList: ['阿拉善', '鞍山', '安庆', '安阳', '阿坝', '安顺']
    },
    {
      initial: 'B',
      cityNameList: ['北京', '保定', '包头', '巴彦淖尔', '本溪', '白山']
    },
    {
      initial: 'C',
      cityNameList: ['成都', '重庆', '长春', '长沙', '承德', '沧州']
    },
    {
      initial: 'D',
      cityNameList: ['大连', '东莞', '大同', '丹东', '大庆', '大兴安岭']
    },
    {
      initial: 'E',
      cityNameList: ['鄂尔多斯', '鄂州', '恩施', '额尔古纳市', '二连浩特市', '恩施市']
    },
    {
      initial: 'F',
      cityNameList: ['福州', '佛山', '抚顺', '阜新', '阜阳', '抚州']
    },
    {
      initial: 'G',
      cityNameList: ['广州', '贵阳', '赣州', '桂林', '贵港', '广元']
    },
    {
      initial: 'H',
      cityNameList: ['杭州', '海口', '哈尔滨', '合肥', '呼和浩特', '邯郸']
    },
    {
      initial: 'J',
      cityNameList: ['济南', '晋城', '晋中', '锦州', '吉林', '鸡西']
    },
    {
      initial: 'K',
      cityNameList: ['昆明', '开封', '康定市', '昆山', '康保县', '宽城满族自治县']
    },
    {
      initial: 'L',
      cityNameList: ['兰州', '廊坊', '临汾', '吕梁', '辽阳', '辽源']
    },
    {
      initial: 'M',
      cityNameList: ['牡丹江', '马鞍山', '茂名', '梅州', '绵阳', '眉山']
    },
    {
      initial: 'N',
      cityNameList: ['南京', '宁波', '南昌', '南宁', '南通', '南平']
    },
    {
      initial: 'P',
      cityNameList: ['盘锦', '莆田', '萍乡', '平顶山', '濮阳', '攀枝花']
    },
    {
      initial: 'Q',
      cityNameList: ['青岛', '秦皇岛', '齐齐哈尔', '七台河', '衢州', '泉州']
    },
    {
      initial: 'R',
      cityNameList: ['日照', '日喀则', '饶阳县', '任丘市', '任泽区', '饶河县']
    },
    {
      initial: 'S',
      cityNameList: ['上海', '苏州', '深圳', '沈阳', '石家庄', '朔州']
    },
    {
      initial: 'T',
      cityNameList: ['天津', '太原', '唐山', '通辽', '铁岭', '通化']
    },
    {
      initial: 'W',
      cityNameList: ['无锡', '武汉', '乌海', '乌兰察布', '温州', '芜湖']
    },
    {
      initial: 'X',
      cityNameList: ['厦门', '西安', '西宁', '邢台', '忻州', '兴安盟']
    },
    {
      initial: 'Y',
      cityNameList: ['扬州', '阳泉', '运城', '营口', '延边', '伊春']
    },
    {
      initial: 'Z',
      cityNameList: ['郑州', '珠海', '张家口', '镇江', '舟山', '漳州']
    }
  ]
  // 右侧导航索引
  alphabets: string[] = ['#', '热', "A", "B", "C", "D", "E", "F", "G", "H", "J", "K", "L", "M", "N", "P", "Q", "R", "S", "T", "W", "X", "Y", "Z"]
  // 控制全屏模态的状态变量
  @State isShow: boolean = false
  // 选中的索引
  @State selectedIndex: number = 0
  // 控制器对象,控制 List 滚动,无需添加@State
  scroller: Scroller = new Scroller()

  build() {
    Column() {
      Image($r('app.media.ic_BK_content'))
        .width('100%')// 绑定全屏模态
        .bindContentCover(this.isShow, this.ContentCoverBuilder())// 点击显示全屏模态
        .onClick(() => {
          this.isShow = true
        })
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#f8f8f8')
  }

  @Builder
  ContentCoverBuilder() {
    Stack({ alignContent: Alignment.End }) {
      Column() {
        // 顶部
        this.TopBuilder();
        // 列表
        this.ListBuilder();
      }
      .backgroundColor(Color.White)

      // 导航 写这里
      this.AlphabetBuilder()
    }
  }

  @Builder
  AlphabetBuilder() {
    // 添加导航组件
    AlphabetIndexer({ arrayValue: this.alphabets, selected: $$this.selectedIndex })
      // 选中不同导航点,同步滚动 List
      .onSelect((index: number) => {
        this.scroller.scrollToIndex(index)
      })
  }

  @Builder
  ListBuilder() {
    // 绑定控制器对象
    List({ space: 30, scroller: this.scroller }) {
      // 历史 索引 0
      this.LocationListItemBuilder()
      // 热门 索引 1
      this.HotListItemBuilder()

      // A-B的区域 索引 2-n
      this.LetterListItemBuilder()
    }
    .divider({ startMargin: 20, endMargin: 20, color: '#f3f3f3', strokeWidth: 2 })
    .width('100%')
    .layoutWeight(1)
    .sticky(StickyStyle.Header)
      // 滚动 List 同步更新导航
    .onScrollIndex((start: number) => {
      this.selectedIndex = start
    })

  }

  @Builder
  LetterListItemBuilder() {
    // 外层循环
    ForEach(this.cityContentList, (item: BKCityContent, index: number) => {
      // A-B的区域
      ListItemGroup({ header: this.ListItemGroupHeaderBuilder(item.initial) }) {
        // 内层循环
        ForEach(item.cityNameList, (it: string, index: number) => {
          ListItem() {
            Text(it)
              .width('100%')
              .padding({ left: 20 })
          }
          .width('100%')
          .height(50)
          .backgroundColor(Color.White)
        })

      }
      .padding({ bottom: 20 })
      .divider({ startMargin: 20, endMargin: 20, color: '#f3f3f3', strokeWidth: 2 })
    })

  }

  @Builder
  ListItemGroupHeaderBuilder(title: string) {
    Text(title)
      .padding({ left: 20, bottom: 15, top: 20 })
      .fontSize(14)
      .fontColor(Color.Gray)
      .backgroundColor('#f8f8f8')
      .width('100%')
  }

  @Builder
  HotListItemBuilder() {
    // 热门
    ListItem() {
      Column({ space: 10 }) {
        Text('热门城市')
          .alignSelf(ItemAlign.Start)
          .fontColor(Color.Gray)
          .fontSize(14)
        Flex({ wrap: FlexWrap.Wrap }) {
          ForEach(this.hotCitys, (item: string, index: number) => {
            Text(item)
              .height(25)
              .backgroundColor(Color.White)
              .width('25%')
              .margin({ bottom: 10 })
          })

        }
        .padding({ left: 20, right: 20 })
      }
      .width('100%')
      .padding({ left: 20, right: 20, bottom: 10 })
    }
  }

  @Builder
  LocationListItemBuilder() {
    ListItem() {
      Column({ space: 15 }) {
        // 定位地址
        Row() {
          Text('北京')
          Text() {
            ImageSpan($r('app.media.ic_public_location_fill_blue'))
              .width(20)
            Span('开启定位')
          }
        }
        .width('100%')
        .padding({ top: 10, bottom: 10, right: 20, left: 20 })
        .justifyContent(FlexAlign.SpaceBetween)
        .backgroundColor(Color.White)

        // 历史
        Column({ space: 10 }) {
          Text('历史')
            .fontColor(Color.Gray)
            .alignSelf(ItemAlign.Start)
            .fontSize(14)
          Flex({ wrap: FlexWrap.Wrap }) {
            ForEach(this.historyCitys, (item: string, index: number) => {
              Text(item)
                .height(25)
                .backgroundColor(Color.White)
                .width('25%')
                .margin({ bottom: 10 })
            })

          }
          .padding({ left: 20, right: 20 })
        }
        .width('100%')
        .padding({ left: 20, right: 20 })
      }
    }
    .padding({ top: 20 })
  }

  @Builder
  TopBuilder() {
    Column() {
      // X + 输入框
      Row({ space: 20 }) {
        Image($r('app.media.ic_public_cancel'))
          .width(30)
          .fillColor(Color.Gray)// 点击关闭全屏模态
          .onClick(() => {
            this.isShow = false
          })

        Row({ space: 5 }) {
          Image($r('app.media.ic_public_search'))
            .width(18)
          Text('请输入城市名称')
            .layoutWeight(1)
        }
        .height(50)
        .border({ width: .5, color: Color.Gray, radius: 5 })
        .padding({ left: 5 })
        .layoutWeight(1)
        .shadow({
          radius: 20,
          color: '#f6f6f7'
        })
      }
      .padding({
        left: 15,
        right: 15,
        top: 15
      })

      // 国内城市
      Column() {
        Text('国内城市')
          .fontSize(15)
          .fontWeight(800)
          .padding(5)
        Row()
          .width(20)
          .height(2)
          .backgroundColor('#0094ff')
          .borderRadius(2)
      }

    }
    .width('100%')
    .backgroundColor(Color.White)
    .height(100)
    .border({
      width: { bottom: 4 },
      color: '#f6f6f7',
    })
  }
}

 

  • 14
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值