您好,登錄后才能下訂單哦!
這篇文章主要介紹“Android Compose如何實現聯系人列表”,在日常操作中,相信很多人在Android Compose如何實現聯系人列表問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”Android Compose如何實現聯系人列表”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!
效果:
data class ContactEntity( val letter: Char, val name: String, val color: Color ) /** * 獲取聯系人數據 */ fun getContactData(): MutableList<ContactEntity> { val contactList = mutableListOf<ContactEntity>() (65..90).forEach { letter -> // val random = (5..20).random() val random = 5 repeat(random) { index -> contactList.add( ContactEntity( letter = letter.toChar(), name = "聯系人 $index", color = Color( red = (0..255).random(), blue = (0..255).random(), green = (0..255).random() ) ) ) } } return contactList } /** * 獲取首字母列表 */ fun getCharList(): MutableList<Char> { val charList = mutableListOf<Char>() (65..90).forEach { letter -> charList.add(letter.toChar()) } return charList }
整體是由Box布局包裹, 左側是LazyColumn 右側放置自定義布局
左側LazyColumn用state來觀察滑動的一些參數,來控制右側字母的位置
右側使用canvas繪制字母 在觸控的時候使用scrollToItem準確的定位到左側應該滑動的位置,感覺有些不準確,不知道是計算的問題還是bug,在谷歌上看到類似的issue提交。
中間顯示滑動到的字母,也可以使用貝塞爾曲線來繪制類似于水滴的樣式,懶得去搞了。
利用derivedStateOf來跟蹤變化的remember數據,這樣可以減少性能的損耗,但是感覺有個地方沒處理好,就是觸摸的時候需要改變,滑動的時候也需要改變,而derivedStateOf是val類型的,不能直接賦值,所以又設置了一個remember變量。有可以優化的地方請指出。
數據和樣式都可以自定義,非常方便
@OptIn(ExperimentalTextApi::class) @Composable fun ContactPage(navCtrl: NavHostController, title: String) { //聯系人數據 val contactList = getContactData() //字母數據 val charList = getCharList() val offsetY = with(LocalDensity.current) { 20.dp.toPx() } val offsetX = with(LocalDensity.current) { 25.dp.toPx() } val coroutineScope = rememberCoroutineScope() val textMeasure = rememberTextMeasurer() val state = rememberLazyListState() //觸摸的位置 var touchOffset by remember() { mutableStateOf(Offset.Zero) } //觸摸到的上一次的字母下標 var touchIndexLast by remember { mutableStateOf(-1) } //觸摸的字母的下標 val touchIndex = remember(touchOffset.y) { derivedStateOf { if (touchOffset.y > 0f) { //通過偏移的倍數計算滑動到哪個字母的位置了 val y = (touchOffset.y / offsetY).roundToInt() if (y in 0..25) { touchIndexLast = y y } else { touchIndexLast } } else touchIndexLast } } //觸摸到的字符的index val touchLetterIndex = remember { derivedStateOf { if (state.isScrollInProgress && state.layoutInfo.visibleItemsInfo.isNotEmpty()) { val key = state.layoutInfo.visibleItemsInfo[0].key if (key is Int && key < contactList.size) { val letter = contactList[key].letter val findIndex = charList.indexOfFirst { it == letter } findIndex } else { touchIndex.value } } else { touchIndex.value } } } //上一次選擇的letter var lastLetter by remember { mutableStateOf("") } //滑動到的letter val scrollLetter = remember { derivedStateOf { if (touchIndex.value > 0) { val letter = (65 + touchIndex.value).toChar() coroutineScope.launch { //找到相應的item val index = getIndex(contactList, letter) state.scrollToItem(index, scrollOffset = 20) } val str = letter.toString() lastLetter = str str } else { lastLetter } } } CommonToolbar(navCtrl, title) { Box(modifier = Modifier.fillMaxSize()) { LazyColumn(modifier = Modifier.fillMaxWidth(), state = state, content = { contactList.forEachIndexed { index, contactEntity -> item(key = index) { Column( modifier = Modifier.fillMaxWidth() ) { Row( modifier = Modifier .fillMaxWidth() .padding(horizontal = 10.dp), verticalAlignment = Alignment.CenterVertically ) { Box( modifier = Modifier .size(50.dp) .clip(CircleShape) .background( color = contactEntity.color, ), contentAlignment = Alignment.Center ) { Text( text = "${contactEntity.letter}", fontSize = 16.sp, color = Color.White, fontWeight = FontWeight.SemiBold, ) } Text( text = contactEntity.name, modifier = Modifier .padding(20.dp) .weight(1f) .padding(horizontal = 10.dp, vertical = 16.dp) ) } Divider() } } } item { Text( text = "共${contactList.size}聯系人", fontSize = 12.sp, color = Color(0xFF333333), modifier = Modifier .fillMaxWidth() .padding(vertical = 20.dp), textAlign = TextAlign.Center ) } }) //繪制右側的26個字母 Canvas(modifier = Modifier .padding(top = 30.dp) .width(50.dp) .fillMaxHeight() .align(Alignment.TopEnd) .pointerInput(Unit) { coroutineScope { while (true) { // down事件 val downPointerInputChange = awaitPointerEventScope { awaitFirstDown() } // 如果位置不在手指按下的位置,先動畫的形式過度到手指按下的位置 if (touchOffset.x != downPointerInputChange.position.x && touchOffset.y != downPointerInputChange.position.y) { launch { touchOffset = downPointerInputChange.position } } // touch Move事件 // 滑動的時候,box隨著手指的移動去移動 awaitPointerEventScope { drag(downPointerInputChange.id, onDrag = { touchOffset = it.position }) } // 在手指彈起的時候,才通過動畫的形式,回到原點的位置 val dragUpOrCancelPointerInputChange = awaitPointerEventScope { awaitDragOrCancellation(downPointerInputChange.id) } // 等于空,說明已經抬起 if (dragUpOrCancelPointerInputChange == null) { launch { touchOffset = Offset.Zero } } } } }, onDraw = { charList.forEachIndexed { index, char -> drawText( size = Size(width = offsetY, offsetY), textMeasurer = textMeasure, text = "$char", style = TextStyle( fontWeight = if (touchLetterIndex.value == index) FontWeight.SemiBold else FontWeight.Medium, color = if (touchLetterIndex.value == index) Color.Blue else Color( 0xFF333333 ), textAlign = TextAlign.Center, fontSize = if (touchLetterIndex.value == index) 16.sp else 14.sp ), topLeft = Offset(offsetX, offsetY * index), ) } }) //中間顯示的大寫字母 val textMeasurer1 = rememberTextMeasurer() if (touchOffset.x != 0f && touchOffset.y != 0f && scrollLetter.value.isNotEmpty()) { val annotatedString = AnnotatedString( scrollLetter.value, spanStyle = SpanStyle( fontWeight = FontWeight.Medium, color = Color(0xFF333333), fontSize = 20.sp, ) ) val textLayoutResult = textMeasurer1.measure(text = annotatedString) val textSize = textLayoutResult.size Canvas(modifier = Modifier .align(Alignment.Center) .requiredSize(width = 50.dp, height = 50.dp), onDraw = { //底部顏色 drawRoundRect( color = Color(0xA403A9F4), cornerRadius = CornerRadius(10f, 10f) ) //繪制字母 drawText( textMeasurer = textMeasurer1, text = annotatedString, topLeft = Offset( (size.width - textSize.width) / 2f, (size.height - textSize.height) / 2f ) ) }) } } } }
private fun getIndex(list: MutableList<ContactEntity>, letter: Char): Int { val findIndex = list.indexOfFirst { contactEntity -> contactEntity.letter == letter } return findIndex }
到此,關于“Android Compose如何實現聯系人列表”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續學習更多相關知識,請繼續關注億速云網站,小編會繼續努力為大家帶來更多實用的文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。