feat:发帖时增加 emoji 表情功能

This commit is contained in:
amos
2025-12-17 10:18:44 +08:00
parent 8ebc0965b1
commit b05d53ab77
4 changed files with 260 additions and 18 deletions

View File

@@ -524,22 +524,33 @@ private fun ZoomableImage(
Box(
modifier = Modifier
.fillMaxSize()
.pointerInput(scale) {
detectTransformGestures { _, pan, zoom, _ ->
val newScale = (scale * zoom).coerceIn(1f, 4f)
// 只有在缩放或已经放大时才处理手势
if (zoom != 1f || scale > 1f) {
scale = newScale
if (scale > 1f) {
offsetX += pan.x
offsetY += pan.y
} else {
offsetX = 0f
offsetY = 0f
.then(
// 只有在放大状态下才启用手势检测,否则让 HorizontalPager 处理滑动
if (scale > 1f) {
Modifier.pointerInput(Unit) {
detectTransformGestures { _, pan, zoom, _ ->
val newScale = (scale * zoom).coerceIn(1f, 4f)
scale = newScale
if (scale > 1f) {
offsetX += pan.x
offsetY += pan.y
} else {
offsetX = 0f
offsetY = 0f
}
}
}
} else {
Modifier.pointerInput(Unit) {
detectTransformGestures { _, _, zoom, _ ->
// 未放大时只处理缩放手势(双指捏合)
if (zoom != 1f) {
scale = (scale * zoom).coerceIn(1f, 4f)
}
}
}
}
}
)
.clickable {
if (scale == 1f) {
onTap()

View File

@@ -37,6 +37,7 @@ fun CreatePostScreen(
) {
var content by remember { mutableStateOf("") }
var selectedImages by remember { mutableStateOf<List<Uri>>(emptyList()) }
var showEmojiPicker by remember { mutableStateOf(false) }
val imagePicker = rememberLauncherForActivityResult(
contract = ActivityResultContracts.GetMultipleContents()
@@ -232,7 +233,7 @@ fun CreatePostScreen(
// Emoji Button
IconButton(
onClick = { },
onClick = { showEmojiPicker = true },
modifier = Modifier.size(40.dp)
) {
Icon(
@@ -264,4 +265,113 @@ fun CreatePostScreen(
}
}
}
// Emoji Picker
if (showEmojiPicker) {
CreatePostEmojiPicker(
onDismiss = { showEmojiPicker = false },
onEmojiSelected = { emoji ->
content = content + emoji
showEmojiPicker = false
}
)
}
}
@Composable
private fun CreatePostEmojiPicker(
onDismiss: () -> Unit,
onEmojiSelected: (String) -> Unit
) {
val emojis = listOf(
"😀", "😃", "😄", "😁", "😆", "😅", "🤣", "😂", "🙂", "😊",
"😇", "🥰", "😍", "🤩", "😘", "😗", "😚", "😋", "😛", "😜",
"🤪", "😝", "🤑", "🤗", "🤭", "🤫", "🤔", "🤐", "🤨", "😐",
"😑", "😶", "😏", "😒", "🙄", "😬", "🤥", "😌", "😔", "😪",
"🤤", "😴", "😷", "🤒", "🤕", "🤢", "🤮", "🥴", "😵", "🤯",
"👍", "👎", "👏", "🙌", "🤝", "👊", "", "🤛", "🤜", "🤞",
"✌️", "🤟", "🤘", "👌", "🤌", "👈", "👉", "👆", "👇", "☝️",
"💪", "🦾", "🙏", "✍️", "🤳", "💅", "🖐️", "", "👋", "🤚",
"❤️", "🧡", "💛", "💚", "💙", "💜", "🖤", "🤍", "🤎", "💔",
"❣️", "💕", "💞", "💓", "💗", "💖", "💘", "💝", "💟", "♥️",
"🎉", "🎊", "🎈", "🎁", "🏆", "🥇", "🔥", "", "🌟", "",
"💯", "💢", "💥", "💫", "💦", "💨", "🕳️", "💣", "💬", "👀"
)
androidx.compose.ui.window.Dialog(
onDismissRequest = onDismiss,
properties = androidx.compose.ui.window.DialogProperties(usePlatformDefaultWidth = false)
) {
Surface(
shape = RoundedCornerShape(28.dp),
color = Color.White,
shadowElevation = 16.dp,
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp)
) {
Column(modifier = Modifier.padding(24.dp)) {
// 标题栏
Row(
modifier = Modifier
.fillMaxWidth()
.padding(bottom = 16.dp),
verticalAlignment = Alignment.CenterVertically
) {
Box(
modifier = Modifier
.size(4.dp, 24.dp)
.clip(RoundedCornerShape(2.dp))
.background(Brand500)
)
Spacer(modifier = Modifier.width(12.dp))
Text(
text = "选择表情",
fontWeight = FontWeight.Bold,
fontSize = 20.sp,
color = Slate900
)
Spacer(modifier = Modifier.weight(1f))
IconButton(
onClick = onDismiss,
modifier = Modifier.size(32.dp)
) {
Icon(
imageVector = Icons.Filled.Close,
contentDescription = "关闭",
tint = Slate400,
modifier = Modifier.size(20.dp)
)
}
}
// 表情网格
androidx.compose.foundation.lazy.grid.LazyVerticalGrid(
columns = androidx.compose.foundation.lazy.grid.GridCells.Fixed(6),
modifier = Modifier
.fillMaxWidth()
.heightIn(max = 400.dp),
horizontalArrangement = Arrangement.SpaceEvenly,
verticalArrangement = Arrangement.spacedBy(12.dp),
contentPadding = PaddingValues(vertical = 8.dp)
) {
items(emojis.size) { index ->
Box(
modifier = Modifier
.fillMaxWidth()
.aspectRatio(1f)
.clip(RoundedCornerShape(8.dp))
.clickable { onEmojiSelected(emojis[index]) },
contentAlignment = Alignment.Center
) {
Text(
text = emojis[index],
fontSize = 32.sp
)
}
}
}
}
}
}
}

View File

@@ -13,6 +13,7 @@ import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Close
import androidx.compose.material.icons.outlined.Image
import androidx.compose.material.icons.outlined.Mood
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
@@ -37,6 +38,7 @@ fun EditPostScreen(
var content by remember { mutableStateOf(post.content) }
var existingImages by remember { mutableStateOf(post.media.map { it.mediaUrl }) }
var newImages by remember { mutableStateOf<List<Uri>>(emptyList()) }
var showEmojiPicker by remember { mutableStateOf(false) }
val totalImages = existingImages.size + newImages.size
@@ -227,7 +229,8 @@ fun EditPostScreen(
.fillMaxWidth()
.padding(horizontal = 16.dp, vertical = 12.dp)
.navigationBarsPadding(),
verticalAlignment = Alignment.CenterVertically
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
IconButton(
onClick = { imagePicker.launch("image/*") },
@@ -241,6 +244,124 @@ fun EditPostScreen(
modifier = Modifier.size(24.dp)
)
}
IconButton(
onClick = { showEmojiPicker = true },
modifier = Modifier.size(40.dp)
) {
Icon(
Icons.Outlined.Mood,
contentDescription = "添加表情",
tint = Brand500,
modifier = Modifier.size(24.dp)
)
}
}
}
// Emoji Picker
if (showEmojiPicker) {
EditPostEmojiPicker(
onDismiss = { showEmojiPicker = false },
onEmojiSelected = { emoji ->
content = content + emoji
showEmojiPicker = false
}
)
}
}
@Composable
private fun EditPostEmojiPicker(
onDismiss: () -> Unit,
onEmojiSelected: (String) -> Unit
) {
val emojis = listOf(
"😀", "😃", "😄", "😁", "😆", "😅", "🤣", "😂", "🙂", "😊",
"😇", "🥰", "😍", "🤩", "😘", "😗", "😚", "😋", "😛", "😜",
"🤪", "😝", "🤑", "🤗", "🤭", "🤫", "🤔", "🤐", "🤨", "😐",
"😑", "😶", "😏", "😒", "🙄", "😬", "🤥", "😌", "😔", "😪",
"🤤", "😴", "😷", "🤒", "🤕", "🤢", "🤮", "🥴", "😵", "🤯",
"👍", "👎", "👏", "🙌", "🤝", "👊", "", "🤛", "🤜", "🤞",
"✌️", "🤟", "🤘", "👌", "🤌", "👈", "👉", "👆", "👇", "☝️",
"💪", "🦾", "🙏", "✍️", "🤳", "💅", "🖐️", "", "👋", "🤚",
"❤️", "🧡", "💛", "💚", "💙", "💜", "🖤", "🤍", "🤎", "💔",
"❣️", "💕", "💞", "💓", "💗", "💖", "💘", "💝", "💟", "♥️",
"🎉", "🎊", "🎈", "🎁", "🏆", "🥇", "🔥", "", "🌟", "",
"💯", "💢", "💥", "💫", "💦", "💨", "🕳️", "💣", "💬", "👀"
)
androidx.compose.ui.window.Dialog(
onDismissRequest = onDismiss,
properties = androidx.compose.ui.window.DialogProperties(usePlatformDefaultWidth = false)
) {
Surface(
shape = RoundedCornerShape(28.dp),
color = Color.White,
shadowElevation = 16.dp,
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp)
) {
Column(modifier = Modifier.padding(24.dp)) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(bottom = 16.dp),
verticalAlignment = Alignment.CenterVertically
) {
Box(
modifier = Modifier
.size(4.dp, 24.dp)
.clip(RoundedCornerShape(2.dp))
.background(Brand500)
)
Spacer(modifier = Modifier.width(12.dp))
Text(
text = "选择表情",
fontWeight = FontWeight.Bold,
fontSize = 20.sp,
color = Slate900
)
Spacer(modifier = Modifier.weight(1f))
IconButton(
onClick = onDismiss,
modifier = Modifier.size(32.dp)
) {
Icon(
imageVector = Icons.Default.Close,
contentDescription = "关闭",
tint = Slate400,
modifier = Modifier.size(20.dp)
)
}
}
androidx.compose.foundation.lazy.grid.LazyVerticalGrid(
columns = androidx.compose.foundation.lazy.grid.GridCells.Fixed(6),
modifier = Modifier
.fillMaxWidth()
.heightIn(max = 400.dp),
horizontalArrangement = Arrangement.SpaceEvenly,
verticalArrangement = Arrangement.spacedBy(12.dp),
contentPadding = PaddingValues(vertical = 8.dp)
) {
items(emojis.size) { index ->
Box(
modifier = Modifier
.fillMaxWidth()
.aspectRatio(1f)
.clip(RoundedCornerShape(8.dp))
.clickable { onEmojiSelected(emojis[index]) },
contentAlignment = Alignment.Center
) {
Text(
text = emojis[index],
fontSize = 32.sp
)
}
}
}
}
}
}
}

View File

@@ -167,11 +167,11 @@ fun PostDetailScreen(
Spacer(modifier = Modifier.height(16.dp))
// Content Text
// Content Text - 与首页保持一致
Text(
text = post.content,
fontSize = 20.sp,
lineHeight = 28.sp,
fontSize = 15.sp,
lineHeight = 22.sp,
color = Slate800
)