I am using Navigation
from Jetpack
. That means, I have only 1 single activity and I want to move the layout up the keyboard whenever the keyboard pops up. the problem is, this should happen only on certain Fragment
.
Setting on Manifest
works, the problem is that I need it only on a specific Fragment
.
android:windowSoftInputMode="adjustResize"
The app has a BottomNavigationView
, so, for retaining Fragment
state, I am hidding instead of replacing and removing it. Also, each fragment has its NavigationGraph
. So I cannot access to the lifecycle explicitly. I thought that setting that mode programmatically was going to work. But, as explained, it did not.
override fun onResume() {
super.onResume()
keyboardMode = requireActivity().window.attributes.softInputMode
requireActivity().window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE)
}
override fun onPause() {
super.onPause()
keyboardMode?.let { requireActivity().window.setSoftInputMode(it) }
}
I also tried setting on my XML:
android:fitsSystemWindows="true"
It did not work. Also, on which of my nested fragments should I put it?
Any thoughts? What could I do? Is there a way to get size of the keyboard, move the fragment's content up the size of keyboard with a listener or something similar? Might solve that issue that way only...
EDIT:
I found this, which I change as needed:
class KeyboardListener(
private val binding: FragmentWebviewBinding,
private val listener: KeyboardInterface
) {
private var keyboardListenersAttached = false
private var rootLayout: ViewGroup? = null
private val keyboardLayoutListener = OnGlobalLayoutListener {
val r = Rect()
binding.webview.getWindowVisibleDisplayFrame(r)
val screenHeight = binding.root.rootView.height
// r.bottom is the position above soft keypad or device button.
// if keypad is shown, the r.bottom is smaller than that before.
val keypadHeight = screenHeight - r.bottom
logD("Keyboard", "keypadHeight = $keypadHeight")
if (keypadHeight > screenHeight * 0.15) { // 0.15 ratio is perhaps enough to determine keypad height.
// keyboard is opened
listener.onShowKeyboard(keypadHeight)
} else {
// keyboard is closed
listener.onHideKeyboard()
}
}
fun attachKeyboardListeners() {
if (keyboardListenersAttached) return
rootLayout = binding.root as ViewGroup?
rootLayout?.viewTreeObserver?.addOnGlobalLayoutListener(keyboardLayoutListener)
keyboardListenersAttached = true
}
fun destroy() {
if (keyboardListenersAttached) {
rootLayout?.viewTreeObserver?.removeGlobalOnLayoutListener(keyboardLayoutListener)
}
}
interface KeyboardInterface {
fun onShowKeyboard(keyboardHeight: Int)
fun onHideKeyboard()
}
}
What I am doing with the listener is: if keyboard is opened, I am adding bottom margin of the keyboardHeight
, if closed, returned to 0. The problem is, that is not measuring correctly. I am not sure if is from px
to dp
or any similar. Also, it is being called all the time.
But is the best approach so far.
EDIT 2: I was measuring incorrectly.
The correct solution was changing this lines on keyboardLayoutListener
:
binding.webview.getWindowVisibleDisplayFrame(r)
val screenHeight = binding.root.height
Although, the toolbar is moving up even though I am moving on the padding of the webview only:
override fun onHideKeyboard() {
with(binding.webview) {
val params = layoutParams as ConstraintLayout.LayoutParams
params.setMargins(0, 0, 0, 0)
layoutParams = params
}
}
override fun onShowKeyboard(keyboardHeight: Int) {
with(binding.webview) {
val params = layoutParams as ConstraintLayout.LayoutParams
params.setMargins(0, 0, 0, keyboardHeight)
layoutParams = params
}
}