Keyboard hides BottomSheetDialogFragment
Asked Answered
A

8

40

There are more fields below the keyboard. This happened when i updated the support library. I know it's Kotlin but it looks almost the same as java. How do I fix this issue?

This is what it looks like:

enter image description here

My code:

class ProjectsEditBottomSheetFragment(val privateID: String,
                                  val publicID: String) : BottomSheetDialogFragment() {



private val mBottomSheetBehaviorCallback = object : BottomSheetBehavior.BottomSheetCallback() {
    override fun onStateChanged(bottomSheet: View, newState: Int) {
        if (newState == BottomSheetBehavior.STATE_HIDDEN) {
            dismiss()
        }

    }


    override fun onSlide(bottomSheet: View, slideOffset: Float) {
        if (slideOffset < -0.15f) {
            dismiss()
        }
    }
}


override fun setupDialog(dialog: Dialog, style: Int) {
    super.setupDialog(dialog, style)
    val view = View.inflate(context, R.layout.projects_edit_sheet, null)
    dialog.setContentView(view)

    dialog.window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE)


    val params = (view.parent as View).layoutParams as CoordinatorLayout.LayoutParams
    val behavior = params.behavior

    if (behavior != null && behavior is BottomSheetBehavior<*>) {
        behavior.setBottomSheetCallback(mBottomSheetBehaviorCallback)
    }


    // Get and set values
    val realm = Realm.getDefaultInstance()
    val realmObject = realm.where(ProjectsRealmObject::class.java)
            .equalTo("privateID", privateID)
            .findFirst()




    realm.beginTransaction()
    view.title_input.text = SpannableStringBuilder(realmObject.title)
    view.description_input.text = SpannableStringBuilder(realmObject.description)
    view.public_checkbox.isChecked = realmObject.isPublic
    realm.commitTransaction()


    // Keyboard
    view.title_input.onFocusChangeListener = View.OnFocusChangeListener { _, hasFocus ->
        if (hasFocus) {
            (context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager).showSoftInput(view.title_input, InputMethodManager.SHOW_FORCED)
        } else {
            (context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager).hideSoftInputFromWindow(view.title_input.windowToken, 0)
        }
    }

    view.description_input.onFocusChangeListener = View.OnFocusChangeListener { _, hasFocus ->
        if (hasFocus) {
            (context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager).showSoftInput(view.description_input, InputMethodManager.SHOW_FORCED)
        } else {
            (context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager).hideSoftInputFromWindow(view.description_input.windowToken, 0)
        }
    }


    // Click listners
    view.public_layout.setOnClickListener { view.public_checkbox.toggle() }

    view.cancel.setOnClickListener {
        view?.hideKeyboard()
        dismiss()
    }

    view.save.setOnClickListener {
        view?.hideKeyboard()
        // Save to realm
        realm.beginTransaction()
        realmObject.title = if (view.title_input.text.toString() == "") getString(R.string.unnamed) else view.title_input.text.toString()
        realmObject.description = view.description_input.text.toString()
        realmObject.isPublic = view.public_checkbox.isChecked
        realmObject.synced = false
        realmObject.updatedRealm = TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()).toString() + ""
        realm.commitTransaction()

        ProjectsSync(context)

        toast("Sparat")

        dismiss()
    }

  }
}

xml:

<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="@color/white"
app:layout_collapseMode="none"
app:behavior_hideable="false"
app:behavior_peekHeight="100dp"
app:layout_behavior="android.support.design.widget.BottomSheetBehavior"
style="@style/Widget.Design.BottomSheet.Modal">

<FrameLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:id="@+id/content">

        <android.support.design.widget.TextInputLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:paddingRight="16dp"
            android:paddingLeft="16dp"
            android:layout_marginTop="16dp"
            android:layout_marginBottom="8dp">

            <android.support.design.widget.TextInputEditText
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:hint="@string/edit_info_placeholder_title"
                android:id="@+id/title_input"/>

        </android.support.design.widget.TextInputLayout>

        <android.support.design.widget.TextInputLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:paddingRight="16dp"
            android:paddingLeft="16dp">

            <android.support.design.widget.TextInputEditText
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:hint="@string/edit_info_placeholder_description"
                android:id="@+id/description_input"/>

        </android.support.design.widget.TextInputLayout>

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            android:clickable="true"
            android:background="@drawable/click"
            android:paddingTop="8dp"
            android:paddingBottom="8dp"
            android:id="@+id/public_layout">

            <android.support.v7.widget.AppCompatCheckBox
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginLeft="12dp"
                android:id="@+id/public_checkbox"
                android:layout_marginRight="8dp"/>

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@string/edit_info_placeholder_is_public"
                android:layout_gravity="center_vertical"
                style="@style/textMedium"/>

        </LinearLayout>


        <!-- Buttons -->
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            android:gravity="right"
            android:paddingBottom="8dp">

            <Button
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@string/edit_info_button_cancel"
                android:id="@+id/cancel"
                style="@style/Widget.AppCompat.Button.Borderless.Colored"/>

            <Button
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@string/edit_info_button_save"
                android:id="@+id/save"
                style="@style/Widget.AppCompat.Button.Borderless.Colored"/>

        </LinearLayout>

    </LinearLayout>


</FrameLayout>

Antherozoid answered 19/6, 2017 at 8:12 Comment(3)
kotlin code is fine. please post your styles.xml code.Royo
what version of android design library do you use? It works just fine in 25.3.1, but don't work in 25.4.0Dulla
any updates about this?Dulla
D
119

I found the solution for 27 api. So the reason why keyboard hides view even with SOFT_INPUT_ADJUST_RESIZE is that the windowIsFloating is set for Dialogs.

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:

enter image description here

Duodecimal answered 20/6, 2018 at 12:21 Comment(13)
Nice solution! Also note that it's important that the top layout is a ScrollView, otherwise it doesn't work.Sheepshead
@Sheepshead actually it does not matter what is the top layout. On screenshoot I have simple ConstraintLayout with EditText and TextView. BottomSheetDialogFragment wraps my layout in necessary layout.Duodecimal
@Duodecimal Maybe your layout is simple enough that you don't need it. I had more TextViews and buttons below the edit text and I had to wrap ConstraintLayout with a ScrollView otherwise it didn't work properly.Sheepshead
For me it only worked with android:isScrollContainer="true" on the child view that should be scrollable.Sheer
android:statusBarColor requires API 21 and why is it needed?Abolish
this works, i had parent ConstraintLayout with complex design, works only when i wrapped ConstraintLayout with NestedScrollView (ScrollView is not working)Henriquez
@Duodecimal I am using Constraint Layout and tried your solution but it did not work,can you add your XML code too?Bendigo
@Abolish android:statusBarColor is not necessary but without it, with other settings default, there will be status bar visible that breaks BottomSheetDialog feeling. @Bendigo I just tried my solution once again and seems to be working fine even with ConstraintLayout. Just make sure you have all vertical constraint set.Duodecimal
This answer might help: https://mcmap.net/q/206335/-android-show-bottomsheetdialogfragment-above-keyboardPhoto
If you don't want to set style to all your BottomSheetDialogFragments, you can define default style for app theme: <style name="YOURTHEME" parent="Theme.MaterialComponents.Light.NoActionBar"> <item name="bottomSheetDialogTheme">@style/DialogStyle</item> </style> Dealfish
Wow, thank you! Only worked when NestedScrollView was parent container.Hammurabi
to be more specified on the NestedScrollView it's not required in my case I've to extend only BottomSheetDialogFragment instead of the library I'm using and of course, the styling step is too important :DFire
@Sheepshead after around 4 hours of trying, finally i saw your comment and it's done, Thank You!!Antiperiodic
A
24

