Der Extractmode kann wie WhatsApp oder Messenger nicht sauber beendet werden. Ein holpriger ÜbergangAndroid

Forum für diejenigen, die für Android programmieren
Anonymous
 Der Extractmode kann wie WhatsApp oder Messenger nicht sauber beendet werden. Ein holpriger Übergang

Post by Anonymous »

Ich baue eine Chat-App zum Erlernen der Android-Entwicklung mit Kotlin und Jetpack Compose. Ich versuche, den Extraktionsmodus (Querformat + KeyboardVisible) reibungslos zu beenden. Aber eine nach unten gerichtete Tastatur und eine Textfeldreihe, die den verfügbaren Platz für die Animation und die Neuzusammenstellung des gesamten Bildschirms einnimmt. Ich habe zwei verschiedene Kompositionen für die untere Leiste und eine einzige Komposition für beide Modi ausprobiert. Hier ist der Github-Link: https://github.com/Akibilies20001/Pokor_Pokor
Hier ist der Code:

Code: Select all

@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")
}
}
}
}

Quick Reply

Change Text Case: 
   
  • Similar Topics
    Replies
    Views
    Last post