鸿蒙开发与应用之 List容器组件属性及其使用。

910 阅读13分钟

一. 列表 List

List列表什么?
List是一种复杂的容器,当其中的内容超过屏幕大小时,可以自动提供滚动功能。
场景:它适合于同类布局的重复显示,如通讯录、音乐列表、购物清单等 。

bbdd601a452a2780953d1b885f2dabdb_1716715335273-259add2f-a603-4c22-8b3f-7ec49adf3b12.png
app1.gif

通过在 list 组件中按垂直或者水平方向线性排列子组件。 列表作为一种容器,会自动按其滚动方向排列子组件,向列表中添加组件或从列表中移除组件会重新排列子组件。

垂直滚动列表:

水平滚动列表:

List组件基本语法:

List 表示列表容器,ListItem表示单个列表项,可以包含单个子组件。
语法:List(){

  • ListItem(){}
  • ListItem(){}
  • ····· }

ForEach(),为循环,可以快速生成多个 ListItem 组件

语法:List(){

  • ForEach( ,()=>{
  • ListItem(){}
  • }) }

1. 设置主轴方向 :

属性: listDirection()
语法:List(){ }.listDirection(Axis.垂直/水平方向) 参数:枚举 Axis

  • Vertical:垂直方向,默认值
  • Horizontal:水平方向

List组件主轴默认是垂直方向,所以List默认是一个垂直滚动列表。listDirection(Axis.Vertical),将 List 的 listDirection 属性设置为 Axis.Horizontal 即可实现水平滚动列表。

2. 设置交叉轴布局

列模式
属性: lanes(参数1,参数2) 参数:

  • 参数1: 交叉轴方向列数
  • 参数2: 交叉轴方向列间距

列对齐方式
List交叉轴方向宽度大于ListItem交叉轴宽度 *列数时,ListItem在List交叉轴方向的布局方式,默认为首部对齐。 属性:alignListItem() 参数:枚举 ListItemAlign

  • Start:首部对齐(默认)
  • Center:居中对齐
  • End:尾部对齐
List() {
listItem()
......
}
.alignListItem(ListItemAlign.Center)

3. 自定义列表样式

3.1 滚动条状态

属性:

scrollBar() 参数:枚举 BarState

  • Off: 不显示
  • On:常驻显示
  • Auto:按需显示(触摸时显示,2s 后消失)

List() {

listItem()

......

}

.scrollBar(BarState.Off)
3.2 分割线样式

列表默认没有分割线
属性: divider() 参数:

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

分割线属性演示:

image.png

代码:

List() {
listItem()
......
}
.divider({
strokeWidth: 1,
color: Color.Orange,
startMargin: 10,
endMargin: 10
})

List 随笔代码:

@Entry
@Component
struct Index {
  build() {
    Column(){
    //   list基本语法:
    //   list(){
    //     ListItem(){写任意组件}
      //     }
      /*
       * 总结:
       * 1.List的作用:当List内部的内容高度(宽度)超出了List本身,就自动出滚动条
       * 2.语法:list(){ListItem(){写任意组件} }
       * 想要修改List的滚动方向,需要使用listDirection来进行修改
       * 语法:List(){}.listDirection(Axis.水平/垂直)
       * List默认滚动方向是垂直方向,可以省略
       * 注意点:
       * List里面直接写其他组件会报错
       * ListItem(){里面只能有一个子组件}
       * 建议:List组件建议加上宽和高,方便List来计算什么时候出滚动条
       *
       * */
List(){
  ListItem(){
    Text('昨天就尴尬')
  }
  .backgroundColor(Color.Orange)
  .height(20)
  .width(100)
  ListItem(){
    Text('昨天就尴尬')
  }
  .height(20)
  .width(100)
  ListItem(){
    Text('活动回顾')
  }
  .height(20)
  .width(100)
  ListItem(){
    Text('活动回顾')
      .height(20)
      .width(100)
  }
  ListItem(){
    Text('活动回顾')
  }
  .height(20)
  .width(100)
  ListItem(){
    Text('活动回顾')
  }
  ListItem(){
    Text('昨天就尴尬')
  }
  .height(20)
  .width(100)
}
      .height(200)
      .backgroundColor(Color.Pink)
      // 将List的布局改为水平方向
      .listDirection(Axis.Horizontal)
    // 设置ListItem在List交叉轴方向的列对齐方式
    .alignListItem(ListItemAlign.Center)
      // 设置列数和列间距
      .lanes(2,5)
// 关闭滚动条显示
.scrollBar(BarState.Off)
// 设置List的分割线
.divider({
  // 分割线宽度
  strokeWidth:2,
  // 分割线颜色
  color:Color.Red,
  // 分割线右边距
  endMargin:5,
  // 分割线左边距
  startMargin:5
})
    }
    .width('100%')
    .height('100%')
  }
}

