DEV Community

HarmonyOS
HarmonyOS

Posted on

Fast Navigation Guide : ArcAlphabetIndexer in HarmonyOS Next Using ArkTS and ArkUI

Read the original article:Fast Navigation Guide : ArcAlphabetIndexer in HarmonyOS Next Using ArkTS and ArkUI

Hi, in this guide, you’ll learn how to use ArcAlphabetIndexer in HarmonyOS Next with ArkTS and ArkUI to build fast and circular-friendly navigation in your wearable apps.

📘 Introduction

Imagine you’re building a vocabulary or contacts app for a smartwatch. Users are swiping endlessly to find “Zebra” or “Tom.” Wouldn’t it be better if they could just tap a letter and jump there instantly?

That’s where ArcAlphabetIndexer comes in.
This component allows lightning-fast navigation through alphabetically sorted lists on circular screens, like HarmonyOS-powered smartwatches.

In this guide, you’ll learn how to:

  • Build and sort a vocabulary list
  • Create an index mapping for letters
  • Connect an ArcList with ArcAlphabetIndexer
  • Keep scrolling and selection in sync
  • Optimize for circular UI/UX

This tutorial uses ArkTS and ArkUI, targets HarmonyOS Next (API version 18+).

🧩 What is ArcAlphabetIndexer?

ArcAlphabetIndexer is a specialized circular component that shows all letters (A–Z and #) around the edge of a round screen. Users can select a letter to instantly scroll a list to the matching section.

Key Features

  • Optimized for circular UIs
  • Alphabetical fast-jumping
  • Synchronizes with list scroll position
  • Works great with ArcList

⚙️ How to Implement ArcAlphabetIndexer Step by Step

🔤 1. Create the Word List

We start by defining a vocabulary list, where each item contains a word and its meaning.

interface Word { text: string; meaning: string; } private rawWords: Word[] = [ { text: "Apple", meaning: "A red or green fruit" }, { text: "Zebra", meaning: "A striped animal" }, { text: "Ball", meaning: "A round object" }, ... ]; 
Enter fullscreen mode Exit fullscreen mode

🔠 2. Sort the List and Build Letter Index Map

Next, we sort the list alphabetically and build a map to track the first appearance of each letter.

sortAndGroupWords() { this.sortedWords = this.rawWords.sort((a, b) => { return a.text.localeCompare(b.text); }); this.letterIndexMap.clear(); let currentLetter = ''; this.sortedWords.forEach((word, index) => { const firstLetter = this.getFirstLetter(word.text); if (firstLetter !== currentLetter) { currentLetter = firstLetter; this.letterIndexMap.set(firstLetter, index); } }); } 
Enter fullscreen mode Exit fullscreen mode

📜 3. Display Words Using ArcList and Track Scrolling

Use ArcList to display the words. We also detect the center item while scrolling, and update the selected letter in ArcAlphabetIndexer accordingly.

ArcList({ scroller: this.scrollerForList, initialIndex: 0, header: this.header }) { ForEach(this.sortedWords, (word: Word, index: number) => { ArcListItem() { … } … } }) } .onScrollIndex((firstIndex: number, lastIndex: number, centerIndex: number) => { if (centerIndex < this.sortedWords.length) { const currentWord = this.sortedWords[centerIndex]; const letter = this.getFirstLetter(currentWord.text); const letterIndex = this.fullValue.indexOf(letter); if (letterIndex !== -1) { this.indexerIndex = letterIndex; } } }) … 
Enter fullscreen mode Exit fullscreen mode

🔁 4. Enable Fast Navigation with ArcAlphabetIndexer

Here we wire up the ArcAlphabetIndexer to scroll the list to the correct item when a letter is selected.

ArcAlphabetIndexer({ arrayValue: this.fullValue, selected: 0 }) .onSelect((index: number) => { this.indexerIndex = index; const selectedLetter = this.fullValue[index]; if (selectedLetter !== '#' && this.letterIndexMap.has(selectedLetter)) { const wordIndex = this.letterIndexMap.get(selectedLetter); if (wordIndex !== undefined) { this.scrollerForList.scrollToIndex(wordIndex); } } }) … 
Enter fullscreen mode Exit fullscreen mode

