Show entire bottom sheet with EditText above Keyboard
AndroidAndroid LayoutAndroid SoftkeyboardBottom SheetAndroid Problem Overview
I'm implementing a UI where a bottom sheet will appear above the keyboard with an EditText for the user to enter a value. The problem is the View is being partially overlapped by the keyboard, covering up the bottom of the bottom sheet.
Here is the Bottom Sheet and no keyboard.
Here is the Bottom Sheet with the keyboard showing.
What's the best method to ensure the entire Bottom Sheet is shown?
Thanks.
Android Solutions
Solution 1 - Android
Just reposting @jblejder from this question https://stackoverflow.com/questions/44625365/keyboard-hides-bottomsheetdialogfragment/50948146#50948146 since it worked for me, to make it easier for others to find:
The most convenient way that I found to change this is by creating style:
<style name="DialogStyle" parent="Theme.Design.Light.BottomSheetDialog">
<item name="android:windowIsFloating">false</item>
<item name="android:statusBarColor">@android:color/transparent</item>
<item name="android:windowSoftInputMode">adjustResize</item>
</style>
And set this in onCreate method of your BottomSheetDialogFragment:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setStyle(DialogFragment.STYLE_NORMAL, R.style.DialogStyle)
}
This is how it looks on my device:
==== UPDATE ====
As already mentioned in the Comments a few times, you might also need to set the state of the BottomSheetDialog to STATE_EXPANDED like in Nordknight's answer below
dialog = new BottomSheetDialog(getContext(), R.style.BottomSheetDialog);
dialog.setOnShowListener(new DialogInterface.OnShowListener() {
@Override
public void onShow(DialogInterface dialog) {
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
BottomSheetDialog d = (BottomSheetDialog) dialog;
FrameLayout bottomSheet = d.findViewById(R.id.design_bottom_sheet);
BottomSheetBehavior bottomSheetBehavior = BottomSheetBehavior.from(bottomSheet);
bottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
}
},0);
}
});
Solution 2 - Android
This might be a redundant answer. Although just pointing out the issue.
If you're using BottomSheetDialogFragment
, the only way is to enable the attribute android:windowIsFloating to true. This will enable the whole window to be on top of whatever is trying to take the space behind it.
<style name="BottomSheetDialogThemeNoFloating" parent="Theme.Design.Light.BottomSheetDialog">
<item name="android:windowIsFloating">false</item>
<item name="android:windowSoftInputMode">adjustResize|stateVisible</item>
</style>
Then in your onCreate()
of your dialog, set this style.
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// set the window no floating style
setStyle(DialogFragment.STYLE_NORMAL, R.style.AppRoundedBottomSheetDialogThemeNoFloating)
}
This is handy for those who frequently use bottom sheets and may want to deal with EditText
and soft keyboard overlapping each other.
Note: The class KeyboardUtil
by mikepenz has an issue in which on certain phones, the content view with input field is automatically pushed above keyboard despite giving bottom padding to the whole content view supplied.
Solution 3 - Android
dialog = new BottomSheetDialog(getContext(), R.style.BottomSheetDialog);
dialog.setOnShowListener(new DialogInterface.OnShowListener() {
@Override
public void onShow(DialogInterface dialog) {
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
BottomSheetDialog d = (BottomSheetDialog) dialog;
FrameLayout bottomSheet = d.findViewById(R.id.design_bottom_sheet);
BottomSheetBehavior bottomSheetBehavior = BottomSheetBehavior.from(bottomSheet);
bottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
}
},0);
}
});
This code works fine at Fragment's onCreateView method (thanks for ADM)
Solution 4 - Android
Some answers seem to do the trick better than others but will need modification when using the new material design components instead of the older support libraries while also using kotlin
Hope this will help someone.
BottomSheetDialog(this, R.style.DialogStyle).apply {
setContentView(layoutInflater.inflate(R.layout.bottom_sheet, null))
window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE)
findViewById<EditText>(R.id.time_et)?.requestFocus()
show()
}
layout/bottom_sheet.xml
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#ffffff"
android:padding="16dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<View
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1" />
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="5"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Time"
android:textColor="#000000"
android:textSize="24sp"
android:textStyle="bold" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:orientation="horizontal">
<EditText
android:id="@+id/time_et"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:inputType="numberSigned"
android:minWidth="50dp"
android:text="15" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:text="min" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:background="#000"
android:text="Save"
android:textColor="#fff" />
</LinearLayout>
</ScrollView>
styes.xml (Split for v-21 for using statusBarColor)
<style name="DialogStyle" parent="Theme.MaterialComponents.Light.BottomSheetDialog">
<item name="android:windowIsFloating">false</item>
<item name="android:statusBarColor">@android:color/transparent</item>
<item name="android:windowSoftInputMode">adjustResize</item>
</style>
Solution 5 - Android
A BottomSheetDialog
can be helpful for this. it will open with Softkeyboard open with focus on edit text.But user can still close the Softkeyboard and Dialog will be reset to Bottom. Again focusing will make dialog appear at top of Softkeyboard.
public void showDialog() {
final BottomSheetDialog dialog=new BottomSheetDialog(this);
dialog.setContentView(R.layout.item_dialog);
dialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
dialog.show();
}
You can make the BottomSheetDialog expanded over keyboard . But for this you need to call it after SoftKeyboard Open. the Expand code is .
BottomSheetDialog d = (BottomSheetDialog) dialog;
FrameLayout bottomSheet = (FrameLayout) d.findViewById(android.support.design.R.id.design_bottom_sheet);
BottomSheetBehavior.from(bottomSheet).setState(BottomSheetBehavior.STATE_EXPANDED);
I have tested it on DialogInterface.OnShowListener()
but its not working . Tested with it 1 second delay its working . But Delay is not the solution . You need to figure out the on which action you should expand the dialog.
final BottomSheetDialog dialog=new BottomSheetDialog(this);
dialog.setContentView(R.layout.item_dialog);
dialog.getWindow().setSoftInputMode(
WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE|
WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
FrameLayout bottomSheet = (FrameLayout) dialog.findViewById(android.support.design.R.id.design_bottom_sheet);
BottomSheetBehavior.from(bottomSheet).setState(BottomSheetBehavior.STATE_EXPANDED);
}
},2000);
dialog.show();
Solution 6 - Android
An updated answer for those using Material Components theme, and also an improved answer to remove the need to add anything into each dialog's onCreate()
.
In your main AppTheme style, you can add the attribute bottomSheetDialogTheme
to apply the style to all of your BottomSheetDialogFragments:
<style name="AppTheme" parent="Theme.MaterialComponents.Light.DarkActionBar">
<item name="colorPrimary">@color/primary</item>
<item name="colorPrimaryDark">@color/primary_dark</item>
<item name="colorAccent">@color/accent</item>
<item name="bottomSheetDialogTheme">@style/BottomSheetDialogStyle</item>
</style>
So with the above, no need to add anything to your BottomSheetDialogFragment code.
And then, as previous answers, your Dialog style, noting to also match the style to the same Material Components library (or you'll get some weird looking buttons, edittexts etc):
<style name="BottomSheetDialogStyle" parent="Theme.MaterialComponents.Light.BottomSheetDialog">
<item name="android:windowIsFloating">false</item>
<item name="android:windowSoftInputMode">adjustResize</item>
<item name="android:statusBarColor">@android:color/transparent</item>
<item name="colorPrimary">@color/primary</item>
<item name="colorPrimaryDark">@color/primary_dark</item>
<item name="colorAccent">@color/accent</item>
</style>
Note that I am adding my app theme colors back in here; as you can't have multiple inheritance in Android styles, you may want these colors defining here so any buttons and accents align with the rest of your app.
Solution 7 - Android
This one working
BottomSheetDialog dialog = new BottomSheetDialog(this, R.style.DialogStyle);
View sheetView = getLayoutInflater().inflate(R.layout.dialog_remark, null);
Objects.requireNonNull(dialog.getWindow())
.setSoftInputMode(SOFT_INPUT_STATE_VISIBLE);
dialog.setContentView(sheetView);
dialog.setOnShowListener(new DialogInterface.OnShowListener() {
@Override
public void onShow(DialogInterface dialog) {
BottomSheetDialog d = (BottomSheetDialog) dialog;
View bottomSheetInternal = d.findViewById(com.google.android.material.R.id.design_bottom_sheet);
BottomSheetBehavior.from(bottomSheetInternal).setState(BottomSheetBehavior.STATE_EXPANDED);
}
});
dialog.show();
add this style to your styles.xml
<style name="DialogStyle" parent="Theme.Design.Light.BottomSheetDialog">
<item name="android:windowIsFloating">false</item>
<item name="android:statusBarColor">@android:color/transparent</item>
<item name="android:windowSoftInputMode">adjustPan</item>
</style>
add your layout like this
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:id="@+id/scrollview"
android:layout_height="match_parent">
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="8dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_margin="8dp"
android:fontFamily="@font/montserratmedium"
android:text="Add Remarks"
android:textColor="@android:color/black"
android:textSize="18sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="24dp"
android:fontFamily="@font/montserratmedium"
android:text="Branch"
android:textColor="#8B8B8B"
android:textSize="18sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:fontFamily="@font/montserratmedium"
android:text="BLR-CO-SINDHUBHAVAN-384"
android:textColor="@android:color/black"
android:textSize="18sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="24dp"
android:fontFamily="@font/montserratmedium"
android:text="Enter Remarks"
android:textColor="#8B8B8B"
android:textSize="18sp" />
<EditText
android:id="@+id/input_remark"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:layout_marginTop="8dp"
android:background="@drawable/remark_inputbg"
android:gravity="start"
android:inputType="textMultiLine"
android:lines="5" />
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:id="@+id/action"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:weightSum="2">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:layout_weight="1"
android:background="@drawable/reset_bg"
android:padding="8dp"
android:text="CANCEL" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:layout_weight="1"
android:background="#4F4DBB"
android:padding="8dp"
android:text="CANCEL"
android:textColor="@android:color/white" />
</LinearLayout>
</RelativeLayout>
</LinearLayout>
</androidx.cardview.widget.CardView>
</ScrollView>
Solution 8 - Android
private fun enterMobileNumberPopUp() {
val dialog = BottomSheetDialog(this,R.style.DialogStyle)
val view = layoutInflater.inflate(R.layout.layout_otp, null)
dialog.setContentView(view)
dialog.behavior.state = BottomSheetBehavior.STATE_EXPANDED
dialog.show()}
<style name="DialogStyle" parent="Theme.Design.Light.BottomSheetDialog">
<item name="android:windowIsFloating">false</item>
<item name="android:statusBarColor">@android:color/transparent</item>
<item name="android:windowSoftInputMode">adjustResize</item>
Style reference
Solution 9 - Android
bottomSheetDialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
It will work for sure.
Solution 10 - Android
Try this solution:
https://stackoverflow.com/a/45197761/18311024
Using NestedScrollView as root layout work for me.
Solution 11 - Android
For this, I found an AlertDialog
worked best. While it doesn't sit flush against the bottom or side of the screen, it still looks good enough.
First, create the AlertDialog
with your view.
val view = LayoutInflater.from(context).inflate(R.layout.alert, null)
dialog = AlertDialog.Builder(context)
.setView(view)
.create()
Next, set the gravity.
dialog.window.attributes.gravity = Gravity.BOTTOM
And finally, show it.
dialog.show()
You can also bind the keyboard to stay with the dialog, by using an onDismissListener
.
After showing the AlertDialog
, I force up the keyboard.
Call this method, passing in your EditText
.
fun showKeyboard(view: View?) {
if (view == null) return;
val imm = (InputMethodManager) view.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
imm.toggleSoftInput(InputMethodManager.SHOW_FORCED, InputMethodManager.HIDE_IMPLICIT_ONLY);
}
And for dismissing within the onDismissListener
.
private fun hideKeyboard() {
val imm = context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
imm.toggleSoftInput(InputMethodManager.HIDE_IMPLICIT_ONLY, 0)
}
Solution 12 - Android
My answer might be useful for someone who is still looking for solution. If keyboard is covering edittext in BottomSheetDialogFragment then in setupDialog()
method create instance of a class KeyboardUtil and pass your rootview.
@Override
public void setupDialog(final Dialog dialog, int style) {
super.setupDialog(dialog, style);
View view = View.inflate(getActivity(), R.layout.reopen_dialog_layout, null);
new KeyboardUtil(getActivity(), view);
}
Create a new class
public class KeyboardUtil {
private View decorView;
private View contentView;
//a small helper to allow showing the editText focus
ViewTreeObserver.OnGlobalLayoutListener onGlobalLayoutListener = new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
Rect r = new Rect();
//r will be populated with the coordinates of your view that area still visible.
decorView.getWindowVisibleDisplayFrame(r);
//get screen height and calculate the difference with the useable area from the r
int height = decorView.getContext().getResources().getDisplayMetrics().heightPixels;
int diff = height - r.bottom;
//if it could be a keyboard add the padding to the view
if (diff != 0) {
// if the use-able screen height differs from the total screen height we assume that it shows a keyboard now
//check if the padding is 0 (if yes set the padding for the keyboard)
if (contentView.getPaddingBottom() != diff) {
//set the padding of the contentView for the keyboard
contentView.setPadding(0, 0, 0, diff);
}
} else {
//check if the padding is != 0 (if yes reset the padding)
if (contentView.getPaddingBottom() != 0) {
//reset the padding of the contentView
contentView.setPadding(0, 0, 0, 0);
}
}
}
};
public KeyboardUtil(Activity act, View contentView) {
this.decorView = act.getWindow().getDecorView();
this.contentView = contentView;
//only required on newer android versions. it was working on API level 19
if (Build.VERSION.SDK_INT >= 19) {
decorView.getViewTreeObserver().addOnGlobalLayoutListener(onGlobalLayoutListener);
}
}
/**
* Helper to hide the keyboard
*
* @param act
*/
public static void hideKeyboard(Activity act) {
if (act != null && act.getCurrentFocus() != null) {
InputMethodManager inputMethodManager = (InputMethodManager) act.getSystemService(Activity.INPUT_METHOD_SERVICE);
inputMethodManager.hideSoftInputFromWindow(act.getCurrentFocus().getWindowToken(), 0);
}
}
public void enable() {
if (Build.VERSION.SDK_INT >= 19) {
decorView.getViewTreeObserver().addOnGlobalLayoutListener(onGlobalLayoutListener);
}
}
public void disable() {
if (Build.VERSION.SDK_INT >= 19) {
decorView.getViewTreeObserver().removeOnGlobalLayoutListener(onGlobalLayoutListener);
}
}
}
Solution 13 - Android
Kotlin, +viewBinding, +by using the accepted answer's dialog style
val bottomSheet = BottomSheetDialog(this, R.style.BottomSheetDialogStyle)
val binding = [YourSheetBinding].inflate(LayoutInflater.from(YourActivity.this))
bottomSheet.setContentView(binding.root)
bottomSheet.behavior.state = BottomSheetBehavior.STATE_EXPANDED
bottomSheet.show()
Solution 14 - Android
See https://stackoverflow.com/a/61813321/2914140:
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val dialog = super.onCreateDialog(savedInstanceState) as BottomSheetDialog
dialog.behavior.state = BottomSheetBehavior.STATE_EXPANDED
return dialog
}
But if a layout is not tall enough, you can use https://stackoverflow.com/a/66287187/2914140 instead. It will open BottomSheetDialog almost fullscreen:
<style name="BottomSheetDialogTheme" parent="Theme.MaterialComponents.Light.BottomSheetDialog">
<item name="android:windowIsFloating">false</item>
<item name="android:windowSoftInputMode">adjustResize|stateVisible</item>
</style>
Solution 15 - Android
Fiddling with BottomSheetDialogFragmentwasn't really worth it. So I just changed it to a simple DialogFragment
and just set its gravity to bottom:
window.setGravity(Gravity.BOTTOM);
Worked like a charm.