一、前言
该功能是一个左侧为字符索引列表,右侧为联系人列表的实现,点击左侧字符索引可以自动滑动到对应联系人条目。该功能代码实现绝大多数有通义灵码完成。实现效果如下:
关键代码
data class Contact(val name: String)
class ContactsPage {
val contacts = listOf(
Contact("Alice"),
Contact("Bob"),
Contact("Charlie"),
Contact("David"),
Contact("Eve"),
Contact("Frank"),
Contact("Grace"),
Contact("Heidi"),
Contact("Ivan"),
Contact("John"),
Contact("Karen"),
Contact("Larry"),
Contact("Mary"),
Contact("Nancy"),
Contact("Olivia"),
Contact("Paul"),
Contact("Quincy"),
Contact("Rachel"),
Contact("Steve"),
Contact("Tom"),
Contact("Ursula"),
Contact("Victor"),
Contact("Wendy"),
Contact("Xavier"),
Contact("Yolanda"),
Contact("Zoe")
)
fun alphabetIndexer(contact: Contact) = contact.name.first().toString().uppercase()
@Composable
fun IndexedContactsList(contacts: List<Contact>) {
val indexer = { contact: Contact -> alphabetIndexer(contact) }
val sections = contacts.groupBy(indexer)
val entries = sections.entries.toList()
// 记录每个字母索引的位置
val letterPositions = remember { mutableMapOf<String, Int>() }
val listState = remember { LazyListState() }
// 计算每个字母索引的位置
var currentLetter = ""
for ((position, contact) in contacts.withIndex()) {
val letter = indexer(contact)
if (letter != currentLetter) {
letterPositions[letter] = position
currentLetter = letter
}
}
val coroutineScope = rememberCoroutineScope()
Row(
modifier = Modifier.fillMaxSize()
) {
// 右侧字母索引列表
LazyColumn(
modifier = Modifier
.weight(0.2f) // 设置宽度比例
.fillMaxHeight()
.padding(8.dp)
) {
itemsIndexed(entries) { index, entry ->
Text(
text = entry.key,
modifier = Modifier
.fillMaxWidth()
.clickable {
/* Handle click */
// 点击字母索引时滚动到对应位置
coroutineScope.launch {
listState.animateScrollToItem(letterPositions[entry.key] ?: 0)
}
}
.padding(vertical = 8.dp),
style = MaterialTheme.typography.subtitle1,
color = Color.Gray
)
}
}
// 左侧联系人列表
LazyColumn(
state = listState,
modifier = Modifier
.weight(0.8f) // 设置宽度比例
.fillMaxHeight()
.padding(8.dp)
) {
itemsIndexed(contacts) { index, contact ->
val letter = indexer(contact)
if (index == 0 || indexer(contacts[index - 1]) != letter) {
Text(
text = letter,
modifier = Modifier.padding(
start = 16.dp,
top = 8.dp,
end = 16.dp,
bottom = 4.dp
),
style = MaterialTheme.typography.subtitle1,
color = Color.Gray
)
}
Text(
text = contact.name,
modifier = Modifier
.fillMaxWidth()
.clickable { /* Handle click */ }
.padding(horizontal = 16.dp, vertical = 8.dp),
style = MaterialTheme.typography.body1
)
}
}
}
}
@Preview(showBackground = true)
@Composable
fun PreviewIndexedContactsList() {
MaterialTheme {
IndexedContactsList(contacts)
}
}
}