关于list列表和AlphabetIndexer联动效果实现

272 阅读4分钟

一.关于list列表和AlphabetIndexer联动效果实现(就联系人列表展开说明)

1.实现效果

动画.gif

2.涉及知识点

实现原理:

Snipaste_2024-08-19_20-44-05.png

List布局,子组件(ListItemGroup,ListItem)嵌套展示,它的相关属性

基础语法

@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)
        }
      }
      .sticky(StickyStyle.Header)  //枚举类型:
       //.sticky(StickyStyle.None) // 不吸附 默认值
       // .sticky(StickyStyle. Footer) //  底部吸附,如果有的
      .divider({ strokeWidth: 1, color: Color.Orange,startMargin:40 })  //设置分割线
              注意:.divider的位置:listitemGroup(){}.divider是以listitem为单位设置分割线
                                   list(){}.divider是以listItemGroup为单位设置分割线
    }
  }
}

基础属性

见开发文档:List-容器组件-ArkTS组件-ArkUI(方舟UI框架)-应用框架 - 华为HarmonyOS开发者 (huawei.com)

事件

一,子组件划入或划出List显示区域时触发,自动获取index的索引值。从API version 10开始,List显示区域中间位置子组件变化时也会触发。

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

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

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

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

    List(){
      // ...
    }
    .onScrollIndex((index: number) => {   //子组件划入或划出List显示区域时触发,自动获取index的索引值
      console.log('index:', index)
    })
二,代码控制滚动(索引组件改变list的核心)

关键步骤:

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


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

AlphabetIndexer:一般用层叠布局的{ alignContent: 枚举值}或者position进行定位

核心用法

@Entry
@Component
struct ContactsList {
  @State selectedIndex: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.selectedIndex })
            //$$通过双向绑定变量。可以实现修改变量值,同步更新选中的索引
    }
    .width('100%')
    .height('100%')
  }
}

弹窗提示

usingPopupboolean设置是否使用提示弹窗。默认值:false。
popupBackgroundResourceColor设置提示弹窗背景色。默认值:0xFFFFFFFF。
popupColorResourceColor设置提示弹窗文字颜色。默认值:0xFF254FF7。
@Entry
@Component
struct ContactsList {
  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: 0 })
        .usingPopup(true)     //开启弹窗
        .popupColor(Color.Orange)// 弹窗文字颜色
        .popupTitleBackground(Color.Pink)  // 弹窗背景颜色
    }
    .width('100%')
    .height('100%')
  }
}

事件

onSelect(callback: (index: number) => void)8+索引条选中回调,返回值为当前选中索引。
 .onSelect((index: number) => {
          this.selectedIndex = index
        })

随机用户图标

核心

function Getrandomcolor() {
  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)`
}  //以Getrandomcolor()调用即可

3.代码展示说明

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

function Getrandomcolor() {
  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)`
}

@Entry
@Component
struct Index {
  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: ['南山', '南极', '南海', '南京', '南阳', '南风', '南瓜', '南竹', '南花', '南鸟'] },
]
  @State selectedIndex: 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']

  @Builder
  headBuilder(initial: string) {
    Text(initial)
      .backgroundColor('#ffb5b4b4')
      .width('100%')
      .height(30)
      .margin({ top: 10, bottom: 5 })
  }

  scroll = new Scroller()

  build() {
    Column() {
      Stack({ alignContent: Alignment.End }) {
        List({ scroller: this.scroll }) {
          ForEach(this.contacts, (item: ContactContent) => {
            ListItemGroup({
              header: this.headBuilder(item.initial),
              space: 30,
            }
            ) {
              ForEach(item.nameList, (Name: string) => {
                ListItem() {
                  Row({ space: 15 }) {
                    Image($r('app.media.user_01'))
                      .width(20)
                      .fillColor(Getrandomcolor())
                    //.fillColor(`rgba(${Math.floor(Math.random()*256)},${Math.floor(Math.random()*256)},${Math.floor(Math.random()*256)},0.5)`)
                    Text(Name)
                  }

                }
              })
            }
            .divider({
              strokeWidth: 1,
              color: '#ffdddada',
              startMargin: 15,
              endMargin: 15
            })
          })
        }
        .onScrollIndex((index: number) => {
          this.selectedIndex = index
        })
        .sticky(StickyStyle.Header)

        AlphabetIndexer({
          arrayValue: this.alphabets,
          selected: this.selectedIndex
        })
          .itemSize(20)
          .usingPopup(true)
          .popupColor(Color.Orange)
          .popupPosition({ x: 25 })
        .onSelect((index:number)=>{
          this.scroll.scrollToIndex(index)
        })
      }

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