OutlinedTextField click in compose with talkback - android

import kotlinx.coroutines.flow.collect
OutlinedTextField(
value = ...,
onValueChange = { ... },
interactionSource = remember { MutableInteractionSource() }
.also { interactionSource ->
LaunchedEffect(interactionSource) {
interactionSource.interactions.collect {
if (it is PressInteraction.Release) {
// works like onClick
}
}
}
}
)
I am using OutlinedTextField, the problem is that with no talkback it works fine.
But when Talkback is enabled, it doesn't behave as a click.And Click not detected. How to solve this issue.
Tried to implement click functionality on OutlinedTextField, works with no talkback. But with talkback it doesn't.

Related

How to prevent elements at the back from scrolling up when keyboard appears in Kotlin Compose? [duplicate]

This question already has answers here:
how to avoid jetpack compose content going up when keyboard opens
(2 answers)
Closed 25 days ago.
In this app, I want to keep the TextField at the Bottom while LazyColumn behind the TextField. But when I start typing, the keyboard appears which moves both TextField and the elements behind up. What I would prefer is if only the TextField moves up.
Before keyboard appears
After keyboard appears (the LazyColumn went up with the TextField)
#Composable
fun App() {
var todos = arrayOf("hello","hello","hello","hello","hello","hello","hello","hello")
var textFieldValue by remember { mutableStateOf("Hello World") }
MaterialTheme {
LazyColumn{
items(todos) {
Text(it)
}
}
Box(
modifier = Modifier.fillMaxHeight().fillMaxWidth(),
) {
TextField(
value = textFieldValue,
singleLine = true,
onValueChange = {
textFieldValue = it
},
modifier = Modifier.background(Color.White).fillMaxWidth().align(Alignment.BottomCenter),
trailingIcon = {
IconButton(
onClick = {
textFieldValue = ""
},
) {
Icon(
Icons.Rounded.Add,
contentDescription = "add a task",
)
}
},
)
}
}
}
You need to add android:windowSoftInputMode in the manifest of your activity
see https://developer.android.com/develop/ui/views/touch-and-input/keyboard-input/visibility

TextField is overlapped by keyboard in Android Compose

I have a TextField in column with verticalScroll().
When adding a large number of characters, the textfield size goes beyond the keyboard and I stop seeing what I am typing
I tried to use this lib, but that's doesn't help
I think you can use BringIntoViewRequester in your TextField.
var state by rememberSaveable {
mutableStateOf("")
}
val coroutineScope = rememberCoroutineScope()
val bringIntoViewRequester = remember {
BringIntoViewRequester()
}
TextField(
value = state,
onValueChange = { text ->
state = text
// This will cause the TextField be repositioned on the screen
// while you're typing
coroutineScope.launch {
bringIntoViewRequester.bringIntoView()
}
},
modifier = Modifier
.bringIntoViewRequester(bringIntoViewRequester)
.onFocusChanged {
if (it.isFocused) {
coroutineScope.launch {
delay(400) // delay to way the keyboard shows up
bringIntoViewRequester.bringIntoView()
}
}
},
)
See the complete sample here.
you can add android:ellipsize="end" and android:maxLines="1" or whatever lines you want, in your text xml hope it would be helpful.

Long-press event on IconToggleButton

I have a toggle button. It works fine, but I want to be able to long-press it.
IconToggleButton(
checked = isChecked,
onCheckedChange = onCheck,
modifier = modifier
.pointerInput(Unit) {
detectTapGestures(
onLongPress = {
showingHelpDialog = true
}
)
}
) {
Icon(
imageVector = if (isChecked) {
Icons.Filled.Star
} else {
Icons.Filled.StarBorder
},
contentDescription = null,
)
}
This doesn't work, because IconToggleButton itself overrides pointerInput through the toggleable modifier.
I could copy all the source code from IconToggleButton/toggleable and hack in my own pointerInput. But that would require me to copy a lot of low-level component logic which I expressly don't want to copy into my own source code, because it should be part of the Material library and I want to benefit from potential improvements in the Material library.
Is there another way I could listen for long-press events?
instead of using
modifier.pointerInput(Unit) { /*...*/ }
use
modifier.combinedClickable(
onLongClick = { /*....*/ },
onClick ={ /*....*/ })

Jetpack Compose: When using Modifier.selectable how do you prevent a layout composable from being selectable while scrolling?

