1# 如何按字母分组展示联系人 2 3## 场景说明 4在通讯录中,需要将联系人按照姓氏的首字母进行分组排列,从而更方便联系人的查找;联系人列表右侧的字母导航可以随列表的滑动而定位到对应字母处;同时,也可以通过字母导航控制列表跳到指定联系人分组。 5本例即为大家介绍如何实现上述场景。 6 7## 效果呈现 8本示例最终效果如下: 9 10 11 12## 环境要求 13- IDE:DevEco Studio 3.1 Beta1 14- SDK:Ohos_sdk_public 3.2.11.9 (API Version 9 Release) 15 16## 实现思路 17本例涉及的四个关键特性及其实现方案如下: 18- 联系人按字母分组展示:通过List组件显示联系人列表,通过ListItemGroup组件实现联系人分组。 19- 联系人右侧呈现字母导航:使用AlphabetIndexer组件实现字母导航,同时通过Stack组件使字母导航浮在联系人列表右侧。 20- 滑动联系人列表,右侧字母导航随之变动:通过List组件的onScrollIndex事件获取到联系人列表的滑动位置,并将该位置索引传递给字母导航的selected属性,作为字母导航的被选中项。 21- 通过右侧字母导航控制联系人列表滑动到指定分组:通过字母导航的onSelected事件获取选中字母的索引,并将该索引传递给联系人列表的控制器,控制列表滑动到指定分组。 22 23## 开发步骤 24针对上述关键特性,具体实现步骤如下: 25 261、通过Stack、List、ListItemGroup、AlphabetIndexer等关键组件将UI框架搭建起来。 27先构建列表数据,其中Contact为联系人数据类。 28```ts 29contactGroups: object[] = [ 30 ... 31 { 32 title: 'D', 33 contacts: [ 34 new Contact('Donna', $r('app.media.contact6')), 35 new Contact('朵朵', $r('app.media.contact1')), 36 ], 37 }, 38 ... 39 { 40 title: 'K', 41 contacts: [ 42 new Contact('孔孔', $r('app.media.contact2')), 43 new Contact('康康', $r('app.media.contact3')), 44 ], 45 }, 46 { 47 title: 'L', 48 contacts: [ 49 new Contact('Lisa', $r('app.media.contact4')), 50 new Contact('玲玲', $r('app.media.contact5')), 51 ], 52 }, 53 { 54 title: 'N', 55 contacts: [ 56 new Contact('牛牛', $r('app.media.contact6')), 57 new Contact('Natasha', $r('app.media.contact1')), 58 ], 59 }, 60 ... 61 ] 62``` 63有了列表数据后,我们来构建UI框架,关键代码如下: 64```ts 65@Entry 66@Component 67struct ContactList{ 68 ... 69 // 自定义组件groupHeader,作为ListItemGroup的头部组件,即A、B、C等字母列表项 70 @Builder groupHeader(titleLetter:string){ 71 Text(titleLetter) 72 .fontSize(20) 73 .backgroundColor('#fff1f3f5') 74 .width('100%') 75 .padding(5) 76 } 77 // 创建字母列表作为字母导航的内容 78 private alphabets:string[] = [ 'A', 'B', 'D', 'G', 'K', 'L', 'N', 'X']; 79 80 build() { 81 Stack({alignContent:Alignment.End}){ 82 List(){ 83 // 循环渲染列表内容 84 ForEach(this.contactGroups,contactGroup=>{ 85 // 采用ListItemGroup对联系人进行分组,将groupHeader作为ListItemGroup的头部组件 86 ListItemGroup({header:this.groupHeader(contactGroup.title)}){ 87 ForEach(contactGroup.contacts,contact=>{ 88 ListItem(){ 89 Column(){ 90 Row(){ 91 Image(contact.icon) 92 ... 93 Text(contact.name) 94 } 95 ... 96 Divider().color('#fff1f3f5') 97 } 98 ... 99 } 100 }) 101 } 102 }) 103 } 104 ... 105 // 使用AlphabetIndexer组件实现右侧字母导航 106 AlphabetIndexer({arrayValue:this.alphabets,selected:0}) 107 ... 108 } 109 } 110} 111``` 112完成上述代码,我们的框架就搭建起来了,如图: 113 114 115 1162、接下来为UI框架添加逻辑控制。首先,通过List的onScrollIndex事件获取到列表滑动位置的索引,并将索引同步给右侧字母表的selected属性,从而在滑动联系人时,使右侧字母导航随之变动,关键代码如下: 117```ts 118 ... 119 // 创建动态变量,用于指定字母导航的选择项 120 @State selectedIndex:number = 0; 121 ... 122 build() { 123 Stack({alignContent:Alignment.End}){ 124 List({scroller:this.listScroller}){ 125 ForEach(this.contactGroups,contactGroup=>{ 126 ListItemGroup({header:this.groupHeader(contactGroup.title)}){ 127 ForEach(contactGroup.contacts,contact=>{ 128 ListItem(){ 129 ... 130 } 131 }) 132 } 133 }) 134 } 135 ... 136 // 获取联系人列表滑动位置的索引,并将索引通过selectedIndex同步给右侧字母导航 137 .onScrollIndex((firstIndex:number)=>{ 138 this.selectedIndex = firstIndex 139 }) 140 AlphabetIndexer({arrayValue:this.alphabets,selected:0}) 141 ... 142 // 指定字母导航的选择项为selectedIndex,完成跟联系人列表的同步 143 .selected(this.selectedIndex) 144 ... 145 } 146 } 147``` 148至此,当我们滑动联系人列表时,就可以让右侧字母导航随之变动了。效果如下: 149 150 151 1523、最后,我们通过AlphabetIndexer组件的onSelect事件获取到字母导航选择项的索引,然后通过List组件的scroller控制器控制联系人列表滑动到相同的索引处,从而实现通过右侧字母导航控制联系人列表滑动到指定分组。关键代码如下: 153```ts 154... 155 @State selectedIndex:number = 0; 156 // 创建List组件的scroller控制器:listScroller,用于控制联系人列表的滑动位置 157 private listScroller:Scroller = new Scroller() 158 ... 159 build() { 160 Stack({alignContent:Alignment.End}){ 161 // 将scroller控制器绑定到List组件 162 List({scroller:this.listScroller}){ 163 ForEach(this.contactGroups,contactGroup=>{ 164 ListItemGroup({header:this.groupHeader(contactGroup.title)}){ 165 ForEach(contactGroup.contacts,contact=>{ 166 ListItem(){ 167 ... 168 } 169 }) 170 } 171 }) 172 } 173 ... 174 AlphabetIndexer({arrayValue:this.alphabets,selected:0}) 175 ... 176 // 获取字母导航中选中字母的索引值,并通过listScroller控制列表滑动到对应索引位置 177 .onSelect((index:number)=>{ 178 this.listScroller.scrollToIndex(index) 179 }) 180 ... 181 } 182 } 183``` 184至此,当我们在右侧字母导航选择某个字母时就可以控制联系人列表跳转到指定分组了,效果如下: 185 186 187 188## 完整代码 189通过上述步骤我们已经完成了整个示例的开发,现提供本示例的完整代码供大家参考: 190 191联系人数据类代码: 192```ts 193// ListModel.ets 194export default class Contact{ 195 name:string; 196 icon:Resource; 197 198 constructor(name:string,icon:Resource) { 199 this.name = name 200 this.icon = icon 201 } 202} 203``` 204案例主代码: 205```ts 206// Contact.ets 207import Contact from '../model/ListModel' 208 209@Entry 210@Component 211struct ContactList{ 212 // 联系人列表数据 213 contactGroups: object[] = [ 214 { 215 title: 'A', 216 contacts: [ 217 new Contact('艾薇而', $r('app.media.contact1')), 218 new Contact('安琪', $r('app.media.contact2')), 219 new Contact('Angela', $r('app.media.contact3')), 220 ], 221 }, 222 { 223 title: 'B', 224 contacts: [ 225 new Contact('Bobe', $r('app.media.contact4')), 226 new Contact('勃勃', $r('app.media.contact5')), 227 ], 228 }, 229 { 230 title: 'D', 231 contacts: [ 232 new Contact('Donna', $r('app.media.contact6')), 233 new Contact('朵朵', $r('app.media.contact1')), 234 ], 235 }, 236 { 237 title: 'G', 238 contacts: [ 239 new Contact('Gavin', $r('app.media.contact4')), 240 new Contact('果味', $r('app.media.contact1')), 241 ], 242 }, 243 { 244 title: 'K', 245 contacts: [ 246 new Contact('孔孔', $r('app.media.contact2')), 247 new Contact('康康', $r('app.media.contact3')), 248 ], 249 }, 250 { 251 title: 'L', 252 contacts: [ 253 new Contact('Lisa', $r('app.media.contact4')), 254 new Contact('玲玲', $r('app.media.contact5')), 255 ], 256 }, 257 { 258 title: 'N', 259 contacts: [ 260 new Contact('牛牛', $r('app.media.contact6')), 261 new Contact('Natasha', $r('app.media.contact1')), 262 ], 263 }, 264 { 265 title: 'X', 266 contacts: [ 267 new Contact('小可爱', $r('app.media.contact2')), 268 new Contact('徐总是', $r('app.media.contact3')), 269 new Contact('璇璇', $r('app.media.contact3')), 270 new Contact('欣欣', $r('app.media.contact3')), 271 ], 272 }, 273 ] 274 // 自定义组件groupHeader,作为ListItemGroup的头部组件,即A、B、C等字母列表项 275 @Builder groupHeader(titleLetter:string){ 276 Text(titleLetter) 277 .fontSize(20) 278 .backgroundColor('#fff1f3f5') 279 .width('100%') 280 .padding(5) 281 } 282 // 创建字母列表作为字母导航的内容 283 private alphabets:string[] = [ 'A', 'B', 'D', 'G', 'K', 'L', 'N', 'X']; 284 // 创建动态变量,用于指定字母导航的选择项 285 @State selectedIndex:number = 0; 286 // 创建List组件的scroller控制器:listScroller,用于控制联系人列表的滑动位置 287 private listScroller:Scroller = new Scroller() 288 289 build() { 290 Stack({alignContent:Alignment.End}){ 291 // 将scroller控制器绑定到List组件 292 List({scroller:this.listScroller}){ 293 // 循环渲染列表内容 294 ForEach(this.contactGroups,contactGroup=>{ 295 // 采用ListItemGroup对联系人进行分组,将groupHeader作为ListItemGroup的头部组件 296 ListItemGroup({header:this.groupHeader(contactGroup.title)}){ 297 ForEach(contactGroup.contacts,contact=>{ 298 ListItem(){ 299 Column(){ 300 Row(){ 301 Image(contact.icon) 302 .width(35) 303 .height(35) 304 .margin(10) 305 Text(contact.name) 306 } 307 .width('100%') 308 Divider().color('#fff1f3f5') 309 } 310 .justifyContent(FlexAlign.Start) 311 } 312 }) 313 } 314 }) 315 } 316 .width('100%') 317 .height('100%') 318 .scrollBar(BarState.Auto) 319 // 获取联系人列表滑动位置的索引,并将索引通过selectedIndex同步给右侧字母导航 320 .onScrollIndex((firstIndex:number)=>{ 321 this.selectedIndex = firstIndex 322 }) 323 // 使用AlphabetIndexer组件实现右侧字母导航 324 AlphabetIndexer({arrayValue:this.alphabets,selected:0}) 325 .margin({right:10}) 326 .itemSize(25) 327 .font({size:15}) 328 // 指定字母导航的选择项为selectedIndex,完成跟联系人列表的同步 329 .selected(this.selectedIndex) 330 // 获取选中字母的索引值,通过listScroller控制列表滑动到对应索引位置 331 .onSelect((index:number)=>{ 332 this.listScroller.scrollToIndex(index) 333 }) 334 } 335 } 336} 337``` 338## 参考 339[创建列表](../application-dev/ui/arkts-layout-development-create-list.md)