您好,登錄后才能下訂單哦!
這篇文章主要介紹“Android Compose如何實(shí)現(xiàn)聯(lián)系人列表”,在日常操作中,相信很多人在Android Compose如何實(shí)現(xiàn)聯(lián)系人列表問題上存在疑惑,小編查閱了各式資料,整理出簡(jiǎn)單好用的操作方法,希望對(duì)大家解答”Android Compose如何實(shí)現(xiàn)聯(lián)系人列表”的疑惑有所幫助!接下來,請(qǐng)跟著小編一起來學(xué)習(xí)吧!
效果:
data class ContactEntity( val letter: Char, val name: String, val color: Color ) /** * 獲取聯(lián)系人數(shù)據(jù) */ 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 = "聯(lián)系人 $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布局包裹, 左側(cè)是LazyColumn 右側(cè)放置自定義布局
左側(cè)LazyColumn用state來觀察滑動(dòng)的一些參數(shù),來控制右側(cè)字母的位置
右側(cè)使用canvas繪制字母 在觸控的時(shí)候使用scrollToItem準(zhǔn)確的定位到左側(cè)應(yīng)該滑動(dòng)的位置,感覺有些不準(zhǔn)確,不知道是計(jì)算的問題還是bug,在谷歌上看到類似的issue提交。
中間顯示滑動(dòng)到的字母,也可以使用貝塞爾曲線來繪制類似于水滴的樣式,懶得去搞了。
利用derivedStateOf來跟蹤變化的remember數(shù)據(jù),這樣可以減少性能的損耗,但是感覺有個(gè)地方?jīng)]處理好,就是觸摸的時(shí)候需要改變,滑動(dòng)的時(shí)候也需要改變,而derivedStateOf是val類型的,不能直接賦值,所以又設(shè)置了一個(gè)remember變量。有可以優(yōu)化的地方請(qǐng)指出。
數(shù)據(jù)和樣式都可以自定義,非常方便
@OptIn(ExperimentalTextApi::class) @Composable fun ContactPage(navCtrl: NavHostController, title: String) { //聯(lián)系人數(shù)據(jù) val contactList = getContactData() //字母數(shù)據(jù) 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) } //觸摸到的上一次的字母下標(biāo) var touchIndexLast by remember { mutableStateOf(-1) } //觸摸的字母的下標(biāo) val touchIndex = remember(touchOffset.y) { derivedStateOf { if (touchOffset.y > 0f) { //通過偏移的倍數(shù)計(jì)算滑動(dòng)到哪個(gè)字母的位置了 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("") } //滑動(dòng)到的letter val scrollLetter = remember { derivedStateOf { if (touchIndex.value > 0) { val letter = (65 + touchIndex.value).toChar() coroutineScope.launch { //找到相應(yīng)的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}聯(lián)系人", fontSize = 12.sp, color = Color(0xFF333333), modifier = Modifier .fillMaxWidth() .padding(vertical = 20.dp), textAlign = TextAlign.Center ) } }) //繪制右側(cè)的26個(gè)字母 Canvas(modifier = Modifier .padding(top = 30.dp) .width(50.dp) .fillMaxHeight() .align(Alignment.TopEnd) .pointerInput(Unit) { coroutineScope { while (true) { // down事件 val downPointerInputChange = awaitPointerEventScope { awaitFirstDown() } // 如果位置不在手指按下的位置,先動(dòng)畫的形式過度到手指按下的位置 if (touchOffset.x != downPointerInputChange.position.x && touchOffset.y != downPointerInputChange.position.y) { launch { touchOffset = downPointerInputChange.position } } // touch Move事件 // 滑動(dòng)的時(shí)候,box隨著手指的移動(dòng)去移動(dòng) awaitPointerEventScope { drag(downPointerInputChange.id, onDrag = { touchOffset = it.position }) } // 在手指彈起的時(shí)候,才通過動(dòng)畫的形式,回到原點(diǎn)的位置 val dragUpOrCancelPointerInputChange = awaitPointerEventScope { awaitDragOrCancellation(downPointerInputChange.id) } // 等于空,說明已經(jīng)抬起 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 }
到此,關(guān)于“Android Compose如何實(shí)現(xiàn)聯(lián)系人列表”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實(shí)踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識(shí),請(qǐng)繼續(xù)關(guān)注億速云網(wǎng)站,小編會(huì)繼續(xù)努力為大家?guī)砀鄬?shí)用的文章!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。