ArkTs - 如何快速手搓一个简易通讯录列表

364 阅读5分钟

ArkTs中 list()组件 和 AlphabetIndexer()组件,用两个就可以轻松实现一个简易通讯录。

大概思路

  • list()组件 负责数据渲染 布局 滚动 获取 ListItemGroup索引值
  • AlphabetIndexer()组件 负责 字母表与数据的交互

联系人数据

由于我们主要实现的是通讯录,数据就放这里

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()层叠布局来包裹两个组件,state()有个属性可以把AlphabetIndexer()组件 的位置安排到页面右边。用list()组件 配合 ForEach()循环来实现页面布局。

Stack({alignContent:Alignment.End}) {
//通讯录
  List({scroller:this.scroller}) {
    this.history()
    this.hot()
    this.signal()
  }
  .onScrollIndex((start:number)=>{
    this.curren = start
  })
  .width('100%')
  //字母表
  AlphabetIndexer({arrayValue:this.alphabets,
    selected:this.curren
  })
    .itemSize(28)
    .font({size:15})
    .selectedFont({size:18})
    .onSelect((index)=>{ 
 
      this.scroller.scrollToIndex(index)
    })
}

image.png

list()组件

历史数据

@Builder
history(){
  ListItemGroup({ header: this.mygroup('历史') }) {
    ListItem() {
      Flex({ wrap: FlexWrap.Wrap }) {
        ForEach(this.historyCitys, (item: string) => {
          Text(item)
            .textAlign(TextAlign.Center)
            .margin({top:10,bottom:10})
            .width('33%')
        })
      }
      .padding(10)
      .width('100%')
    }
  }
  .width('100%')
}

热门城市

@Builder
hot(){
  ListItemGroup({ header: this.mygroup('热门城市') }) {
    ListItem() {
      Flex({ wrap: FlexWrap.Wrap }) {
        ForEach(this.hotCitys, (item: string) => {
          Text(item)
            .textAlign(TextAlign.Center)
            .margin({top:10,bottom:10})
            .width('33%')
        })
      }
      .padding(10)
      .width('100%')
    }
  }
  .width('100%')
}

字母城市 通过双重ForEach循环来实现页面基本布局

@Builder
signal(){
  ForEach(this.cityContentList,(item:BKCityContent,index)=>{
    ListItemGroup({header:this.mygroup(item.initial)}) {
      ForEach(item.cityNameList, (item1: string, index) => {
        ListItem() {
          Text(item1)
            .fontSize(20)
            .margin({ top: 8, bottom: 8 })
        }
      })
    }
    .divider({strokeWidth:2})
  })
}

通过创建控制器来获取listItemGroup开始的索引值

struct Index {
    //创建控制器
  scroller:ListScroller = new ListScroller()
build() {
  Stack({alignContent:Alignment.End}) {
    //设置给list组件
    List({scroller:this.scroller}) {
      this.history()
      this.hot()
      this.signal()
    }
    //获取listItemGroup开始的索引值
    .onScrollIndex((start:number)=>{
    //将获取开始的索引值赋值给 curren 来实现与 AlphabetIndexer 交互
      this.curren = start 
    })
    .width('100%')
  }

AlphabetIndexer()组件

通过获取listItemGroup开始的索引值来实现双方交互

AlphabetIndexer({arrayValue:this.alphabets,
  selected:this.curren//选中下标高亮
})
  .itemSize(28)
  .font({size:15})
  .selectedFont({size:18})

  .onSelect((index)=>{  
    //index 字母表的下标
    // 控制List滚动
    this.scroller.scrollToIndex(index)
  })

整体代码以及运行效果


interface BKCityContent {
  initial: string
  cityNameList: string[]
}
@Entry
@Component
struct Index {
  scroller:ListScroller = new ListScroller()
  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 curren:number = 0
  @Builder
  mygroup(titles: string) {
    Text(titles)
      .fontSize(23)
      .fontColor(Color.Gray)
  }
@Builder
history(){
  ListItemGroup({ header: this.mygroup('历史') }) {
    ListItem() {
      Flex({ wrap: FlexWrap.Wrap }) {
        ForEach(this.historyCitys, (item: string) => {
          Text(item)
            .textAlign(TextAlign.Center)
            .margin({top:10,bottom:10})
            .width('33%')
        })
      }
      .padding(10)
      .width('100%')
    }
  }
  .width('100%')
}
@Builder
hot(){
  ListItemGroup({ header: this.mygroup('热门城市') }) {
    ListItem() {
      Flex({ wrap: FlexWrap.Wrap }) {
        ForEach(this.hotCitys, (item: string) => {
          Text(item)
            .textAlign(TextAlign.Center)
            .margin({top:10,bottom:10})
            .width('33%')
        })
      }
      .padding(10)
      .width('100%')
    }
  }
  .width('100%')
}
@Builder
signal(){
  ForEach(this.cityContentList,(item:BKCityContent,index)=>{
    ListItemGroup({header:this.mygroup(item.initial)}) {
      ForEach(item.cityNameList, (item1: string, index) => {
        ListItem() {
          Text(item1)
            .fontSize(20)
            .margin({ top: 8, bottom: 8 })
        }
      })
    }
    .divider({strokeWidth:2})
  })
}
  build() {
    Stack({alignContent:Alignment.End}) {
      List({scroller:this.scroller}) {
        this.history()
        this.hot()
        this.signal()
      }
      //获取listItemGroup开始的索引值
      .onScrollIndex((start:number)=>{
        this.curren = start
      })
      .width('100%')
      AlphabetIndexer({arrayValue:this.alphabets,
        selected:this.curren//选中下标高亮
      })
        .itemSize(28)
        .font({size:15})
        .selectedFont({size:18})

        .onSelect((index)=>{   //index 字母表的下标
          // 控制滚动
          this.scroller.scrollToIndex(index)
        })
    }
  }
}

运行

PixPin_2024-08-19_16-27-00.gif

最后

本篇到此结束,感谢大家的支持,我们下次再见! 👋