TextField is hiding under the keyboard when focused - android-jetpack

I have a problem with TextField that is hiding under the keyboard, I've found the answers for the similar question here, but they are not helping me in my case. I have a LazyColumn with the list of different composables in it, and when I have not enough elements in the window for scroll to be activated, focusing on TextField is not lifting up focused TextField above keyboard. Keyboard just hides it.
My code:
val listState = rememberLazyListState()
typealias ComposableType = #Composable (Int) -> Unit
val uiList = listOf<ComposableType>( {IconButton}, {Text}, {CustomTextField(listState,it)}, {CustomTextField(listState,it)})
LazyColumn() {
itemsIndexed(uiList) { index, ui ->
ui.invoke(index)
}
}
val scope = rememberCoroutineScope()
#Composable
CustomTextField(scrollState: LazyListState, position: Int) {
OutlinedTextField(
modifier = Modifier.onFocusEvent { focusState ->
if (focusState.isFocused) {
scope.launch {
scrollState.animateScrollToItem(position)
}
}
)
}
So, for example if i have 10 CustomTextFields in my uiList, scroll works when one of TextField is focused. But when there are only 2 TextFields in the uiList, focusing on either of them does not lift up them above keyboard.
Also I tried using RelocationRequester() and used Column with scroll instead of LazyColumn, none of it helped.

It's a combination of things...
You need to add this in your activity's declaration in Android.xml
android:windowSoftInputMode="adjustResize"
Use BringIntoViewRequester as modifier in your TextField as you mentioned.
.bringIntoViewRequester(yourBringIntoViewRequester)
The steps above worked for me when the component gain focus programatically (using FocusRequester). However, when the user taps on the TextField, it didn't work for me. So I implemented a workaround (which I'm not very proud of): when the TextField gain focus, I wait a bit to use the RelocationRequester. So I added this modifier to my TextField.
.onFocusEvent {
if (it.isFocused) {
coroutineScope.launch {
delay(200)
yourBringIntoViewRequester.bringIntoView()
}
}
}
These three things worked for me.

You need to add this in your activity's declaration in AndroidManifest.xml
android:windowSoftInputMode="adjustResize"
You have to set wrapContentHeight().navigationBarsWithImePadding() to the modifier of parent composable
Column(modifier = Modifier.wrapContentHeight().navigationBarsWithImePadding()){}
This solution worked for me.

Add this to your activity in manifest file
android:windowSoftInputMode="adjustResize"
And in the column for example use :
Column (horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight()
.navigationBarsPadding().imePadding()
.verticalScroll(rememberScrollState())
.fillMaxHeight()
.padding(top = 20.dp))
works for me , happy coding ..

Related

Editable NSTextFields with Variable Height in NSTableView Rows in macOS App

Xcode 10.1, Swift 4.2, macOS 10.14.2
I am trying to make a simple to do list app for macOS where there are a series of NSTableView rows and inside each one is an NSTextField. Each field is a to-do item. I want the NSTableView rows to expand to fit the size of the text within each NSTextField.
I have all of the following working:
Setting the text in the NSTextField makes the NSTableView row expand as needed. Auto layout constraints are set in my storyboard.
Using tableView.reloadData(forRowIndexes: ..., columnIndexes: ...) sets the text and resizes the table row correctly.
But doing tableView.reloadData() always resets every NSTextField to a single line of text as shown here:
Interestingly, if you click into the NSTextField after reloading the whole table, the field resizes to fit its content again:
I believe I have set all the appropriate auto layout constraints on my NSTextField and I'm using a custom subclass for it as well (from a helpful answer here):
class FancyField: NSTextField{
override var intrinsicContentSize: NSSize {
// Guard the cell exists and wraps
guard let cell = self.cell, cell.wraps else {return super.intrinsicContentSize}
// Use intrinsic width to jibe with autolayout
let width = super.intrinsicContentSize.width
// Set the frame height to a reasonable number
self.frame.size.height = 150.0
// Calcuate height
let height = cell.cellSize(forBounds: self.frame).height
return NSMakeSize(width, height)
}
override func textDidChange(_ notification: Notification) {
super.textDidChange(notification)
super.invalidateIntrinsicContentSize()
}
}
⭐️Here is a sample project: https://d.pr/f/90CTEh
I'm at a loss as to what else I can try. Any ideas?
I think there is some basic problems with the constraints in interface builder. Resizing the window makes everything wonky. Also you should call validateEditing() in the textDidChange(forBounds:) in your FancyField class.
I created a sample project, that does what you want on Github
Write a comment if you have any problems with it.
Thinking a little about it, thought i would add the meat of the code here. Only thing that really needs to work, is the update on the NSTextField when the "Tasks" is being updated. Following is the code required for the NSTextField.
public class DynamicTextField: NSTextField {
public override var intrinsicContentSize: NSSize {
if cell!.wraps {
let fictionalBounds = NSRect(x: bounds.minX, y: bounds.minY, width: bounds.width, height: CGFloat.greatestFiniteMagnitude)
return cell!.cellSize(forBounds: fictionalBounds)
} else {
return super.intrinsicContentSize
}
}
public override func textDidChange(_ notification: Notification) {
super.textDidChange(notification)
if cell!.wraps {
validatingEditing()
invalidateIntrinsicContentSize()
}
}
}
Hope it helps.

Smooth scrolling in JavaFX TableView