代码实现效果:

动画12.gif

图中可以看到预览器中分割线可能会粗细不一,这是预览器渲染的问题。

二. List列表实现分组展示

核心用法:

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

image.png

1. ListItemGroup

ListItemGroup组件参数,以对象形式传入
ListItemGroup(参数){}.属性()

参数名参数类型必填参数描述
headerCustomBuilder设置ListItemGroup头部组件。
footerCustomBuilder设置ListItemGroup尾部组件。
spacenumberstring列表项间距。只作用于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 Index {
//  ListItemGroup头部函数
@Builder head(){
  Text('x')
    .fontSize(25)
    .fontWeight(600)
    .fontColor(Color.Red)
}
//  ListItemGroup底部函数
@Builder dibu(){
  Text('dibu')
    .fontSize(25)
    .fontWeight(600)
    .fontColor(Color.Red)
}
  build() {
    Column(){
      List(){
        ListItemGroup({
          header:this.head(),
          // 设置ListItem的间距
          space:10,
          footer:this.dibu()
        }){
          ListItem(){
            Text('小小怪')
              .fontWeight(600)
              .fontSize(30)
          }
          ListItem(){
            Text('小灰灰')
              .fontWeight(600)
              .fontSize(30)
          }
        }
        .divider({strokeWidth:1,color:Color.Red})
      }
    }
    .width('100%')
    .height('100%')
    .backgroundColor('rgba(0,0,0,0.3)')
  }
}

代码效果:

image.png

ForEach渲染演示:

代码:

interface sju{
  xuhao:string
  names:string[]
}
@Entry
@Component
struct Index {
  // ListItemGroup头部函数
  @Builder head(guilei:string){
    Text(guilei)
      .fontSize(25)
      .fontWeight(600)
      .fontColor(Color.Red)
  }
  // ListItemGroup底部函数
  @Builder dibu(){
    Text('dibu')
      .fontSize(25)
      .fontWeight(600)
      .fontColor(Color.Red)
  }
  // 定义sj数组  xuhao:序号,names:名字的数组
  sj:sju[]=[
    {xuhao:'A',names:['小小怪','小灰灰']},
    {xuhao:'D',names:['大大怪','大货车']}
  ]
  build() {
    Column(){
      List(){
// 渲染使用sj数组,自定义listig给其自定义的sju类型(保持和定义的sj数组类型一致),其意义是将sj的数据给listig
ForEach(this.sj,(listig:sju)=>{
  ListItemGroup({
    // 头部里内容引用  刚刚赋值的listig数组中的xuhao
    header:this.head(listig.xuhao),
    // 设置ListItem的间距
    space:10,
    footer:this.dibu()
  }) {
    // 内容引用  刚刚赋值的listig数组中的names
    ForEach(listig.names,(listinr:string)=>{
      ListItem() {
        Text(listinr)
          .fontWeight(600)
          .fontSize(30)
      }
    })

  }
})
        // .divider({strokeWidth:1,color:Color.Red})
      }
    }
    .width('100%')
    .height('100%')
    .backgroundColor('rgba(0,0,0,0.3)')
  }
}

效果:

image.png

2. 粘性标题

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

如图:名字首字母大小

image.png

动画16.gif

写法: .sticky.(StickyStyle .枚举值)

注意: 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) //  底部吸附,如果有的话

3. 控制滚动

如果列表很长,需要快速滚动到列表底部或返回列表顶部,就可以使用代码来控制滚动
关键步骤:

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

参数:

参数名参数类型必填参数描述
indexnumber要滑动到的目标元素所在的ListItemGroup或者ListItem在当前容器中的索引值。
smoothboolean设置滑动到列表项在列表中的索引值时是否有动效,true表示有动效,false表示没有动效。 默认值:false。
alignScrollAlign指定滑动到的元素与当前容器的对齐方式。 默认值:ScrollAlign.START。

4. 事件

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)
})

5. 综合案例:

