Невозможно выйти из режима извлечения аккуратно, как в WhatsApp или Messenger. Прерывистый переходAndroid

Форум для тех, кто программирует под Android
Ответить
Anonymous
 Невозможно выйти из режима извлечения аккуратно, как в WhatsApp или Messenger. Прерывистый переход

Сообщение 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")
}
}
}
}



Подробнее здесь: https://stackoverflow.com/questions/798 ... choppy-tra
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «Android»