当前位置: 首页 > news >正文

demo 通讯录 + 城市选择器 (字母索引左右联动 ListItemGroup+AlphabetIndexer)笔记

一、城市选择器实现笔记

1. 双层 for 循环渲染

数据结构

interface BKCityContent {initial: string; // 字母索引cityNameList: string[]; // 城市列表
}

核心实现

// 外层循环:字母分组 - 遍历城市数据,按字母分组显示
ForEach(this.cityContentList, (item: BKCityContent, index: number) => {// ListItemGroup:创建分组容器,header显示字母标题ListItemGroup({ header: this.ListItemGroupHeaderBuilder(item.initial) }) {// 内层循环:城市列表 - 遍历每个分组下的城市名称ForEach(item.cityNameList, (ele: string, index: number) => {ListItem() {Text(ele)  // 显示城市名称.width('100%').padding({ left: 20 })}.width('100%').height(50)  // 统一高度,保持列表整齐.backgroundColor(Color.White)})}
})

要点

  • 外层遍历字母分组,内层遍历城市
  • ListItemGroup 实现分组效果

2. 模态框左右联动

状态变量

@State isShow: boolean = false    // 模态框显示
@State selected: number = 0       // 选中索引
scroller: Scroller = new Scroller() // 滚动器

联动代码