I tried all of answers in this topic but nothing helped. I looked through many sites and found only one solution that working for me.

 override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
    val dialog = super.onCreateDialog(savedInstanceState)

    dialog.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE)
    dialog.setOnShowListener {
        Handler().post {
            val bottomSheet = (dialog as? BottomSheetDialog)?.findViewById<View>(R.id.design_bottom_sheet) as? FrameLayout
            bottomSheet?.let {
                BottomSheetBehavior.from(it).state = BottomSheetBehavior.STATE_EXPANDED
            }
        }
    }

    return dialog
}

Original solution

Altdorfer answered 16/6, 2019 at 8:14 Comment(4)
Worked for me too!Interknit
Worked for me as well.Forster
May I know what is R.id.design_bottom_sheet?Spruik
@CheokYanCheng this is FrameLayout from com.google.android.material. If you can't find that id in the R class just use com.google.android.material.R.id.design_bottom_sheetAltdorfer
H
3

You can use the next class:

import android.graphics.Rect; 
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.design.widget.BottomSheetBehavior;
import android.support.design.widget.BottomSheetDialog;
import android.support.design.widget.BottomSheetDialogFragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;

public class TestBottomSheetDialog extends BottomSheetDialogFragment {

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View fragmentView = LayoutInflater.from(getContext()).inflate(R.layout.fragment_bottom_sheet, container, false);
        if (getDialog().getWindow() != null) {
           getDialog().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
        }
        if (getActivity() != null) {
            View decorView = getActivity().getWindow().getDecorView();
            decorView.getViewTreeObserver().addOnGlobalLayoutListener(() -> {
                Rect displayFrame = new Rect();
                decorView.getWindowVisibleDisplayFrame(displayFrame);
                int height = decorView.getContext().getResources().getDisplayMetrics().heightPixels;
                int heightDifference = height - displayFrame.bottom;
                if (heightDifference != 0) {
                    if (fragmentView.getPaddingBottom() != heightDifference) {
                        fragmentView.setPadding(0, 0, 0, heightDifference);
                    }
                } else {
                    if (fragmentView.getPaddingBottom() != 0) {
                        fragmentView.setPadding(0, 0, 0, 0);
                    }
                }
            });
        }
        getDialog().setOnShowListener(dialog -> {
            BottomSheetDialog d = (BottomSheetDialog) dialog;
            View bottomSheetInternal = d.findViewById(android.support.design.R.id.design_bottom_sheet);
            if (bottomSheetInternal == null) return;
             BottomSheetBehavior.from(bottomSheetInternal).setState(BottomSheetBehavior.STATE_EXPANDED);
        });
        return fragmentView;
    }
}
Hitch answered 28/2, 2019 at 10:14 Comment(1)
You should give some explanation on your answerZwieback
D
1

This is working for me

public class CustomBottomSheetDialogFragment extends BottomSheetDialogFragment 
{
 @Override
 public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle 
    savedInstanceState) {
View v = inflater.inflate(R.layout.content_dialog_bottom_sheet, container, false);


getDialog().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
    return v;
 }
Divergency answered 27/2, 2018 at 9:18 Comment(1)
May be. I didn't check on 27. Please put your working code for android 27 if you findDivergency
P
1

The answer to the highest score is partly right. In my case, 90% of the view is visible after the same style is set. Finally, I made it completely visible through the following solution:

editText.setOnFocusChangeListener { v, hasFocus ->
            if (hasFocus) {
                (this@****BottomSheetDialogFragment.dialog as BottomSheetDialog).behavior.state = BottomSheetBehavior.STATE_EXPANDED
            }
        }
Pouch answered 25/10, 2022 at 9:55 Comment(0)
S
0

This solution worked for me after spending 5 hours without luck:

Step #1:

Add this code to your styles.xml (located in res\values folder)