In Jetpack Compose there is a Modifier extension called selectable.
Configure component to be selectable, usually as a part of a mutually exclusive group, where
only one item can be selected at any point in time.
I'm using this for a mutually exclusive radio group inside a scrollable list. In my case a LazyColumn. This works fine, clicking on the selectable areas lights them up and results in detected clicks. However I noticed that the area also lights up while "touching" these areas while scrolling.
I made a simple example composable if you want to see what I mean, simply scroll through the list and you will see how scrolling triggers a short selected state:
#Composable
fun Example() {
LazyColumn {
item {
repeat(100){
Column(
modifier = Modifier
.fillMaxWidth()
.height(40.dp)
.selectable(
selected = false,
onClick = { }
)
) {
Text("Example")
}
}
}
}
}
Has anyone figure out how to fix kind of behaviour? I tried looking for any related documentation at https://developer.android.com/jetpack/compose/gestures but nothing really explains how to "block" touch events while scrolling.
You can selectively enable Modifier.selectable(enabled) based on scroll state but even with derivedStateOf i see that there is huge performance loss.
val scrollState = rememberLazyListState()
val enableSelectable = derivedStateOf {
!scrollState.isScrollInProgress
}
Modifier
.fillMaxWidth()
.height(40.dp)
.selectable(
enabled = enableSelectable.value,
selected = false,
onClick = { }
)
I created a simple but longer example than you did, and included a video showing how it behaves with this code.
I believe what you are seeing is the ACTION_DOWN causing a ripple. It's not actually "selecting" the item because it does not change the selected state. I am not seeing the ripple when I scroll, but only when I keep my finger pressed on a specific row - the ripple disappears when my finger moves down.
I got the info about MotionEvents from this answer: https://stackoverflow.com/a/64594717/1703677
(Change the falses to true to see more info in the logs)
#Composable
fun Content() {
val selectedValue = remember { mutableStateOf("") }
LazyColumn {
item {
repeat(100) {
val label = "Item $it"
val selected = selectedValue.value == label
SingleRadioButtonWithLabel(label, selected) {
selectedValue.value = label
}
}
}
}
}
#OptIn(ExperimentalComposeUiApi::class)
#Composable
fun SingleRadioButtonWithLabel(
label: String,
selected: Boolean,
onClick: () -> Unit
) {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.selectable(
selected = selected,
onClick = {
onClick()
Log.e("TestApp", "Row onClick")
}
)
.pointerInteropFilter {
when (it.action) {
MotionEvent.ACTION_DOWN -> {
Log.e("TestApp", "MotionEvent.ACTION_DOWN")
}
MotionEvent.ACTION_MOVE -> {
Log.e("TestApp", "MotionEvent.ACTION_MOVE")
}
MotionEvent.ACTION_UP -> {
Log.e("TestApp", "MotionEvent.ACTION_UP")
}
else -> false
}
false
}
) {
RadioButton(
selected = selected,
onClick = {
onClick()
Log.e("TestApp", "Radio Button onClick")
},
)
Text(
text = label,
modifier = Modifier.fillMaxWidth()
)
}
}

Prevent the keyboard from appearing in a Jetpack Compose app

I'm making a calculator to learn Compose, so I placed my own number buttons on screen and I wanted to prevent the soft keyboard from appearing.
Here is my repo: https://github.com/vitor-ramos/CalculadorCompose
I noticed in TextFieldImpl.kt there is a modifier to show the keyboard, so I tried to clone the code and remove the line: keyboardController.value?.showSoftwareKeyboard() I know it's not a good idea to duplicate code like that, but I wanted to give it a try, and it didn't work. As you can see in the original code below there's a TODO saying it should be handled by BaseTextField, but I looked in it's code and didn't find where it shows or hides the keyboard.
val textFieldModifier = modifier
.focusRequester(focusRequester)
.focusObserver { isFocused = it.isFocused }
.clickable(indication = null) {
focusRequester.requestFocus()
// TODO(b/163109449): Showing and hiding keyboard should be handled by BaseTextField.
// The requestFocus() call here should be enough to trigger the software keyboard.
// Investiate why this is needed here. If it is really needed, instead of doing
// this in the onClick callback, we should move this logic to the focusObserver
// so that it can show or hide the keyboard based on the focus state.
keyboardController.value?.showSoftwareKeyboard()
}
I found in this question that with views I can extend EditText and change the functionality, but I haven't found a equivalent for Compose: Android: Disable soft keyboard at all EditTexts
public class NoImeEditText extends EditText {
public NoImeEditText(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
public boolean onCheckIsTextEditor() {
return false;
}
}
I have tested Arun Pdiyan solution and works like a charm with null LocalTextInputService (in my case I read data from device attached Barcode reader)
CompositionLocalProvider(
LocalTextInputService provides null
) {
TextField(
value = barcodeReaderService.readedText.value,
onValueChange = { textState.value=it },
label = { Text("The Label") }
)
}
Explanation
I created a Composable ReadonlyTextField, that places a invisible box in front of the text field. The box has the same size as the text field.
With that workaround you can't focus the text field anymore, so no keyboard appears. In order to apply custom click handling, i added a onClick to the Box-Modifier.
This is not really a clean solution, but a good workaround.
Implementation of ReadonlyTextField
#Composable
fun ReadonlyTextField(
value: TextFieldValue,
onValueChange: (TextFieldValue) -> Unit,
modifier: Modifier = Modifier,
onClick: () -> Unit,
label: #Composable () -> Unit
) {
Box {
TextField(
value = value,
onValueChange = onValueChange,
modifier = modifier,
label = label
)
Box(
modifier = Modifier
.matchParentSize()
.alpha(0f)
.clickable(onClick = onClick),
)
}
}
Usage of ReadonlyTextField
#Composable
fun App() {
val textState = remember { mutableStateOf(TextFieldValue()) }
Column {
ReadonlyTextField(
value = textState.value,
onValueChange = { textState.value = it },
onClick = {
// custom click handling (e.g. open dialog)
},
label = {
Text(text = "Keyboardless Input")
}
)
}
}
A complete integrated example can be found in my medium post:
https://caelis.medium.com/jetpack-compose-datepicker-textfield-39808e42646a
Credits also go to this stackoverflow answer:
Jetpack Compose: Disable Interaction with TextField
You can hide keyboard on compose by providing TextInputService to TextField. You can implement your TextInputService or just pass it null for disabling input service.
CompositionLocalProvider(
// You can also provides null to completely disable the default input service.
LocalTextInputService provides myTextInputService
) {
BaseTextField(...)
}
You may see google employee answer here about this subject.
With ReadonlyTextField it is not possible to position the cursor. So added wrapped EditText inside a compose AndroidView
#Composable
fun NoKeyboardTextField(
modifier: Modifier,
text: String,
textColor: Int
) {
AndroidView(
modifier = modifier,
factory = { context ->
AppCompatEditText(context).apply {
isFocusable = true
isFocusableInTouchMode = true
showSoftInputOnFocus = false
}
},
update = { view ->
view.setText(text)
view.setTextColor(textColor)
view.setSelection(text.length)
}
)
}

Categories

Resources