Jetpack Compose复刻Persona5聊天界面:消息间连接线y值获取问题求助
嘿,我现在正在用Jetpack Compose + Kotlin在Android Studio里复刻Persona 5的聊天界面,遇到了个头疼的问题——消息气泡之间的连接线总是对不齐,因为不同消息的长度不一样,我没法正确获取每个消息对应的y坐标,导致线的位置总是跑偏。
目前我是手动给y值加了个偏移量来凑,但这完全是硬编码,消息内容一变就崩了。先给大家讲讲我的问题核心,再贴出代码,有没有懂Android的大佬能帮我看看怎么解决这个y值的问题?
我的问题核心
我需要让消息头像之间的连接线,不管消息内容有多长(换行多少),都能精准地连接上下两个消息的对应位置,但现在我只能靠估算偏移,完全不灵活。
我目前的代码实现
首先是自定义的drawConnectingLine Modifier,用来绘制连接线:
fun Modifier.drawConnectingLine( entry1: Entry, entry2: Entry? ): Modifier { if (entry2 == null) return this return drawWithCache { val linePath = Path() val charCount = entry1.message.text.split("\n").size.toFloat() val bottomOffset = Offset(0f, 200+((charCount-1)*2000f)) val topLeft = entry1.lineCoordinates.leftPoint val topRight = entry1.lineCoordinates.rightPoint val bottomLeft = entry2.lineCoordinates.leftPoint + bottomOffset val bottomRight = entry2.lineCoordinates.rightPoint + bottomOffset val shadowPaint = Paint().apply { color = android.graphics.Color.BLACK alpha = (0.5f * 255).toInt() maskFilter = BlurMaskFilter(4.dp.toPx(), BlurMaskFilter.Blur.NORMAL) } onDrawBehind { // monta o path with(linePath) { rewind() moveTo(topLeft.x, topLeft.y) // canto superior esquerdo lineTo(topRight.x, topRight.y) // canto superior direito lineTo(bottomRight.x, bottomRight.y) // canto inferior direito lineTo(bottomLeft.x, bottomLeft.y) // canto inferior esquerdo close() } // pontos de debug drawCircle(Color.Green, radius = 10f, center = entry1.lineCoordinates.leftPoint) drawCircle(Color.Blue, radius = 10f, center = entry1.lineCoordinates.rightPoint) drawCircle(Color.Black, radius = 10f, center = entry2.lineCoordinates.leftPoint) drawCircle(Color.White, radius = 10f, center = entry2.lineCoordinates.rightPoint) // desenha com Paint Android nativo (com blur) drawIntoCanvas { canvas -> val paint = android.graphics.Paint().apply { color = android.graphics.Color.BLACK alpha = (0.5f * 255).toInt() strokeWidth = 8f style = android.graphics.Paint.Style.STROKE strokeCap = android.graphics.Paint.Cap.ROUND maskFilter = BlurMaskFilter(6.dp.toPx(), BlurMaskFilter.Blur.NORMAL) } canvas.nativeCanvas.drawPath(linePath.asAndroidPath(), paint) } // desenha path "puro" Compose drawPath( linePath, Color.Black, style = Fill ) } } }
然后是把消息转换成Entry的方法,这里我给每个Entry设置了线的坐标,但y值都是基于头像高度的一半,没有考虑消息本身的高度:
fun messagesToEntries( messages: List<Message>, avatarSize: Size, lineWidth: Float = 80f, // largura da linha minShift: Float = 30f, // deslocamento mínimo maxShift: Float = 50f, // deslocamento máximo randomBetween: (Float, Float) -> Float = { min, max -> Random.nextFloat() * (max - min) + min } ): List<Entry> { return messages.mapIndexed { index, message -> val leftX = (avatarSize.width/2f) - (lineWidth / 2f) val rightX = leftX + lineWidth val y = avatarSize.height / 2f val direction = if (index % 2 == 0) 1f else -1f val horizontalShift = randomBetween(minShift, maxShift) * direction val lineCoordinates = LineCoordinates( leftPoint = Offset(leftX + horizontalShift, y), rightPoint = Offset(rightX + horizontalShift, y) ) Entry(message, lineCoordinates) } }
最后是LazyColumn里的布局代码:
val listState = rememberLazyListState() LaunchedEffect(Unit) { viewModel.setMessages(viewModel.messages, avatarSize) } Box(modifier = Modifier.fillMaxSize()) { LazyColumn (state = listState, modifier = Modifier .fillMaxSize(), //.drawConnectingLines(uiState.entries), verticalArrangement = Arrangement.spacedBy(16.dp), ) { items(uiState.entries.size) { index -> val entry = uiState.entries[index] val proxEntry = uiState.entries.getOrNull(index + 1) Box( modifier = Modifier //.fillMaxWidth() .then( Modifier.drawConnectingLine(entry, proxEntry).matchParentSize() // último não desenha ) ) { MessageRow(entry = entry, generalViewModel = viewModel) //Não tá atualizando os valores /* if (prevEntry != null && entry.lineCoordinates.leftPoint.y != 0f && prevEntry.lineCoordinates.leftPoint.y != 0f ) { DebugLineCoordinates( entry = prevEntry, modifier = Modifier.matchParentSize(), color1 = Color.Blue, color2 = Color.Green ) }*/ HorizontalCenterDebugCircle(modifier = Modifier.matchParentSize()) } } } }
我尝试过的调试
我在代码里加了几个调试用的圆,用来标记每个Entry的线坐标点,但发现上下两个Entry的y值几乎是一样的——因为我在messagesToEntries里把y值固定成了avatarSize.height / 2f,完全没考虑消息本身的高度,所以只能手动加偏移,但偏移量又没法根据消息长度动态计算。
现在我最困惑的就是:怎么才能获取到每个消息Item在LazyColumn里的实际y坐标,或者说每个消息气泡底部的y值?这样连接线就能精准地从上个消息的位置连到下个消息的位置了。有没有大佬能给我指个方向,或者告诉我Jetpack Compose里有没有什么方法能获取到组件的实际布局位置呀?
内容来源于stack exchange

