TextField Covered By Software Keyboard And Doesn't Scroll Into View - kotlin

When using Compose with a TextField at the bottom of the screen, when I focus the TextField, the software keyboard is opening up and covering the TextField, and it is not scrolling the TextField into view.
I am using accompanist and have things set up so that it could scroll into view, but it is not doing it automatically.
I also found that if the keyboard is already open, and you focus a TextField that is scrolled off screen, it does automatically scroll it onto screen. So it sort of seems like the behavior is there, but it just isn't working correctly because the focus happens before the keyboard opens.
Does anyone have a good solution to make the TextField scroll into view when the software keyboard opens?
Edit:
Simple Example:
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
WindowCompat.setDecorFitsSystemWindows(window, false)
window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE)
setContent {
ProvideWindowInsets {
Column(
Modifier
.statusBarsPadding()
.navigationBarsWithImePadding()
.verticalScroll(rememberScrollState())
) {
val focusManager = LocalFocusManager.current
(0..20).forEach {
var test by remember { mutableStateOf("") }
TextField(
test,
{ test = it },
label = { Text(it.toString()) },
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Next),
keyboardActions = KeyboardActions(onNext = {
focusManager.moveFocus(FocusDirection.Next)
})
)
}
}
}
}
}
}
With the keyboard closed, if I click a TextField near the bottom, it gets focus, then the keyboard pops up and covers it. What I want to happen is after the keyboard pops up, the TextField should scroll into view.
However if I then hit the Next button on the keyboard, it will go to the next TextField and scroll it into view. Which is what I want.

I had a similiar issue. This seems to be a bug in Compose. To fix it, add the following in your activity's onCreate:
window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE)

Related

Jetpack compose: Top App Bar pinnedScrollBehavior not changing color on scroll from text focus events

Okay, so this is a bit of a specific issue, but hopefully someone understands what's happening:
I'm using a Jetpack Compose Material 3 CenterAlignedTopAppBar, with TopAppBarDefaults.pinnedScrollBehavior to make it so that the color of the appbar changes when I scroll down on nested content.
It works! In most cases. However, one of my screens has a large text field, that when clicked causes the content to scroll down by itself (to focus on the text field)—and that seems to confuse the appbar, which doesn't change color. It will only change color if I manually scroll up and down.
Relevant code:
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
Scaffold(
contentWindowInsets = EmptyWindowInsets,
modifier = Modifier
.fillMaxHeight()
.nestedScroll(scrollBehavior.nestedScrollConnection),
topBar = {
// just a wrapper around CenterAlignTopAppBar
StandardTopAppBar(scrollBehavior = scrollBehavior)
},
content = { innerPadding ->
// I've also tried with LazyColumn and see the same behavior
Column(
Modifier
.padding(innerPadding)
.padding(start = 10.dp, end = 10.dp)
.fillMaxHeight()
.verticalScroll(rememberScrollState()),
verticalArrangement = Arrangement.spacedBy(10.dp),
) {
tl;dr; manually scrolling changes the appbar color, but scrolling caused by clicking into a text field that scrolls into view does not. Any idea how I could fix this?
Probably not quite what you are looking for unfortunately, but I had a similar problem. My TopAppBar color was also stuck, but when navigating between different screens. I fixed it by assigning the TopAppBar a different scrollBehaviour depending on the screen.
val scrollBehavior1 = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
val scrollBehavior2 = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
In TopAppBar arguments:
scrollBehavior =
when (currentRoute) {
Screens.ExampleScreen1.route -> scrollBehavior1
Screens.ExampleScreen2.route -> scrollBehavior2
else -> null
}
Hopefully this helps you in one way or another.

On touch close AndroidX DialogFragment

Any ideas...?
Using import androidx.fragment.app.DialogFragment, I want to close the dialog when the user touches it. Touch outside dialog closes the dialog (good). I tried
dialog!!.setCancelable(true) // no effect
dialog!!.onTouchEvent...// not intuitive solution
There is no onClick event and I do not want a button. Thanks
If you want close dialog even you touch inside dialog, you can try this:
class EnsureDialog: DialogFragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?){
super.onViewCreated(view, savedInstanceState)
//you can set onclick with 'view' of this dialog
view.setOnClickListener {
dismiss()
}
}
}
And call DialogFragment from another view:
val dialog = EnsureDialog()
//you can manual set cancelable here:
//dialog.isCancelable = false
dialog.show(childFragmentManager, "")
Here I'm using kotlin, convert to java if you wan't.
Hope it helpful

How to reference a button from different view in Fragment. Kotlin

I want to achieve a simple task. I have an invisible button in one layout and a have a Fragment. When this function is executed I want the button in the other layout to become visible. However this layout with the button is not in the Fragment layout, which means I have to reference that button in the Fragment, but I don't know how to go about that.
This is what Fragment will look like to first time users. The images you see are in a recyclerview which inflates a layout. That layout has the invisible button.
Fragment class
//item subscribed
if (subscribeValueFromPref) {
subscribeAbstract.visibility = View.GONE
// abstractDownload.visibility = View.VISIBLE
} else {
subscribeAbstract.visibility = View.VISIBLE
// abstractDownload.visibility = View.VISIBLE
}
}
When this line of code executed the button in the fragment is gone and the button from the other layout becomes visible. As you can see I put two strokes in front of that code. Once the code has been executed the layout should look like this.
Summary
I want to reference a button in another layout from a fragment class.
Do you even need to communicate between fragments? Your example looks like a single fragment with a subscribe button, and a RecyclerView that contains items which have a button that can be displayed or hidden. You can just make that part of your Adapter's state, like this:
class MyAdapter : RecyclerView.Adapter<ViewHolder>() {
var showDownloadButtons = false
set(value) {
field = value
// call this to update the display (calls onBindViewHolder for items)
notifyDataSetChanged()
}
override fun onBindViewHolder(viewHolder: ViewHolder, position.Int) {
...
// when displaying an item, show or hide the download button as appropriate
viewHolder.downloadButton.visibility = if (showDownloadButtons) VISIBLE else INVISIBLE
}
}
Then in your fragment, when you work out your UI state based on that subscription value, you can just handle your main button and tell the adapter what to display:
if (subscribeValueFromPref) {
subscribeAbstract.visibility = View.GONE
adapter.showDownloadButtons = true
} else {
subscribeAbstract.visibility = View.VISIBLE
adapter.showDownloadButtons = false
}

