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" },
...
];
🔠 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);
}
});
}
📜 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;
}
}
})
…
🔁 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);
}
}
})
…
📌 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],
]
})
}
}
🧠 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
Top comments (0)