📌 When the user taps “M”, the list instantly jumps to the first “M” word. Smooth and fast!

💻 5. Full Code Example

import { LengthMetrics, ColorMetrics, ArcList, ArcListItem, ArcListAttribute, ArcAlphabetIndexer, ArcAlphabetIndexerAttribute, ComponentContent } from '@kit.ArkUI'; interface Word { text: string; meaning: string; } @Builder function buildText() { Column() { Text('Word List') .fontSize(26) .fontWeight(FontWeight.Bold) .fontColor(Color.White) }.margin(6) } @Entry @Component struct Index { private fullValue: 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' ]; private rawWords: Word[] = [ { text: "Apple", meaning: "A red or green fruit" }, { text: "Jump", meaning: "To leap up" }, { text: "Zebra", meaning: "A striped animal" }, { text: "Fan", meaning: "Cools the air" }, { text: "X-ray", meaning: "Used to see bones" }, { text: "Desk", meaning: "A table for work or study" }, { text: "Orange", meaning: "A citrus fruit" }, { text: "Train", meaning: "Runs on tracks" }, { text: "Rain", meaning: "Water from sky" }, { text: "Lion", meaning: "King of jungle" }, { text: "Mouse", meaning: "A small rodent" }, { text: "Kite", meaning: "Flies in the wind" }, { text: "Unicorn", meaning: "Mythical horse" }, { text: "Queen", meaning: "Female ruler" }, { text: "Whale", meaning: "Large sea animal" }, { text: "Car", meaning: "A vehicle with wheels" }, { text: "Needle", meaning: "Used in sewing" }, { text: "Egg", meaning: "Laid by birds" }, { text: "Xylophone", meaning: "A musical instrument" }, { text: "Guitar", meaning: "A musical instrument" }, { text: "Ball", meaning: "A round object for playing" }, { text: "Violin", meaning: "A string instrument" }, { text: "Book", meaning: "Something you read" }, { text: "Lamp", meaning: "Gives light" }, { text: "Van", meaning: "A type of vehicle" }, { text: "Yellow", meaning: "A bright color" }, { text: "Dog", meaning: "A loyal pet animal" }, { text: "Queen", meaning: "Female ruler" }, { text: "Jelly", meaning: "A soft sweet food" }, { text: "House", meaning: "A place to live" }, { text: "Igloo", meaning: "A snow house" }, { text: "Zoo", meaning: "Place for animals" }, { text: "Ice", meaning: "Frozen water" }, { text: "Moon", meaning: "Earth's satellite" }, { text: "Pen", meaning: "Used to write" }, { text: "Quilt", meaning: "A warm bedcover" }, { text: "Cat", meaning: "A small pet animal" }, { text: "Nest", meaning: "A bird's home" }, { text: "Key", meaning: "Opens doors" }, { text: "Ant", meaning: "A small insect" }, { text: "Yacht", meaning: "A luxury boat" }, { text: "Piano", meaning: "A musical instrument" }, { text: "Elephant", meaning: "A large grey animal" }, { text: "Game", meaning: "Something you play" }, { text: "Hat", meaning: "Worn on the head" }, { text: "Oven", meaning: "Used for baking" }, { text: "Sun", meaning: "Shines in the sky" }, { text: "Water", meaning: "Essential liquid" }, { text: "Jacket", meaning: "Worn for warmth" }, { text: "Robot", meaning: "A programmable machine" } ] @State sortedWords: Word[] = []; @State letterIndexMap: Map<string, number> = new Map(); private scrollerForList: Scroller = new Scroller(); @State indexerIndex: number = 0; @State selectedWord: Word | null = null; private watchSize: string = '466px'; private itemSize: number = 16; context: UIContext = this.getUIContext() header: ComponentContent<Object> = new ComponentContent(this.context, wrapBuilder(buildText)); aboutToAppear() { this.sortAndGroupWords(); } getFirstLetter(word: string): string { return word.charAt(0).toUpperCase(); } sortAndGroupWords() { this.sortedWords = this.rawWords.sort((a, b) => { return a.text.localeCompare(b.text); }); this.letterIndexMap.clear(); let currentLetter = ''; this.sortedWords.forEach((word, index) => { const firstLetter = this.getFirstLetter(word.text); if (firstLetter !== currentLetter) { currentLetter = firstLetter; this.letterIndexMap.set(firstLetter, index); } }); } build() { Column() { Row() { Stack() { ArcList({ scroller: this.scrollerForList, initialIndex: 0, header: this.header }) { ForEach(this.sortedWords, (word: Word, index: number) => { ArcListItem() { Column({space: 6}) { Text(word.text) .fontSize(18) .fontWeight(FontWeight.Bold) .textAlign(TextAlign.Center) .fontColor("#FF0B1F3B") Text(word.meaning) .fontSize(12) .fontColor(Color.Gray) .textAlign(TextAlign.Center) .maxLines(2) .textOverflow({ overflow: TextOverflow.Ellipsis }) .fontColor(Color.Gray) } .backgroundColor(Color.White) .width('90%') .height(50) .padding(8) .borderRadius(23) .justifyContent(FlexAlign.Center) .onClick(() => { this.selectedWord = word; }) } }) } .scrollBar(BarState.Off) .onScrollIndex((firstIndex: number, lastIndex: number, centerIndex: number) => { if (centerIndex < this.sortedWords.length) { const currentWord = this.sortedWords[centerIndex]; const letter = this.getFirstLetter(currentWord.text); const letterIndex = this.fullValue.indexOf(letter); if (letterIndex !== -1) { this.indexerIndex = letterIndex; } } }) .borderWidth(1) .width(this.watchSize) .height(this.watchSize) .borderRadius(this.watchSize) .space(LengthMetrics.px(4)) ArcAlphabetIndexer({ arrayValue: this.fullValue, selected: 0 }) .autoCollapse(true) .width(this.watchSize) .height(this.watchSize) .usePopup(false) .selected(this.indexerIndex) .onSelect((index: number) => { this.indexerIndex = index; const selectedLetter = this.fullValue[index]; if (selectedLetter !== '#' && this.letterIndexMap.has(selectedLetter)) { const wordIndex = this.letterIndexMap.get(selectedLetter); if (wordIndex !== undefined) { this.scrollerForList.scrollToIndex(wordIndex); } } }) .borderWidth(1) .hitTestBehavior(HitTestMode.Transparent) .selectedColor(ColorMetrics.resourceColor(0xFFFFFF)) .selectedBackgroundColor(ColorMetrics.resourceColor(0x1F71FF)) .color(ColorMetrics.resourceColor(0xFFFFFF)) .popupColor(ColorMetrics.resourceColor(0xFFFFFF)) .popupBackground(ColorMetrics.resourceColor(0xD8404040)) .itemSize(LengthMetrics.px(this.itemSize)) .selectedFont({ size: '11.0fp', style: FontStyle.Normal, weight: 500, family: 'HarmonyOS Sans' }) .font({ size: '11.0fp', style: FontStyle.Normal, weight: 500, family: 'HarmonyOS Sans' }) } .width('100%') .height('100%') } .width('100%') .height('100%') } .width('100%') .height('100%') .linearGradient({ direction: GradientDirection.Bottom, colors: [ ["#FF0B1F3B", 0.0], ["#FF00050A", 1], ] }) } } 
Enter fullscreen mode Exit fullscreen mode

🧠 Tips and Warnings

Use short words to avoid layout overflow on round screens.

Sort list alphabetically before using the indexer.

Use ArcList and ArcAlphabetIndexer inside a Stack to align them properly.

⚠️ Avoid overcrowding the UI with too many elements.

⚠️ Works only on API 18+ and circular devices.

✅ Conclusion

ArcAlphabetIndexer gives your wearable apps a professional, responsive, and elegant navigation experience — just like scrolling through contacts or vocabulary with ease.

This component is lightweight but powerful, especially when combined with ArcList.

🚀 Try it in your next HarmonyOS Next wearable project and elevate your UX!

📚 References

For more information about ArcList, here is an article written by my teammate (Bilal Basboz) : ArcList
For more information about ArcAlphabetIndexer, please visit the link : ArcAlphabetIndexer

Written by Sefa Koyuncu

Top comments (0)