How to clear focus of BasicTextField upon clicking somewhere else in Compose Multiplatform?

I have a BasicTextField in Jetbrains Compose Multiplatform for desktop. When I click on it, the TextField gets focus and becomes editable. However, when I click somewhere else in my application, the focus is not lost and the field is still editable as if I just clicked on it.
I know this behavior is normal and intended. Nonetheless, I want to the TextField to become unfocused when the user clicks somewhere else, regardless of it being a clickable or non-clickable composable.
How do I achieve this?
This is one way I've done it in the past.
val keyboardController = LocalSoftwareKeyboardController.current
val focusManager = LocalFocusManager.current
val interactionSource = remember { MutableInteractionSource() }
Then I made my parent layout clickable.
Box(modifier = Modifier
.clickable(
interactionSource = interactionSource,
indication = null // this gets rid of the ripple effect
) {
keyboardController?.hide()
focusManager.clearFocus(true)
}

Disable scroll for PDFView inside NSCollectionView

I have a PdfView inside a CollectionView. Since both have its own scrollviews I have conflicts in scrolling. So i want to disable scrolling for PdfView. How can I do it?
My solution is a little rough, but does work.
I only wanted to stop horizontal scrolling on my PDFViews - my collection view scrolls horizontally.
I made a view that selectively filters out the scroll mouse events and put it over the PFD view.
class HorizontalScrollBlockerView: NSView
{
var scrollNextResponder: NSResponder?
override func scrollWheel(with event: NSEvent) {
guard scrollNextResponder != nil else {
return
}
if fabs(event.deltaX) >= fabs(event.deltaY) {
scrollNextResponder!.scrollWheel(with: event)
} else {
super.scrollWheel(with: event)
}
}
}
I set the view's 'scrollNextResponder' to be the superView of the PDFView.
I also wrote a method that gets the first child scroll view (enclosedScrollView) which makes sure the PDFView is solid in the horizontal axis when correctly scaled.
if let scrollView = pdfView.enclosedScrollView {
scrollView.usesPredominantAxisScrolling = true
scrollView.hasHorizontalScroller = false
scrollView.horizontalScrollElasticity = NSScrollView.Elasticity.none
}
For a regular Scroll View you can remove scrolls by setting horizontal & vertical scrolls to false value. So for a PDF view try this :
NSScrollView *enclosingScrollView = [myPdfView enclosingScrollView];
[enclosingScrollView setHasHorizontalScroller:NO];
[enclosingScrollView setHasVerticalScroller:NO];