Я создаю чат-приложение для обучения разработке Android с использованием Kotlin и Jetpack Compose. Я пытаюсь плавно выйти из режима извлечения (Landscape + KeyboardVisible). Но когда клавиатура опускается, а строка текстового поля занимает анимацию доступного пространства и полноэкранную рекомпозицию. Я попробовал две разные композиции нижней панели и одну композицию для обоих режимов. Вот ссылка на github: https://github.com/Akibilies20001/Pokor_Pokor
Вот код:
@OptIn(ExperimentalMaterial3Api::class, ExperimentalLayoutApi::class)
@Composable
fun ChatroomScreen(
newMessages: List,
oldMessages: LazyPagingItems,
state: ChatroomState,
onEvent: (ChatroomEvent) -> Unit,
onBackClick: () -> Unit
) {
val keyboardController = LocalSoftwareKeyboardController.current
val focusManager = LocalFocusManager.current
val isImeVisible = WindowInsets.isImeVisible
val configuration = LocalConfiguration.current
val isLandscape = configuration.orientation == Configuration.ORIENTATION_LANDSCAPE
val extractMode = isLandscape && isImeVisible
val focusRequester = remember { FocusRequester() }
LaunchedEffect(isLandscape, isImeVisible) {
if (isImeVisible) {
focusRequester.requestFocus()
}
}
Scaffold(
topBar = {
TopAppBar(
title = { ChatPartner(partner = state.chatPartner) },
navigationIcon = {
IconButton(onClick = { onBackClick() }) {
Icon(
imageVector = Icons.AutoMirrored.Filled.ArrowBack,
contentDescription = "Back",
tint = MaterialTheme.colorScheme.onPrimary
)
}
},
actions = {
//
},
colors =
TopAppBarDefaults.topAppBarColors(
containerColor = MaterialTheme.colorScheme.primary
)
)
},
bottomBar = {
ChatBottomAppBar2(
text = state.newMessage,
onSendClick = { onEvent(ChatroomEvent.SendMessage) },
onImageClick = { onEvent(ChatroomEvent.SendImage) },
onTextChange = { onEvent(ChatroomEvent.UpdateNewMessage(it)) },
isLandscape = isLandscape,
focusRequester = focusRequester,
isExtractMode = extractMode,
onDoneClick = {
keyboardController?.hide() // Instant hide here too if using IME Done
focusManager.clearFocus()
}
)
// if (extractMode){
// LandscapeExtractInput(
// text = state.newMessage,
// onTextChange ={onEvent(ChatroomEvent.UpdateNewMessage(it))},
// onDoneClick = {focusManager.clearFocus()},
// focusRequester = focusRequester
// ) }else{
// ChatBottomAppBar(
// text = state.newMessage,
// onSendClick = { onEvent(ChatroomEvent.SendMessage) },
// onImageClick = { onEvent(ChatroomEvent.SendImage) },
// onTextChange = {
// onEvent(ChatroomEvent.UpdateNewMessage(it))
// },
// isLandscape = isLandscape,
// focusRequester = focusRequester
// )
// }
},
modifier = Modifier.fillMaxSize().navigationBarsPadding()
) { paddingValues ->
val listState = rememberLazyListState()
Box(modifier = Modifier.fillMaxSize()) {
Image(
modifier = Modifier.fillMaxSize(),
contentDescription = null,
painter =
painterResource(
id =
if (isSystemInDarkTheme()) {
R.drawable.background_dark
} else {
R.drawable.background_light
}
),
contentScale = ContentScale.Crop
)
Column(modifier = Modifier.fillMaxSize().padding(paddingValues)) {
// Replace this with LazyColumn for chat messages later
LazyColumn(
state = listState,
reverseLayout = true,
modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp),
verticalArrangement = Arrangement.spacedBy(12.dp)
) {
item { Spacer(modifier = Modifier.weight(1f, fill = true)) }
items(
items = newMessages,
key = { it.timestamp } // stable key
) { message ->
MessageBox(message, chatOwner = message.sentBy == state.currentUserId)
}
// --- 2. Paging old messages ---
items(
count = oldMessages.itemCount,
key = { index -> oldMessages[index]?.timestamp ?: index }
) { index ->
oldMessages[index]?.let { message ->
MessageBox(message, chatOwner = message.sentBy == state.currentUserId)
}
}
}
}
}
}
}
@Composable
fun ChatBottomAppBar(
text: TextFieldValue,
onSendClick: () -> Unit,
onImageClick: () -> Unit,
onTextChange: (TextFieldValue) -> Unit,
isLandscape: Boolean,
focusRequester: FocusRequester
) {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier =
Modifier.fillMaxWidth()
.windowInsetsPadding(
if (isLandscape) WindowInsets.safeDrawing.only(WindowInsetsSides.Horizontal)
else WindowInsets(0, 0, 0, 0)
)
.imePadding()
.padding(4.dp)
) {
Row(
verticalAlignment = Alignment.Bottom,
modifier =
Modifier.weight(1f)
.clip(RoundedCornerShape(16.dp))
.background(
if (isSystemInDarkTheme()) {
Color.Gray
} else {
Color.White
}
)
) {
IconButton(
onClick = { onImageClick() },
modifier = Modifier.padding(16.dp).size(24.dp),
colors =
IconButtonDefaults.iconButtonColors(
containerColor = Color.Transparent, // No background
contentColor = MaterialTheme.colorScheme.primary // Icon color
)
) {
Icon(Icons.Default.Image, contentDescription = "Image")
}
TextField(
value = text,
onValueChange = { newValue ->
Log.d("ChatDebug", "IME sent: '$newValue' (length = ${newValue.text.length})")
onTextChange(newValue)
},
placeholder = {
Text(
text = "Message",
color = MaterialTheme.colorScheme.primary.copy(alpha = 0.5f)
)
},
maxLines = 6,
minLines = 1,
keyboardOptions =
KeyboardOptions(
capitalization = KeyboardCapitalization.Sentences, // capital after newline
autoCorrectEnabled = false, // prevent IME commit
keyboardType = KeyboardType.Text,
imeAction = ImeAction.Default // allow newline properly
),
keyboardActions =
KeyboardActions(onAny = { /* no-op: prevents IME overriding on newline */}),
modifier =
Modifier.fillMaxWidth().focusRequester(focusRequester).heightIn(min = 48.dp),
shape = RoundedCornerShape(25.dp),
colors =
TextFieldDefaults.colors(
focusedContainerColor = Color.Transparent,
unfocusedContainerColor = Color.Transparent,
disabledContainerColor = Color.Transparent,
focusedIndicatorColor = Color.Transparent,
unfocusedIndicatorColor = Color.Transparent,
disabledIndicatorColor = Color.Transparent,
cursorColor = MaterialTheme.colorScheme.primary,
)
)
}
Spacer(modifier = Modifier.width(8.dp))
IconButton(
onClick = { onSendClick() },
shape = RoundedCornerShape(100.dp),
modifier = Modifier.size(48.dp),
colors =
IconButtonDefaults.iconButtonColors(
containerColor = MaterialTheme.colorScheme.primary,
contentColor = MaterialTheme.colorScheme.onPrimary
)
) {
Icon(imageVector = Icons.AutoMirrored.Filled.Send, contentDescription = "Send")
}
}
}
@Composable
fun LandscapeExtractInput(
text: TextFieldValue,
onTextChange: (TextFieldValue) -> Unit,
onDoneClick: () -> Unit,
focusRequester: FocusRequester
) {
Box(
modifier =
Modifier.fillMaxWidth()
.windowInsetsPadding(WindowInsets.safeDrawing)
.imePadding()
.background(MaterialTheme.colorScheme.background)
) {
Row(
modifier = Modifier.fillMaxSize().padding(8.dp),
verticalAlignment = Alignment.CenterVertically
) {
TextField(
value = text,
onValueChange = { newValue ->
Log.d("ChatDebug", "IME sent: '$newValue' (length = ${newValue.text.length})")
onTextChange(newValue)
},
modifier =
Modifier.weight(1f)
.clip(RoundedCornerShape(24.dp))
.background(MaterialTheme.colorScheme.surfaceVariant)
.focusRequester(focusRequester),
colors =
TextFieldDefaults.colors(
focusedContainerColor = MaterialTheme.colorScheme.surfaceVariant,
unfocusedContainerColor = MaterialTheme.colorScheme.surfaceVariant,
focusedTextColor = MaterialTheme.colorScheme.onSurfaceVariant,
unfocusedTextColor = MaterialTheme.colorScheme.onSurfaceVariant,
cursorColor = MaterialTheme.colorScheme.primary,
// Remove underline
focusedIndicatorColor = Color.Transparent,
unfocusedIndicatorColor = Color.Transparent
),
placeholder = {
Text(
"Type a message...",
color = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.6f)
)
},
// Allow up to 4 lines
maxLines = 4,
minLines = 1,
keyboardOptions =
KeyboardOptions(
capitalization = KeyboardCapitalization.Sentences, // capital after newline
autoCorrectEnabled = false, // prevent IME commit
keyboardType = KeyboardType.Text,
imeAction = ImeAction.Default // allow newline properly
),
)
Spacer(modifier = Modifier.width(8.dp))
Button(
onClick = { onDoneClick() },
shape = RoundedCornerShape(24.dp),
contentPadding = PaddingValues(8.dp),
colors =
ButtonDefaults.buttonColors(containerColor = MaterialTheme.colorScheme.primary),
) {
Text(
text = "DONE",
style = MaterialTheme.typography.bodySmall,
fontWeight = FontWeight.Bold,
color = MaterialTheme.colorScheme.onPrimary
)
}
}
}
}
@Composable
fun ChatBottomAppBar2(
text: TextFieldValue,
onSendClick: () -> Unit,
onImageClick: () -> Unit,
onTextChange: (TextFieldValue) -> Unit,
isLandscape: Boolean,
isExtractMode: Boolean,
onDoneClick: () -> Unit,
focusRequester: FocusRequester
) {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier =
if (isExtractMode) {
Modifier.fillMaxWidth()
.windowInsetsPadding(WindowInsets.safeDrawing)
.background(MaterialTheme.colorScheme.background)
} else {
Modifier.fillMaxWidth()
.windowInsetsPadding(
if (isLandscape) WindowInsets.safeDrawing.only(WindowInsetsSides.Horizontal)
else WindowInsets(0, 0, 0, 0)
)
.imePadding()
.padding(4.dp)
}
) {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier =
if (isExtractMode) {
Modifier.fillMaxSize().padding(8.dp)
} else {
Modifier.weight(1f)
.clip(RoundedCornerShape(16.dp))
.background(
if (isSystemInDarkTheme()) {
Color.Gray
} else {
Color.White
}
)
}
) {
if (!isExtractMode) {
IconButton(
onClick = { onImageClick() },
modifier = Modifier.padding(16.dp).size(24.dp),
colors =
IconButtonDefaults.iconButtonColors(
containerColor = Color.Transparent, // No background
contentColor = MaterialTheme.colorScheme.primary // Icon color
)
) {
Icon(Icons.Default.Image, contentDescription = "Image")
}
}
TextField(
value = text,
onValueChange = { newValue ->
Log.d("ChatDebug", "IME sent: '$newValue' (length = ${newValue.text.length})")
onTextChange(newValue)
},
placeholder = {
Text(
text = "Message",
color = MaterialTheme.colorScheme.primary.copy(alpha = 0.5f)
)
},
maxLines = 6,
minLines = 1,
keyboardOptions =
KeyboardOptions(
capitalization = KeyboardCapitalization.Sentences, // capital after newline
autoCorrectEnabled = false, // prevent IME commit
keyboardType = KeyboardType.Text,
imeAction = ImeAction.Default // allow newline properly
),
keyboardActions =
KeyboardActions(onAny = { /* no-op: prevents IME overriding on newline */}),
modifier = Modifier.weight(1f).focusRequester(focusRequester).heightIn(min = 48.dp),
shape = RoundedCornerShape(25.dp),
colors =
TextFieldDefaults.colors(
focusedContainerColor = Color.Transparent,
unfocusedContainerColor = Color.Transparent,
disabledContainerColor = Color.Transparent,
focusedIndicatorColor = Color.Transparent,
unfocusedIndicatorColor = Color.Transparent,
disabledIndicatorColor = Color.Transparent,
cursorColor = MaterialTheme.colorScheme.primary,
)
)
if (isExtractMode) {
Button(
onClick = { onDoneClick() },
shape = RoundedCornerShape(24.dp),
contentPadding = PaddingValues(8.dp),
colors =
ButtonDefaults.buttonColors(
containerColor = MaterialTheme.colorScheme.primary
),
) {
Text(
text = "DONE",
style = MaterialTheme.typography.bodySmall,
fontWeight = FontWeight.Bold,
color = MaterialTheme.colorScheme.onPrimary
)
}
}
}
Spacer(modifier = Modifier.width(8.dp))
if (!isExtractMode) {
IconButton(
onClick = { onSendClick() },
shape = RoundedCornerShape(100.dp),
modifier = Modifier.size(48.dp),
colors =
IconButtonDefaults.iconButtonColors(
containerColor = MaterialTheme.colorScheme.primary,
contentColor = MaterialTheme.colorScheme.onPrimary
)
) {
Icon(imageVector = Icons.AutoMirrored.Filled.Send, contentDescription = "Send")
}
}
}
}
Подробнее здесь: https://stackoverflow.com/questions/798 ... choppy-tra
Невозможно выйти из режима извлечения аккуратно, как в WhatsApp или Messenger. Прерывистый переход ⇐ Android
Форум для тех, кто программирует под Android
1767412362
Anonymous
Я создаю чат-приложение для обучения разработке Android с использованием Kotlin и Jetpack Compose. Я пытаюсь плавно выйти из режима извлечения (Landscape + KeyboardVisible). Но когда клавиатура опускается, а строка текстового поля занимает анимацию доступного пространства и полноэкранную рекомпозицию. Я попробовал две разные композиции нижней панели и одну композицию для обоих режимов. Вот ссылка на github: https://github.com/Akibilies20001/Pokor_Pokor
Вот код:
@OptIn(ExperimentalMaterial3Api::class, ExperimentalLayoutApi::class)
@Composable
fun ChatroomScreen(
newMessages: List,
oldMessages: LazyPagingItems,
state: ChatroomState,
onEvent: (ChatroomEvent) -> Unit,
onBackClick: () -> Unit
) {
val keyboardController = LocalSoftwareKeyboardController.current
val focusManager = LocalFocusManager.current
val isImeVisible = WindowInsets.isImeVisible
val configuration = LocalConfiguration.current
val isLandscape = configuration.orientation == Configuration.ORIENTATION_LANDSCAPE
val extractMode = isLandscape && isImeVisible
val focusRequester = remember { FocusRequester() }
LaunchedEffect(isLandscape, isImeVisible) {
if (isImeVisible) {
focusRequester.requestFocus()
}
}
Scaffold(
topBar = {
TopAppBar(
title = { ChatPartner(partner = state.chatPartner) },
navigationIcon = {
IconButton(onClick = { onBackClick() }) {
Icon(
imageVector = Icons.AutoMirrored.Filled.ArrowBack,
contentDescription = "Back",
tint = MaterialTheme.colorScheme.onPrimary
)
}
},
actions = {
//
},
colors =
TopAppBarDefaults.topAppBarColors(
containerColor = MaterialTheme.colorScheme.primary
)
)
},
bottomBar = {
ChatBottomAppBar2(
text = state.newMessage,
onSendClick = { onEvent(ChatroomEvent.SendMessage) },
onImageClick = { onEvent(ChatroomEvent.SendImage) },
onTextChange = { onEvent(ChatroomEvent.UpdateNewMessage(it)) },
isLandscape = isLandscape,
focusRequester = focusRequester,
isExtractMode = extractMode,
onDoneClick = {
keyboardController?.hide() // Instant hide here too if using IME Done
focusManager.clearFocus()
}
)
// if (extractMode){
// LandscapeExtractInput(
// text = state.newMessage,
// onTextChange ={onEvent(ChatroomEvent.UpdateNewMessage(it))},
// onDoneClick = {focusManager.clearFocus()},
// focusRequester = focusRequester
// ) }else{
// ChatBottomAppBar(
// text = state.newMessage,
// onSendClick = { onEvent(ChatroomEvent.SendMessage) },
// onImageClick = { onEvent(ChatroomEvent.SendImage) },
// onTextChange = {
// onEvent(ChatroomEvent.UpdateNewMessage(it))
// },
// isLandscape = isLandscape,
// focusRequester = focusRequester
// )
// }
},
modifier = Modifier.fillMaxSize().navigationBarsPadding()
) { paddingValues ->
val listState = rememberLazyListState()
Box(modifier = Modifier.fillMaxSize()) {
Image(
modifier = Modifier.fillMaxSize(),
contentDescription = null,
painter =
painterResource(
id =
if (isSystemInDarkTheme()) {
R.drawable.background_dark
} else {
R.drawable.background_light
}
),
contentScale = ContentScale.Crop
)
Column(modifier = Modifier.fillMaxSize().padding(paddingValues)) {
// Replace this with LazyColumn for chat messages later
LazyColumn(
state = listState,
reverseLayout = true,
modifier = Modifier.fillMaxSize().padding(horizontal = 8.dp),
verticalArrangement = Arrangement.spacedBy(12.dp)
) {
item { Spacer(modifier = Modifier.weight(1f, fill = true)) }
items(
items = newMessages,
key = { it.timestamp } // stable key
) { message ->
MessageBox(message, chatOwner = message.sentBy == state.currentUserId)
}
// --- 2. Paging old messages ---
items(
count = oldMessages.itemCount,
key = { index -> oldMessages[index]?.timestamp ?: index }
) { index ->
oldMessages[index]?.let { message ->
MessageBox(message, chatOwner = message.sentBy == state.currentUserId)
}
}
}
}
}
}
}
@Composable
fun ChatBottomAppBar(
text: TextFieldValue,
onSendClick: () -> Unit,
onImageClick: () -> Unit,
onTextChange: (TextFieldValue) -> Unit,
isLandscape: Boolean,
focusRequester: FocusRequester
) {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier =
Modifier.fillMaxWidth()
.windowInsetsPadding(
if (isLandscape) WindowInsets.safeDrawing.only(WindowInsetsSides.Horizontal)
else WindowInsets(0, 0, 0, 0)
)
.imePadding()
.padding(4.dp)
) {
Row(
verticalAlignment = Alignment.Bottom,
modifier =
Modifier.weight(1f)
.clip(RoundedCornerShape(16.dp))
.background(
if (isSystemInDarkTheme()) {
Color.Gray
} else {
Color.White
}
)
) {
IconButton(
onClick = { onImageClick() },
modifier = Modifier.padding(16.dp).size(24.dp),
colors =
IconButtonDefaults.iconButtonColors(
containerColor = Color.Transparent, // No background
contentColor = MaterialTheme.colorScheme.primary // Icon color
)
) {
Icon(Icons.Default.Image, contentDescription = "Image")
}
TextField(
value = text,
onValueChange = { newValue ->
Log.d("ChatDebug", "IME sent: '$newValue' (length = ${newValue.text.length})")
onTextChange(newValue)
},
placeholder = {
Text(
text = "Message",
color = MaterialTheme.colorScheme.primary.copy(alpha = 0.5f)
)
},
maxLines = 6,
minLines = 1,
keyboardOptions =
KeyboardOptions(
capitalization = KeyboardCapitalization.Sentences, // capital after newline
autoCorrectEnabled = false, // prevent IME commit
keyboardType = KeyboardType.Text,
imeAction = ImeAction.Default // allow newline properly
),
keyboardActions =
KeyboardActions(onAny = { /* no-op: prevents IME overriding on newline */}),
modifier =
Modifier.fillMaxWidth().focusRequester(focusRequester).heightIn(min = 48.dp),
shape = RoundedCornerShape(25.dp),
colors =
TextFieldDefaults.colors(
focusedContainerColor = Color.Transparent,
unfocusedContainerColor = Color.Transparent,
disabledContainerColor = Color.Transparent,
focusedIndicatorColor = Color.Transparent,
unfocusedIndicatorColor = Color.Transparent,
disabledIndicatorColor = Color.Transparent,
cursorColor = MaterialTheme.colorScheme.primary,
)
)
}
Spacer(modifier = Modifier.width(8.dp))
IconButton(
onClick = { onSendClick() },
shape = RoundedCornerShape(100.dp),
modifier = Modifier.size(48.dp),
colors =
IconButtonDefaults.iconButtonColors(
containerColor = MaterialTheme.colorScheme.primary,
contentColor = MaterialTheme.colorScheme.onPrimary
)
) {
Icon(imageVector = Icons.AutoMirrored.Filled.Send, contentDescription = "Send")
}
}
}
@Composable
fun LandscapeExtractInput(
text: TextFieldValue,
onTextChange: (TextFieldValue) -> Unit,
onDoneClick: () -> Unit,
focusRequester: FocusRequester
) {
Box(
modifier =
Modifier.fillMaxWidth()
.windowInsetsPadding(WindowInsets.safeDrawing)
.imePadding()
.background(MaterialTheme.colorScheme.background)
) {
Row(
modifier = Modifier.fillMaxSize().padding(8.dp),
verticalAlignment = Alignment.CenterVertically
) {
TextField(
value = text,
onValueChange = { newValue ->
Log.d("ChatDebug", "IME sent: '$newValue' (length = ${newValue.text.length})")
onTextChange(newValue)
},
modifier =
Modifier.weight(1f)
.clip(RoundedCornerShape(24.dp))
.background(MaterialTheme.colorScheme.surfaceVariant)
.focusRequester(focusRequester),
colors =
TextFieldDefaults.colors(
focusedContainerColor = MaterialTheme.colorScheme.surfaceVariant,
unfocusedContainerColor = MaterialTheme.colorScheme.surfaceVariant,
focusedTextColor = MaterialTheme.colorScheme.onSurfaceVariant,
unfocusedTextColor = MaterialTheme.colorScheme.onSurfaceVariant,
cursorColor = MaterialTheme.colorScheme.primary,
// Remove underline
focusedIndicatorColor = Color.Transparent,
unfocusedIndicatorColor = Color.Transparent
),
placeholder = {
Text(
"Type a message...",
color = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.6f)
)
},
// Allow up to 4 lines
maxLines = 4,
minLines = 1,
keyboardOptions =
KeyboardOptions(
capitalization = KeyboardCapitalization.Sentences, // capital after newline
autoCorrectEnabled = false, // prevent IME commit
keyboardType = KeyboardType.Text,
imeAction = ImeAction.Default // allow newline properly
),
)
Spacer(modifier = Modifier.width(8.dp))
Button(
onClick = { onDoneClick() },
shape = RoundedCornerShape(24.dp),
contentPadding = PaddingValues(8.dp),
colors =
ButtonDefaults.buttonColors(containerColor = MaterialTheme.colorScheme.primary),
) {
Text(
text = "DONE",
style = MaterialTheme.typography.bodySmall,
fontWeight = FontWeight.Bold,
color = MaterialTheme.colorScheme.onPrimary
)
}
}
}
}
@Composable
fun ChatBottomAppBar2(
text: TextFieldValue,
onSendClick: () -> Unit,
onImageClick: () -> Unit,
onTextChange: (TextFieldValue) -> Unit,
isLandscape: Boolean,
isExtractMode: Boolean,
onDoneClick: () -> Unit,
focusRequester: FocusRequester
) {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier =
if (isExtractMode) {
Modifier.fillMaxWidth()
.windowInsetsPadding(WindowInsets.safeDrawing)
.background(MaterialTheme.colorScheme.background)
} else {
Modifier.fillMaxWidth()
.windowInsetsPadding(
if (isLandscape) WindowInsets.safeDrawing.only(WindowInsetsSides.Horizontal)
else WindowInsets(0, 0, 0, 0)
)
.imePadding()
.padding(4.dp)
}
) {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier =
if (isExtractMode) {
Modifier.fillMaxSize().padding(8.dp)
} else {
Modifier.weight(1f)
.clip(RoundedCornerShape(16.dp))
.background(
if (isSystemInDarkTheme()) {
Color.Gray
} else {
Color.White
}
)
}
) {
if (!isExtractMode) {
IconButton(
onClick = { onImageClick() },
modifier = Modifier.padding(16.dp).size(24.dp),
colors =
IconButtonDefaults.iconButtonColors(
containerColor = Color.Transparent, // No background
contentColor = MaterialTheme.colorScheme.primary // Icon color
)
) {
Icon(Icons.Default.Image, contentDescription = "Image")
}
}
TextField(
value = text,
onValueChange = { newValue ->
Log.d("ChatDebug", "IME sent: '$newValue' (length = ${newValue.text.length})")
onTextChange(newValue)
},
placeholder = {
Text(
text = "Message",
color = MaterialTheme.colorScheme.primary.copy(alpha = 0.5f)
)
},
maxLines = 6,
minLines = 1,
keyboardOptions =
KeyboardOptions(
capitalization = KeyboardCapitalization.Sentences, // capital after newline
autoCorrectEnabled = false, // prevent IME commit
keyboardType = KeyboardType.Text,
imeAction = ImeAction.Default // allow newline properly
),
keyboardActions =
KeyboardActions(onAny = { /* no-op: prevents IME overriding on newline */}),
modifier = Modifier.weight(1f).focusRequester(focusRequester).heightIn(min = 48.dp),
shape = RoundedCornerShape(25.dp),
colors =
TextFieldDefaults.colors(
focusedContainerColor = Color.Transparent,
unfocusedContainerColor = Color.Transparent,
disabledContainerColor = Color.Transparent,
focusedIndicatorColor = Color.Transparent,
unfocusedIndicatorColor = Color.Transparent,
disabledIndicatorColor = Color.Transparent,
cursorColor = MaterialTheme.colorScheme.primary,
)
)
if (isExtractMode) {
Button(
onClick = { onDoneClick() },
shape = RoundedCornerShape(24.dp),
contentPadding = PaddingValues(8.dp),
colors =
ButtonDefaults.buttonColors(
containerColor = MaterialTheme.colorScheme.primary
),
) {
Text(
text = "DONE",
style = MaterialTheme.typography.bodySmall,
fontWeight = FontWeight.Bold,
color = MaterialTheme.colorScheme.onPrimary
)
}
}
}
Spacer(modifier = Modifier.width(8.dp))
if (!isExtractMode) {
IconButton(
onClick = { onSendClick() },
shape = RoundedCornerShape(100.dp),
modifier = Modifier.size(48.dp),
colors =
IconButtonDefaults.iconButtonColors(
containerColor = MaterialTheme.colorScheme.primary,
contentColor = MaterialTheme.colorScheme.onPrimary
)
) {
Icon(imageVector = Icons.AutoMirrored.Filled.Send, contentDescription = "Send")
}
}
}
}
Подробнее здесь: [url]https://stackoverflow.com/questions/79848598/can-not-exit-extractmode-cleanly-like-whatsapp-or-messenger-having-a-choppy-tra[/url]
Ответить
1 сообщение
• Страница 1 из 1
Перейти
- Кемерово-IT
- ↳ Javascript
- ↳ C#
- ↳ JAVA
- ↳ Elasticsearch aggregation
- ↳ Python
- ↳ Php
- ↳ Android
- ↳ Html
- ↳ Jquery
- ↳ C++
- ↳ IOS
- ↳ CSS
- ↳ Excel
- ↳ Linux
- ↳ Apache
- ↳ MySql
- Детский мир
- Для души
- ↳ Музыкальные инструменты даром
- ↳ Печатная продукция даром
- Внешняя красота и здоровье
- ↳ Одежда и обувь для взрослых даром
- ↳ Товары для здоровья
- ↳ Физкультура и спорт
- Техника - даром!
- ↳ Автомобилистам
- ↳ Компьютерная техника
- ↳ Плиты: газовые и электрические
- ↳ Холодильники
- ↳ Стиральные машины
- ↳ Телевизоры
- ↳ Телефоны, смартфоны, плашеты
- ↳ Швейные машинки
- ↳ Прочая электроника и техника
- ↳ Фототехника
- Ремонт и интерьер
- ↳ Стройматериалы, инструмент
- ↳ Мебель и предметы интерьера даром
- ↳ Cантехника
- Другие темы
- ↳ Разное даром
- ↳ Давай меняться!
- ↳ Отдам\возьму за копеечку
- ↳ Работа и подработка в Кемерове
- ↳ Давай с тобой поговорим...
Мобильная версия