<style name="CustomizedBottomDialogStyle">
    <item name="android:windowBackground">@android:color/transparent</item>
    <item name="android:backgroundDimEnabled">true</item>
    <item name="android:backgroundDimAmount">0.7</item>
    <item name="android:windowIsFloating">false</item>
    <item name="android:windowAnimationStyle">@android:style/Animation.Dialog</item>
    <item name="android:statusBarColor">@android:color/transparent</item>
    <item name="android:windowSoftInputMode">adjustResize</item>
    <item name="android:background">@android:color/transparent</item>
</style>

The key here is to set android:windowIsFloating -> false, if it is true your code will not work! Therefor i used rather android:backgroundDimEnabled and android:backgroundDimAmount to make background looks transparent with beautiful overlay.

Step #2:

Write this function to adjust it programmatically (note it is not optional, you need to perform both steps #1 and #2):

private fun showDialog() {

    BottomSheetDialog(requireActivity(), R.style.CustomizedBottomDialogStyle).apply {

        window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE)

        setOnShowListener {
            Handler().post {
                val bottomSheet = findViewById<View>(R.id.design_bottom_sheet) as? FrameLayout
                bottomSheet?.let {
                    BottomSheetBehavior.from(it).state = STATE_EXPANDED
                }
            }
        }

        setContentView(R.layout.dialog_layout)

        // Your code goes here....

        show()
    }
}
Shoran answered 25/10, 2020 at 10:39 Comment(0)
S
0

Most solutions posted are changing the theme of BottomSheetDialogFragment.

However, this has a side effect, of "not smooth" keyboard animation.

My suggestion is to use

setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE)

The complete source code is as follow

import android.app.Dialog
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import com.yocto.wetodo.databinding.TodoInputDialogFragmentBinding

class TodoInputDialogFragment : BottomSheetDialogFragment() {

    private var _binding: TodoInputDialogFragmentBinding? = null

    // This property is only valid between onCreateView and
    // onDestroyView.
    private val binding get() = _binding!!

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
    }

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        _binding = TodoInputDialogFragmentBinding.inflate(inflater, container, false)
        val root: View = binding.root

        return root
    }

    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
        val dialog = super.onCreateDialog(savedInstanceState)

        dialog.window?.also {
            it.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE)
        }

        return dialog
    }

    override fun onDestroyView() {
        super.onDestroyView()
        _binding = null
    }
}

Output (Smooth keyboard animation)

enter image description here


But, there are 2 glitches which I do not have a solution yet.

How should we resolve deprecated SOFT_INPUT_ADJUST_RESIZE?

WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE is deprecated. Based https://developer.android.com/reference/android/view/WindowManager.LayoutParams#SOFT_INPUT_ADJUST_RESIZE, we should use Window#setDecorFitsSystemWindows(boolean). But, it isn't clear how we should apply Window#setDecorFitsSystemWindows(boolean) for the above case.


Smooth keyboard animation will gone if we are applying style?

If I want to apply theme on the BottomSheetDialogFragment, I will lost the smooth keyboard animation.

For instance, I add the following code

TodoInputDialogFragment.kt

class TodoInputDialogFragment : BottomSheetDialogFragment() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        setStyle(DialogFragment.STYLE_NORMAL, R.style.TodoInputDialogFragment)
    }
    ...

styles.xml

<style name="TodoInputDialogFragment" 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>

Output (No more smooth keyboard animation)

enter image description here

If you know how to resolve such glitches, leave your comment below :) Thank you

Spruik answered 19/2 at 11:35 Comment(0)
D
-1

add this to your styles

<style name="DialogStyle">
    <item name="android:windowBackground">@android:color/transparent</item>
    <item name="colorPrimaryDark">@android:color/transparent</item>
</style>

then in your's bottom sheet dialog's onCreate() add

setStyle(DialogFragment.STYLE_NO_FRAME, R.style.DialogStyle);

also don't forget to add to dialog's setupDialog() method

dialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
Dulla answered 5/8, 2017 at 8:39 Comment(1)
It worked now. I have posted the code separately which is working for me. Thanks for your suggestion, will try that as well.Divergency

© 2022 - 2024 — McMap. All rights reserved.