// 模态框结构 - 使用Stack布局,右侧放置索引器
@Builder
ContentCoverBuilder() {Stack({ alignContent: Alignment.End }) {  // 内容右对齐,为索引器留出空间Column() {this.TopBuilder()    // 顶部搜索栏this.ListBuilder()   // 城市列表}.backgroundColor(Color.White)// 右侧索引器 - 提供快速定位功能AlphabetIndexer({arrayValue: this.alphabets, selected: this.selected}).usingPopup(true)  // 启用弹出提示,显示当前选中的字母.onSelect((index) => {this.scroller.scrollToIndex(index, true)  // 点击索引时滚动到对应位置})}
}// 列表滚动监听 - 实现双向联动
List({scroller: this.scroller}) {  // 绑定滚动控制器// 列表内容
}
.onScrollIndex((index) => {this.selected = index  // 手动滚动时同步更新索引器的选中状态
})

联动机制

  • 点击索引 → onSelectscrollToIndex() 滚动
  • 手动滚动 → onScrollIndex → 更新 selected

3. 关键组件

AlphabetIndexer

// 字母索引器 - 右侧快速定位组件
AlphabetIndexer({arrayValue: this.alphabets, // 索引数组:['#', '热', "A", "B", "C"...]selected: this.selected, // 当前选中的索引位置
}).usingPopup(true) // 启用弹出提示,显示当前选中的字母.onSelect((index) => {this.scroller.scrollToIndex(index, true); // 点击时滚动到对应位置});

ListItemGroup

// 列表分组组件 - 按字母对城市进行分组显示
ListItemGroup({header: this.ListItemGroupHeaderBuilder(item.initial) // 自定义分组头部,显示字母
}) {// 分组内容 - 该字母下的所有城市
}
.padding({ bottom: 20 })  // 分组底部间距
.divider({ startMargin: 20, endMargin: 20, color: '#f3f3f3', strokeWidth: 2 }) // 分组间分割线

4. 数据组织

城市数据

cityContentList: BKCityContent[] = [{ initial: 'A', cityNameList: ['阿拉善', '鞍山', '安庆'] },{ initial: 'B', 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"]

5. 交互流程

  1. 打开模态框:this.isShow = true
  2. 点击索引:onSelectscrollToIndex(index)
  3. 滚动列表:onScrollIndexthis.selected = index
  4. 关闭模态框:this.isShow = false

6. 关键代码

模态框绑定

// 背景图片绑定模态框 - 点击图片显示城市选择器
Image($r("app.media.ic_BK_content")).bindContentCover($$this.isShow, this.ContentCoverBuilder()) // 绑定模态框内容.onClick(() => {this.isShow = true; // 点击时显示模态框});

分组头部

// 自定义分组头部构建器 - 显示字母标题
@Builder
ListItemGroupHeaderBuilder(title: string) {Text(title)  // 显示字母(如:A、B、C...).padding({ left: 20, bottom: 15, top: 20 })  // 内边距.fontSize(14)  // 字体大小.fontColor(Color.Gray)  // 灰色字体.backgroundColor('#f8f8f8')  // 浅灰色背景.width('100%')  // 占满宽度
}

总结

  1. 双层循环:外层分组 + 内层列表
  2. 左右联动:索引器 + 滚动器双向同步
  3. 状态管理@State 控制显示,Scroller 控制滚动
  4. 数据驱动:接口规范数据结构

城市选择全部代码:

interface BKCityContent {initial: stringcityNameList: 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 selected:number=0scroller: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')}@BuilderContentCoverBuilder() {Stack({ alignContent: Alignment.End }) {Column() {// 顶部this.TopBuilder();// 列表this.ListBuilder();}.backgroundColor(Color.White)AlphabetIndexer({arrayValue:this.alphabets,selected:this.selected}).usingPopup(true).onSelect((index)=>{this.scroller.scrollToIndex(index,true)})}}@BuilderListBuilder() {List({scroller:this.scroller}) {// 历史this.LocationListItemBuilder()// 热门this.HotListItemBuilder()// A-B的区域this.LetterListItemBuilder()}.divider({ startMargin: 20, endMargin: 20, color: '#f3f3f3', strokeWidth: 2 }).width('100%').layoutWeight(1).onScrollIndex((index)=>{this.selected=index})}@BuilderLetterListItemBuilder() {// A-B的区域ForEach(this.cityContentList,(item:BKCityContent,index:number)=>{ListItemGroup({ header: this.ListItemGroupHeaderBuilder(item.initial) }) {ForEach(item.cityNameList,(ele:string,index:number)=>{ListItem() {Text(ele).width('100%').padding({ left: 20 })}.width('100%').height(50).backgroundColor(Color.White)})}.padding({ bottom: 20 }).divider({ startMargin: 20, endMargin: 20, color: '#f3f3f3', strokeWidth: 2 })})}@BuilderListItemGroupHeaderBuilder(title: string) {Text(title).padding({ left: 20, bottom: 15, top: 20 }).fontSize(14).fontColor(Color.Gray).backgroundColor('#f8f8f8').width('100%')}@BuilderHotListItemBuilder() {// 热门ListItem() {Column({ space: 10 }) {Text('热门城市').alignSelf(ItemAlign.Start).fontColor(Color.Gray).fontSize(14)Flex({ wrap: FlexWrap.Wrap }) {ForEach(this.hotCitys,(item:string)=>{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 })}}@BuilderLocationListItemBuilder() {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,indedx: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 })}@BuilderTopBuilder() {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',})}
}

效果展示:

二、通讯录字母索引左右联动笔记

1. 数据结构设计

// 定义联系人数据结构
interface ContactData {initial: string    // 首字母nameList: string[] // 该字母下的联系人列表
}// 数据组织:按字母分组存储
contacts: ContactData[] = [{ initial: 'A', nameList: ['阿猫', '阿狗', ...] },{ initial: 'B', nameList: ['白兔', '白鸽', ...] },// ... 26个字母分组
]

2. 核心变量定义

// 滚动控制器 - 控制列表滚动
scroller: Scroller = new Scroller()// 当前激活索引 - 用于左右联动
@State activeIndex: number = 0// 字母索引数组 - 提供给AlphabetIndexer使用
alphabets: string[] = ['A', 'B', 'C', ..., 'Z']

3. 随机颜色功能

getRandomColor(): ResourceColor {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)`;
}

4. 左右联动实现流程

4.1 列表滚动 → 索引器高亮

List({ scroller: this.scroller }) {// 列表内容...
}
.onScrollIndex((index) => {this.activeIndex = index  // 关键:滚动时更新激活索引
})

实现原理

  • 用户滚动列表时,onScrollIndex回调触发
  • 更新activeIndex,触发 UI 重新渲染
  • AlphabetIndexer 通过$$this.activeIndex自动高亮对应字母

4.2 索引器点击 → 列表跳转

AlphabetIndexer({arrayValue: this.alphabets,selected: $$this.activeIndex, // 关键:双向绑定
}).onSelect((index) => {this.scroller.scrollToIndex(index); // 关键:点击时滚动到对应位置
});

实现原理

  • $$双向绑定:activeIndex变化时索引器自动更新
  • onSelect回调:点击字母时调用scrollToIndex跳转

5. 分组列表渲染流程

5.1 分组头部组件

@Builder
itemHead(text: string) {Text(text).fontSize(20).backgroundColor('#fff1f3f5').width('100%').padding(5)
}

5.2 分组列表渲染

ForEach(this.contacts, (item: ContactData, index: number) => {ListItemGroup({header: this.itemHead(item.initial),  // 设置分组头部space: 10}) {ForEach(item.nameList, (it: string, i: number) => {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' })
})

渲染流程

  1. 外层遍历 26 个字母分组
  2. 每个分组用ListItemGroup包装
  3. 内层遍历该分组下的联系人
  4. 每个联系人显示头像+姓名
  5. 添加分割线

6. 弹窗功能实现

AlphabetIndexer({ arrayValue: this.alphabets, selected: $$this.activeIndex }).usingPopup(true) // 启用弹窗.selectedColor(Color.Red) // 选中字母颜色.selectedBackgroundColor(Color.Green) // 选中背景色.popupColor(Color.Red) // 弹窗文字颜色.popupBackground(Color.Brown) // 弹窗背景色.popupTitleBackground(Color.Yellow); // 弹窗标题背景色

7. 关键样式设置

List({ scroller: this.scroller }).sticky(StickyStyle.Header)  // 分组标题粘性显示.scrollBar(BarState.Off)     // 隐藏滚动条AlphabetIndexer(...).offset({ x: 0, y: -100 })   // 调整位置避免遮挡

8. 快速回到顶部

Text("通讯录").onClick(() => {this.scroller.scrollToIndex(0, true); // 滚动到第一个位置
});

9. 完整交互流程

  1. 页面加载:显示所有联系人分组
  2. 滚动列表onScrollIndexactiveIndex更新 → 索引器高亮
  3. 点击字母onSelectscrollToIndex → 列表跳转
  4. 弹窗反馈:操作时显示当前选中字母
  5. 回到顶部:点击标题 → scrollToIndex(0)

10. 关键技术点

  • 双向绑定$$this.activeIndex实现状态同步
  • 滚动控制Scroller对象精确控制滚动
  • 分组显示ListItemGroup实现分组列表
  • 粘性头部.sticky(StickyStyle.Header)提升体验
  • 随机颜色:动态生成增加视觉区分度

11. 常见问题

Q: 为什么用@State activeIndex?
A: 响应式状态,变化时 UI 自动更新,实现左右联动

Q: 如何实现平滑滚动?
A: scrollToIndex(index, true) 第二个参数 true

Q: 双向绑定怎么工作?
A: $$符号,activeIndex 变化时索引器自动更新,点击索引器时触发 onSelect

通讯录全部代码:

// 定义联系人数据结构 - 每个字母分组包含首字母和对应的联系人列表
interface ContactData {initial: string    // 首字母,如 'A', 'B', 'C'nameList: string[] // 该字母下的联系人列表
}@Entry
@Component
struct Page09_ContactAndAlpha {// 联系人数据 - 按字母A-Z分组存储,便于后续的字母索引和分组显示// 注意:这里不需要@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: ['珍珠', '紫薇', '紫霞', '紫竹', '紫云', '紫燕', '紫鸢', '紫藤', '紫荆', '紫罗兰'] },]// 随机颜色生成函数 - 为每个联系人头像生成不同的背景色,增加视觉区分度getRandomColor(): ResourceColor {// 生成 0-255 的随机RGB值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)`;}// 字母索引数组 - 提供给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']// 滚动控制器 - 用于控制列表的滚动行为scroller: Scroller = new Scroller()// 当前激活的索引位置 - 用于字母索引器的状态同步@State activeIndex: number = 0build() {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')// 搜索区域 - 模拟搜索框Row() {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')// 主要内容区域 - 使用Stack布局,列表和字母索引器重叠Stack({ alignContent: Alignment.End }) {// 联系人列表 - 核心显示区域List({ scroller: this.scroller }) {// 遍历所有字母分组ForEach(this.contacts, (item: ContactData, index: number) => {// 每个字母分组使用ListItemGroup包装ListItemGroup({ header: this.itemHead(item.initial), // 设置分组头部space: 10 }) {// 遍历该分组下的所有联系人ForEach(item.nameList, (it: string, i: number) => {// 每个联系人的列表项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'     // 分割线颜色})})}.sticky(StickyStyle.Header)  // 分组标题粘性显示,滚动时粘在顶部.scrollBar(BarState.Off)     // 隐藏滚动条,提供更清爽的界面.onScrollIndex((index) => {// 滚动监听 - 当列表滚动时更新激活索引,实现左右联动this.activeIndex = index})// 字母索引器 - 右侧的字母快速定位工具AlphabetIndexer({ arrayValue: this.alphabets,     // 字母数组selected: $$this.activeIndex    // 双向绑定当前选中索引}).offset({ x: 0, y: -100 })      // 调整位置,避免遮挡内容.usingPopup(true)               // 启用弹窗显示.selectedColor(Color.Red)       // 选中字母的颜色.selectedBackgroundColor(Color.Green) // 选中字母的背景色.popupColor(Color.Red)          // 弹窗内文字颜色.popupBackground(Color.Brown)   // 弹窗背景色.popupTitleBackground(Color.Yellow) // 弹窗标题背景色.onSelect((index) => {// 点击字母时的回调 - 滚动到对应的分组位置this.scroller.scrollToIndex(index)})}}}// 分组头部组件构建器 - 创建每个字母分组的标题显示组件@BuilderitemHead(text: string) {Text(text).fontSize(20).backgroundColor('#fff1f3f5').width('100%').padding(5)}
}
http://www.xdnf.cn/news/1300735.html

相关文章:

  • Nginx反向代理与缓存实现
  • 人工智能与社会治理:从工具到生态的范式重构
  • Kafka生产者——提高生产者吞吐量
  • 切换VSCODE 中的默认 shell
  • GitHub 上 Star 数量前 18 的开源 AI Agent 项目
  • 制造装配、仓储搬运、快递装卸皆适配!MinkTec 弯曲形变传感器助力,让人体工学改变劳动生活
  • Vue3从入门到精通: 4.5 数据持久化与同步策略深度解析
  • Elasticsearch 深分页问题
  • 计算图的力量:从 PyTorch 动态图到 TensorFlow 静态图的全景与实战
  • Nginx蜘蛛请求智能分流:精准识别爬虫并转发SEO渲染服务
  • 【Java EE进阶 --- SpringBoot】初识Spring(创建SpringBoot项目)
  • iceberg 底层存储HDFS与juiceFS的区别
  • nflsoi 8.14 题解
  • 集成电路学习:什么是Video Processing视频处理
  • 《量子雷达》第4章 量子雷达的检测与估计 预习2025.8.14
  • ATAM:基于场景的软件架构权衡分析法
  • 解剖HashMap的put <三> JDK1.8
  • Linux入门指南:基础开发工具---yum/apt
  • MacOS 系统计算机专业好用工具安装
  • P5967 [POI 2016] Korale 题解
  • Java 8 新特性介绍
  • 【Docker】安装kafka案例
  • 【深度学习】深度学习的四个核心步骤:从房价预测看机器学习本质
  • 《Leetcode》-面试题-hot100-动态规划
  • 【无标题】卷轴屏手机前瞻:三星/京东方柔性屏耐久性测试进展
  • 待办事项小程序开发
  • 【C#】PNG 和 JPG、JPEG的应用以及三种格式的区别?
  • 2025天府杯数学建模C题
  • RxJava Android 创建操作符实战:从数据源到Observable
  • Java应用架构实战指南:主流模式解析与Spring落地实践