I am writing a small chat application in Kotlin with TornadoFX that works so far.
I am currently trying to make it more visually appealing when receiving new messages.
The messages are in a TableView (sender - message) but scrolling to new messages isn't smooth like I would like.
The snippet where I need help is relatively short:
addEventHandler(ScrollToEvent.ANY) {
it.consume()
timeline {
val keyValue = KeyValue(/* property to change */, /* target value */, Interpolator.EASE_OUT)
keyframe(0.25.seconds) {
this.plusAssign(keyValue)
}
}
}
In general I need help figuring out which property to change and what the target should be in this line:
KeyValue(/* property to change */, /* target value */, Interpolator.EASE_OUT)
Ok, I found the solution.
One needs to lookup the ScrollBar the TableView provides, once enough rows are present (and when scrolling actually does anything).
From TornadoFX JavaFX Sync Scroll across tableviews, I adapted the lookup and came up with this, working, code:
addEventHandler(ScrollToEvent.ANY) {
it.consume()
timeline {
val scrollBar = lookupAll(".scroll-bar").first() as ScrollBar
val keyValue = KeyValue(scrollBar.valueProperty(), scrollBar.max, Interpolator.EASE_OUT)
keyframe(0.5.seconds) {
this.plusAssign(keyValue)
}
}
}

javafx - easiest way of changing the caret color

I would like to set the caret color for all JavaFX text inputs (e.g. TextField, TextArea, the ones in ComboBox:editable, DatePicker, etc...)
I found this Stackoverflow answer: How to change the caret color in JavaFX 2.0?
... and an example on GitHub.
The first one does change the text and the caret color which is not good. The second one extends the TextFieldSkin class, which is already better, but how can I use it in CSS?
Any help is appreciated.
UPDATE 1:
I found the following CSS style property for JavaFX controls: -fx-skin.
This would theoretically allow me to set a custom skin class (-fx-skin: "package.MySkin";), however, the skin class just isn't used!
The class looks like the following:
package gui;
…
public class MyTextFieldSkin extends TextFieldSkin
{
public MyTextFieldSkin(TextField tf) {
super(tf);
System.out.println("MyTextFieldSkin constructor called!");
ReadOnlyObjectWrapper<Color> farbe = new ReadOnlyObjectWrapper<>(Color.green);
caretPath.strokeProperty().bind(farbe);
caretPath.setStrokeWidth(1.5);
}
}
… and is set in CSS like that:
.text-field {
-fx-skin: "gui.MyTextFieldSkin";
}
What am I doing wrong? I looked at the source code of AquaFX, and they are doing it the same way as me!
After a bit of try & error, I solved the problem in the following way:
I gathered all TextFields and controls that have TextFields in them (like ComboBox, DatePicker and so on) inside a container recursively (in deference of TitledPane, ScrollPane, SplitPane and TabPane, because they don't publish their children in getChildren(), so one has to call the getContent() method of the individual classes and scan through it).
After I had all the TextField controls, I looped over them and changed their Skin with the following code:
public class MyTextFieldSkin extends TextFieldSkin {
public MyTextFieldSkin(TextField tf)
{
super(tf);
ReadOnlyObjectWrapper<Color> color = new ReadOnlyObjectWrapper<>(Color.RED);
caretPath.strokeProperty().bind(color);
}
}
Then I just had to call
textfield.setSkin(new MyTextFieldSkin(textfield));
and that was about it.
Cheers

applyCss doesn't work when remove a styleClass

I have a TextField styled by changing styleClass when its value is valid/invalid but there is a great problem. When the value becomes valid I still should change the TextField once more to make the styleClass be applied. I tried with applyCss() method just after, but it didn't work :(.
if(!valid){
field.getStyleClass().add("invalid-field");//Works excellent
} else {
field.getStyleClass().remove("invalid-field");//Doesn't work up to the time textProperty doesn't change once again
field.applyCss();
One common cause of these bugs is that you may have added the "invalid-field" style class more than once. (Remember, getStyleClass() returns a List<String>, not a Set<String>.) So you should probably take steps to make sure the style class is only added once, or take steps to remove all occurrences when you remove it.
I like to do both (where I come from, we call this a "belt and braces approach").
ObservableList<String> styleClasses = field.getStyleClass();
if(!valid)
if( ! styleClass.contains("invalid-field")){
styleClass.add("invalid-field");
}
} else {
// remove all occurrences:
styleClass.removeAll(Collections.singleton("invalid-field"));
}

How can I change the size of icons in the Tree control in Flex?

I embed SVG graphics in my Flex application using
package MyUI
{
public class Assets
{
[Embed(source="/assets/pic.svg"]
[Bindable]
public static var svgPic:Class;
}
}
and then extending the Tree class with some of my own code, setting the icon upon adding a node to the data provider:
public class MyTree extends Tree
{
public function MyTree()
{
// ...
this.iconField = "svgIcon";
// ...
this.dataProvider = new ArrayCollection;
this.dataProvider.addItem({ /* ... */ svgIcon: MyUI.Assets.svgPic /* ... */ });
// ...
}
}
Now I have two things I want to do:
use the SVG graphics in multiple places in the app, scaling them to the appropriate size for each appearance, i. e. scale them to a proper icon size when using them in the tree
change the size of the icon at runtime, e. g. display a slightly larger icon for selected items or let an icon "pulse" as a response to some event
I read the Flex documentation on the 9-slice scaling properties in the Embed tag, but I think that's not what I want.
Edit:
I unsuccessfully checked the "similar questions" suggested by SO, among others this one:
Flex: Modify an embedded icon and use it in a button?
Subclass mx.controls.treeClasses.TreeItemRenderer and make it resize the icon to your desired dimensions, or create your own item renderer implementation by using the same interfaces as TreeItemRenderer. Set a custom item renderer with the itemRenderer property:
exampleTree.itemRenderer = new ClassFactory( ExampleCustomItemRendererClass );
The answer to this question might point you in the right direction, without knowing more about the trouble you're having:
Flex: Modify an embedded icon and use it in a button?
Hope it helps!

Resources