I have create a simple example with six TextFields inside a LazyColumn, when you click the last TextField, the keyboard hides it, if you hide the keyboard and click again last TextField, works fine.
In the AndroidManifest I use "adjustPan"
android:windowSoftInputMode="adjustPan"
This is a capture when you click the last TextField first time, hides the last TextField
This is a capture when you click the last TextField second time, works correctly
This is the code
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
TestComposeTheme {
val numbers = listOf(1,2,3,4,5,6)
LazyColumn() {
items(numbers) { index->
TextField(index = index)
}
}
}
}
}
}
#Composable
fun TextField(index: Int){
var text by remember { mutableStateOf("Hello$index") }
TextField(
modifier = Modifier.padding(25.dp),
value = text,
onValueChange = { text = it },
label = { Text("TextField$index") }
)
}
Does anyone know if there is any way that the first time the last TextField is tapped, it would prevent the keyboard from hiding it
EDIT: There is a known issue:
192043120
This is already a known issue. https://issuetracker.google.com/issues/192043120
One hack to overcome this is use a column with verticalScroll
Column(Modifier.verticalScroll(rememberScrollState(), reverseScrolling = true){
// Content
}
put this in your app AndroidManifest.xml inside activity tag
<activity
...
android:windowSoftInputMode="adjustResize|stateVisible">
Related
As shown above, the list of items, the text input field and the add button go up when the user open the keyboard,
I want the list of items to stay in position while the text input field and the add buton go up as it does.
code:
Activity:
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
OlegarioLopezTheme {
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colors.background
) { Navigation() }
}
}
}
The Navigation() func just call the Composable
Composable:
#Composable
fun ListScreen(
viewModel: MainScreenViewModel,
navController: NavController
) {
LazyColumn{...}
MainTextField(viewModel)
AddButton(viewModel)
}
Ensure that the activity's windowSoftInputMode is set to adjustResize:
<activity
android:name=".MyActivity"
android:windowSoftInputMode="adjustResize">
</activity>
In this way the activity's main window is always resized to make room for the soft keyboard on screen.
Then just use a layout as:
Column() {
LazyColumn(Modifier.weight(1f)) {
//..
}
Row(){
TextField()
Button()
}
}
This was exactly my problem with an extra twist that it only became an issue after the activity returned from pause.
A brand new state had no issue, but when resuming suddenly my entire mainactivity was being pushed up with the keyboard. SUPER weird behavior.
I actually set windowSoftInputMode to "adjustPan" abd it works as expected now, which is how it worked as a fresh activity.
I have a simple view like this. In the emulator, when I press Tab on the keyboard, nothing happens. There's no indication that the button in focused, and the onClick is not called when I press Enter.
NOTE: This is for accessibility, i.e., those who cannot use their fingers to tap and need an indicator that the button has focus, or those who use the app in a tablet + keyboard.
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
MaterialTheme {
Surface(color = MaterialTheme.colors.background) {
Button(onClick = { println("clicked") }) {
Text(text = "A Button")
}
}
}
}
}
Please help. The Compose version is 1.0.5.
By the way,
I've read almost all pages of Compose documentation in Android Developers, including Accessibility in Compose, but I cannot find any useful information that solves my simple problem.
Compose TextField could receive focus either by pressing Tab or arrow keys.
I've also tried to place a View Button and a Compose Button side by side, only ViewButton could receive focus.
Update
val iSource = remember { MutableInteractionSource() }
val focused by iSource.collectIsFocusedAsState()
val pressed by iSource.collectIsPressedAsState()
Button(
onClick = { println("clicked") },
Modifier.focusable(interactionSource = iSource),
) {
Text(text = "A Button (focused=${focused}, pressed=${pressed})")
}
This way, "focused" become true when I navigate by using arrow keys (Up/Down), but:
There's no focused indication.
Pressing Tab still doesn't work.
When it's focused, pressing Space or Enter doesn't trigger the onClick listener.
I want to programmatically change which tab is selected as the user scrolls past each "see more" item in the list below. How would I best accomplish this?
As Ryan M writes, you can use LazyListState.firstVisibleItemIndex. The magic of Compose is that you can just use it in an if statement and Compose will do the work. Look at the following example, which displays a different text based on the first visible item. Similarly, you can select a different tab based on the first visible item.
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
val listState = rememberLazyListState()
Column {
Text(if (listState.firstVisibleItemIndex < 100) "< 100" else ">= 100")
LazyColumn(state = listState) {
items(1000) {
Text(
text = "$it",
modifier = Modifier.fillMaxWidth(),
)
}
}
}
}
}
}
Just create a NestedScrollConnection and assign it to parent view nestedScroll modifier:
val nestedScrollConnection = remember {
object : NestedScrollConnection {
override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
val delta = available.y
// called when you scroll the content
return Offset.Zero
}
}
}
Assign it to LazyColumn or to its parent composed view:
Modifier.nestedScroll(nestedScrollConnection)
Exemple:
LazyColumn(
...
modifier = Modifier.nestedScroll(nestedScrollConnection)
) {
items(...) {
Text("Your text...")
}
}
You can use the LazyListState.firstVisibleItemIndex property (obtained via rememberLazyListState and set as the state parameter to LazyColumn) and set the current tab based on that.
Reading that property is considered a model read in Compose, so it will trigger a recomposition whenever the first visible item changes.
If you instead want to do something more complex based on more than just the first visible item, you can use LazyListState.layoutInfo to get information about all visible items and their locations, rather than just the first.
hi i am trying to use a textfield that upon value changes, i want it to execute a function that filters out a list of items based on the text inputted?
It does not seem to do anything.
I tried adding the searchFilter function call inside onValueChanges but upon clicking on the textfield to try and type something, the keyboard doesnt pop up at all
val textState = remember { mutableStateOf(TextFieldValue()) }
TextField(
value = textState.value,
onValueChange = {
textState.value = it
viewModel.searchCharacter(textState.value.text)
}
)
If i remove the viewModel.searchCharacter(textState.value.text) and add it outside the Textfield, i can now enter something in the actual textfield but it never invokes the searchCharacter function.
My search character method is just a list that apply a filter to
fun searchCharacter(name: String) {
characterList.value?.let {
characterList.value?.let { characterList ->
characterList.filter {
it.name.contains(name)
}
}
}
}
Nothing in your code should prevent the keyboard from popping up. Have you tried running the app again? I've had issues with compose TextFields not reliably opening the keyboard.
Your searchCharacter function doesn't make sense.
Why is there a redundant let block?
filter doesn't modify a list but returns a new one. (https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/filter.html)
Here's a better way:
fun searchCharacter(name: String) {
characterList.value.filter { it.name.contains(name) }?.let { filtered ->
characterList.value = filtered
}
}
Suppose I have some activity with a jetpack-compose content
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
ScrollableColumn(
modifier = Modifier
.fillMaxSize()
.border(4.dp, Color.Red)
) {
val (text, setText) = remember { mutableStateOf("") }
TextField(
value = text,
onValueChange = setText,
label = {},
modifier = Modifier
.fillMaxWidth()
)
for (i in 0..100) {
Text("Item #$i")
}
}
}
}
}
If I were to launch this activity and focus on the TextField a software keyboard would pop up.
The interface, however, would not react to it. ScrollableColumn's bottom border (.border(4.dp, Color.Red)) would not be visible, as well as 100th item (Text("Item #$i")).
In other words, software keyboard overlaps content.
How can I make jetpack compose respect visible area changes (due to software keyboard)?
You can use the standard android procedure, but I don't know if a Compose specific way exists.
If you set the SoftInputMode to SOFT_INPUT_ADJUST_RESIZE, the Layout will resize on keyboard change.
class YourActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
setContent { /* Composable Content */ }
}
}
otherwise, you could use the flags in the manifest. See here for more information:
Move layouts up when soft keyboard is shown?
You can use Accompanist's inset library https://google.github.io/accompanist/insets
first use ProvideWindowInsets at the root of your composable hierarchy most of the time below your app theme compose and set windowInsetsAnimationsEnabled true
ProvideWindowInsets(windowInsetsAnimationsEnabled = true) {
// content }
The use navigationBarsWithImePadding() modifier on TextField
OutlinedTextField(
// other params,
modifier = Modifier.navigationBarsWithImePadding() )
Finaly make sure to call WindowCompat.setDecorFitsSystemWindows(window, false) from your activity(inside onCreate). If you want Software keyboard on/off to animate set your activity's windowSoftInputMode adjustResize in AndroidManifests
<activity
android:name=".MyActivity"
android:windowSoftInputMode="adjustResize">
Besides Compose, no external libraries are needed for this now.
Set android:windowSoftInputMode=adjustResize on the activity in your manifest file
e.g.
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application>
<activity
android:name=".MyActivity"
android:windowSoftInputMode="adjustResize"/>
</application>
</manifest>
Then for the composable that you want the UI to react to, you can use Modifier.imePadding() which reacts to IME visibility
Box(Modifier.imePadding()) {
// content
}
I faced the same problem.
Use OnGlobalLayoutListener which will observe the actual IME rect size and will be triggered when the soft keyboard is fully visible.
Worked solution for me:
val bringIntoViewRequester = remember { BringIntoViewRequester() }
val scope = rememberCoroutineScope()
val view = LocalView.current
DisposableEffect(view) {
val listener = ViewTreeObserver.OnGlobalLayoutListener {
scope.launch { bringIntoViewRequester.bringIntoView() }
}
view.viewTreeObserver.addOnGlobalLayoutListener(listener)
onDispose { view.viewTreeObserver.removeOnGlobalLayoutListener(listener) }
}
TextField(
modifier = Modifier.bringIntoViewRequester(bringIntoViewRequester),
...
)
Origin here
I came up with idea of using accompanist insets library.
Someone could be interested because my approach does not modificate the contents of an application.
I link my post below:
(Compose UI) - Keyboard (IME) overlaps content of app
If you want your TextField scroll up when keyboard appears.
The following it work for me.
You need to add these
class YourActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
setContent { /* Composable Content */ }
}
And add this to your app/build.gradle
implementation "com.google.accompanist:accompanist-insets-ui:0.24.7-alpha"
In my case just adding android:windowSoftInputMode="adjustResize" to activity was enough to solve the problem.
It depends on how you build your UI. If yours screen's root is a vertically scrollable container or a Box, the keyboard resize might get managed automatically.
Use the modifier imePadding(). This will allow the specific compositions to adjust themselves when the keyboard pops up. This does not require you to set any flag on the activity