Android: Bottom Side EditText Position Shifting
You will never be satisfied with the Input position after the soft keyboard is ON if the input box is lower than the keyboard position. It’s too close to select anything else.
Please check the image for the problem. What can we do? Simple, let rise the view to be higher than the keyboard.
— === Menu === —
🌐1. UI Listener: OnGlobalLayoutListener
🔁2. Deal with Keyboard Data
☕3. Test Result
🌐1. UI Listener: OnGlobalLayoutListener
< === Menu
private var mGlobalLayoutListener: OnGlobalLayoutListener? = null
In Android, we need this listener to check the keyboard on/off. Usually, we can declare this listen as an Object in Kotlin. But we need to delete it when the activity is closed or you will have a memory leak. It’d be better to state it in a var and close it at onDestory().
override fun onDestroy() {
super.onDestroy()
rootView.viewTreeObserver
.removeOnGlobalLayoutListener(mGlobalLayoutListener)
}
Let’s add one in Activity — onCreate() or Fragment — onViewCreated():
override fun onViewCreated(...) {
super.onViewCreated(view, savedInstanceState)
...
// EditText adjustment
mGlobalLayoutListener = OnGlobalLayoutListener {
resolvePosition()
}
...
Now, you can attach the view which may have a keyboard issue. The fragment example,
rootView = view.findViewById(R.id.rootFL)
rootView // check keyboard and editText distance
.viewTreeObserver
.addOnGlobalLayoutListener(mGlobalLayoutListener)
🔁2. Deal with Keyboard Data
< === Menu
I put solution into a function, resolvePosition():
fun resolvePosition() {
// achieve keyboard and UI ratio
val kbStatus = isKeyboardShown(
customerEmailEt.rootView,
customerEmailEt,
::getKeyboardData
)
if (kbStatus) {
if (adjustEditText)
welcomeCL.translationY = diffGap.toFloat()
} else {
welcomeCL.translationY = 0f
}
}
(Check the image for welcomeCL.)
The isKeyboardShown(),
// Gather UI data from keyboard, Edittext + rootView
fun isKeyboardShown(
rootView: View,
mEt: EditText,
refSave: KFunction2<Int, Boolean, Unit>
): Boolean {
/* 128dp = 32dp * 4, minimum button height 32dp and
generic 4 rows soft keyboard */
val SOFT_KEYBOARD_HEIGHT_DP_THRESHOLD = 128
val r = Rect()
rootView.getWindowVisibleDisplayFrame(r)
val etR = Rect()
// EditText coordinate
val coords = intArrayOf(0, 0)
mEt.getLocationOnScreen(coords)
val etTop = coords[1]
val etBottom = coords[1] + mEt.height
mEt.getWindowVisibleDisplayFrame(etR)
val dm = rootView.resources.displayMetrics
/* heightDiff = rootView height - status bar height (r.top) -
visible frame height (r.bottom - r.top) */
val heightDiff = rootView.bottom - r.bottom
/* Threshold size: dp to pixels, x display density */
val isKeyboardShown = heightDiff >
SOFT_KEYBOARD_HEIGHT_DP_THRESHOLD * dm.density
var adjustEditText = false
var diffGap = 0
if (r.bottom < etBottom) {
adjustEditText = true
diffGap = r.bottom - etBottom - mEt.height / 2
}
refSave(diffGap, adjustEditText) // ejects data
val tag = "isKbShown: "
lgd(tag+"isKeyboardShown ? $isKeyboardShown, " +
"rootView bottom: ${rootView.bottom}\n" +
"heightDiff: $heightDiff, rect bottom :$r, " +
"et : $etTop :: $etBottom; diff: $diffGap"
)
return isKeyboardShown
}
It calculates the UI data and ejects the data to a local function, getKeyboardData().
The lgd() is from LogHelper.kt to display log on the console:
const val TAG = "MLOG"
fun lgd(s:String) = Log.d(TAG, s)
fun lgi(s:String) = Log.i(TAG, s)
fun lge(s:String) = Log.e(TAG, s)
The getKeyboardData() in Activity/Fragment:
private var diffGap = 0
private var adjustEditText = falseprivate fun getKeyboardData(
diffGap: Int, adjustEditText: Boolean
) {
this.diffGap = diffGap
this.adjustEditText = adjustEditText
}