需求:

  1. 头像的背景颜色设为随机色。
  2. 头部字母要吸顶。
  3. 滚动条与列表实现双向控制。

动画20.gif

代码部分:

// 方法类型
interface ContactContent {
  initial: string
  nameList: string[]
}

@Entry
@Component
struct ContactsList {
  // 数组
  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: ['珍珠', '紫薇', '紫霞', '紫竹', '紫云', '紫燕', '紫鸢', '紫藤', '紫荆', '紫罗兰'] },
  ]
  // 在结构体中定义一个函数,在视图中用this.来调用
  // `rgba(${Math.floor(Math.random()*256)},${Math.floor(Math.random()*256)},${Math.floor(Math.random()*256)},0.6)`
  // 变形为:
  // 随机色
  sjcolor(){
    let r=Math.floor(Math.random()*256)
    let g=Math.floor(Math.random()*256)
    let b=Math.floor(Math.random()*256)
    let rgb=`rgba(${r},${g},${b},0.6)`
    return rgb
  }


  // 创建控制器对象,控制滚动
  ls:ListScroller=new ListScroller()
  build() {
    Column() {
      Stack({ alignContent: Alignment.End }) {
        Text('通讯录')
          .width('100%')
          .textAlign(TextAlign.Center)
          .fontSize(20)
        Image($r('app.media.ic_public_add'))
          .width(20)
          .onClick(()=>{
            //   点击ListItemGroup()或者ListItem()的索引自定义滚动到
            //   调用控制器对象方法,实现滚动
            this.ls.scrollToIndex(4)

          })
      }
      .width('100%')
      .padding(15)
      .backgroundColor('#fff1f3f5')
      // 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')

      // 将ls对象绑定设置给List组件
      List({scroller:this.ls}) {
        // 顶部
        ForEach(this.contacts,(listig:ContactContent)=>{
          ListItemGroup({
            header: this.itemHead(listig.initial),
            space: 10 }) {
            // 循环渲染分组A的ListItem  调用随机颜色
            ForEach(listig.nameList,(listi:string)=>{
              this.contactBuilder(listi, this.sjcolor())
            })
          }
          .divider({
            startMargin: 60,
            strokeWidth: 1,
            color: '#ccc'
          })
        })
      }
      // 粘性标签,可以让listig的内容附在屏幕顶部或者底部
      // .sticky(StickyStyle.Header |StickyStyle.Footer) 顶部底部都粘
      .sticky(StickyStyle.Header)
      .onScrollIndex((index)=>{
        //   能监控用户滑动到哪个区域
        console.log(index.toString())
      })
    }
  }
  @Builder
  itemHead(text: string) {
    // 列表分组的头部组件,对应联系人分组A、B等位置的组件
    Text(text)
      .fontSize(20)
      .backgroundColor('#fff1f3f5')
      .width('100%')
      .padding(5)
  }
  @Builder
  contactBuilder(name: string, color: ResourceColor) {
    ListItem() {
      Row({ space: 10 }) {
        Image($r('app.media.ic_public_lianxiren'))
          .width(40)
          .fillColor(color)
        Text(name)
      }
    }
  }
}

三. AlphabetIndexer (别称:索引组件)

AlphabetIndexer不是容器组件,属于功能类的组件。

如图:

动画19.gif

1. AlphabetIndexer属性了解

/*
 * AlphabetIndexer不是容器组件,属于功能类的组件
 * */
@Entry
@Component
struct ContactsList {
  @State selIndex: number = 0
  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']
  build() {
    Stack({ alignContent: Alignment.End }) {
      // 字母表索引组件
      // AlphabetIndexer({ arrayValue: this.alphabets, selected: $$this.selIndex })
      AlphabetIndexer({ arrayValue: this.alphabets, selected: 0 })
        .itemSize(18)// 文字大小
        .color(Color.Orange)// 文字颜色
        .selectedColor(Color.Green)// 选中文字颜色
        .selectedBackgroundColor(Color.Black) // 选中背景颜色
        .usingPopup(true) // 打开pup
        .popupColor(Color.Orange)// 弹窗文字颜色
        .popupTitleBackground(Color.Pink)  // 弹窗背景颜色
        .onSelect((index: number) => {
          // this.selectedIndex = index
          // 获取当前用户选中的索引
          console.log('索引:',index)
        })
    }
    .width('100%')
    .height('100%')
  }
}

2. AlphabetIndexer可以与List组件一起使用形成通讯录的页面

需求:

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

如图:

动画21.gif

代码实现:

// 方法类型
interface ContactContent {
  initial: string
  nameList: string[]
}

@Entry
@Component
struct ContactsList {
  // 数组
  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: ['珍珠', '紫薇', '紫霞', '紫竹', '紫云', '紫燕', '紫鸢', '紫藤', '紫荆', '紫罗兰'] },
  ]

  /*
   * 和 List 的用户列表进行联动,实现选中切换的联动效果
需求:
1. 整合 AlphabetIndexer 到联系人案例中
2. 滚动 List,同步选中对应的AlphabetIndexer
3. 选中AlphabetIndexer的区域,同步滚动List

分析:
1. 滚动 List,同步选中对应的AlphabetIndexer
  a. list 注册事件onScrollIndex 获取滚到的索引--赋值->索引
  b. 定义索引,并双向绑定给AlphabetIndexer

2. 选中AlphabetIndexer的区域,同步滚动List
  a. AlphabetIndexer 注册 onSelect 事件,
    ⅰ. 可以获取到 索引值
    ⅱ. 通过索引,调用 List 控制器的方法滚动 list 即可
   * */
  //
  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 selIndex: number = 0

  // 在结构体中定义一个函数,在视图中用this.来调用
  // `rgba(${Math.floor(Math.random()*256)},${Math.floor(Math.random()*256)},${Math.floor(Math.random()*256)},0.6)`
  // 变形为:
  // 随机色
  sjcolor(){
    let r=Math.floor(Math.random()*256)
    let g=Math.floor(Math.random()*256)
    let b=Math.floor(Math.random()*256)
    let rgb=`rgba(${r},${g},${b},0.6)`
    return rgb
  }

  // 创建控制器对象,控制滚动
  ls:ListScroller=new ListScroller()
  build() {
    Column() {
      Stack({ alignContent: Alignment.End }) {
        Text('通讯录')
          .width('100%')
          .textAlign(TextAlign.Center)
          .fontSize(20)
        Image($r('app.media.ic_public_add'))
          .width(20)
          .onClick(()=>{
            //   点击ListItemGroup()或者ListItem()的索引自定义滚动到
            //   调用控制器对象方法,实现滚动
            this.ls.scrollToIndex(4)

          })
      }
      .width('100%')
      .padding(15)
      .backgroundColor('#fff1f3f5')

      // 顶部
      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)

      // Stack布局 ->alignContent:Alignment.End索引组件定位到右边
      Stack({alignContent:Alignment.End}){
        // 将ls对象绑定设置给List组件
        List({scroller:this.ls}) {
          ForEach(this.contacts,(listig:ContactContent)=>{
            ListItemGroup({
              header: this.itemHead(listig.initial),
              space: 10 }) {
              // 循环渲染分组A的ListItem  调用随机颜色
              ForEach(listig.nameList,(listi:string)=>{
                this.contactBuilder(listi, this.sjcolor())
              })
            }
            .divider({
              startMargin: 60,
              strokeWidth: 1,
              color: '#ccc'
            })
          })
        }
        // 粘性标签,可以让listig的内容附在屏幕顶部或者底部
        // .sticky(StickyStyle.Header |StickyStyle.Footer) 顶部底部都粘
        .sticky(StickyStyle.Header)
        .onScrollIndex((index)=>{
          //   能监控用户滑动到哪个区域
          console.log(index.toString())
          //
          this.selIndex=index
        })
        // 关闭List滚动条
        .scrollBar(BarState.Off)
        // 索引组件布局
        AlphabetIndexer({arrayValue:this.alphabets,selected:this.selIndex})
          .itemSize(18)
          .usingPopup(true)
          .popupColor(Color.Blue)
          .popupPosition({x:15,y:10})
          .onSelect((indexal)=>{
            this.ls.scrollToIndex(indexal)
          })
      }
    }
  }

  @Builder
  itemHead(text: string) {
    // 列表分组的头部组件,对应联系人分组A、B等位置的组件
    Text(text)
      .fontSize(20)
      .backgroundColor('#fff1f3f5')
      .width('100%')
      .padding(5)
  }
  @Builder
  contactBuilder(name: string, color: ResourceColor) {
    ListItem() {
      Row({ space: 10 }) {
        Image($r('app.media.ic_public_lianxiren'))
          .width(40)
          .fillColor(color)
        Text(name)
      }
    }
  }
}