Android hide keyboad and dismiss bottomsheetfragment when back pressed

1k Views Asked by At

I have issue with hiding the bottomsheetfragment and the keyboard with one back click. the current implementation require user to click back twice to close keyboard and bottomsheet.

I implement setOnKeyListener but the first back click hide keyboard and second click fire the back event

Link to video

the dialog style

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

class CommentInputFragment : BottomSheetDialogFragment() {

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
     setStyle(DialogFragment.STYLE_NORMAL, R.style.BottomSheetDialog);


}

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
                          savedInstanceState: Bundle?): View? {
    // Inflate the layout for this fragment
    var view= inflater.inflate(R.layout.fragment_comment_input, container, false)
    return view;
}

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

    return dialog

}

override fun onResume() {
    super.onResume()

    dialog?.setOnKeyListener(object: DialogInterface.OnKeyListener
    {
        override fun onKey(p0: DialogInterface?, keyCode: Int, p2: KeyEvent?): Boolean {
            if ((keyCode ==  android.view.KeyEvent.KEYCODE_BACK))
            {
                // To dismiss the fragment when the back-button is pressed.
                dismiss();
                return true;
            }
            // Otherwise, do nothing else
            else return false;
        }
    })
}

override fun onActivityCreated(savedInstanceState: Bundle?) {
    super.onActivityCreated(savedInstanceState)
    et_add_comment.requestFocus();
}


}
4

There are 4 best solutions below

0
On

Problem:

The back press won't be intercepted by the dialog in this case as it's trigger by the soft keyboard, if you notice that the back arrow points to the bottom (in recent devices) not to the left. Therefore the callback of the dialog?.setOnKeyListener won't be triggered.

So, you need to intercept the key event by some keyboard listener; well this is a challenge as per many posts like here, and there, and each solution can work in particular cases.

Solution

We will register a listener to the root ViewGroup of the BottomSheetFragment layout (R.layout.fragment_comment_input) when dispatchKeyEventPreIme() is called. This gets called for the key events attached to this layout.

Create the listener:

interface OnBackPressListener {
    fun onBackPressed(event: KeyEvent)
}

So, as the root is FrameLayout, then customize it:

class DispatchKeyFrameLayout @JvmOverloads constructor(
    context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : FrameLayout(context, attrs, defStyleAttr) {

    private var listener: OnBackPressListener? = null

    fun setOnBackPressListener (listener: OnBackPressListener) {
        this.listener = listener
    }

    override fun dispatchKeyEventPreIme(event: KeyEvent): Boolean {
        // Trigger the listener callback
        listener?.onBackPressed(event)
        return super.dispatchKeyEventPreIme(event)
    }
}

And use it as the root layout in the R.layout.fragment_comment_input:

<com.example.kotlintest.DispatchKeyFrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/root_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

<!--Other views-->

</com.example.kotlintest.DispatchKeyFrameLayout>

Then set the listener in the BottomSheetFragment onResume() to dismiss the dialog.

override fun onResume() {
    super.onResume()

    layout.setOnBackPressListener(object : OnBackPressListener{
        override fun onBackPressed(event: KeyEvent) {
            if (event.keyCode == android.view.KeyEvent.KEYCODE_BACK)
                dismiss()
        }
    })
}

And now you can safely remove dialog?.setOnKeyListener

Preview:

1
On

i fix the issue by using custom EditText

public class CustomEditText extends EditText {

public CustomEditText(Context context, AttributeSet attrs) {
    super(context, attrs);
}

@Override
public boolean onKeyPreIme(int keyCode, KeyEvent event) {
    if (keyCode == KeyEvent.KEYCODE_BACK) {
        // User has pressed Back key. So hide the keyboard
        InputMethodManager mgr = (InputMethodManager)

                getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
        mgr.hideSoftInputFromWindow(this.getWindowToken(), 0);
    }
    return false;
}
}
5
On

hide keyboard and dismiss dialog.

 override fun onKey(p0: DialogInterface?, keyCode: Int, event: KeyEvent?): Boolean {
        if ((keyCode ==  android.view.KeyEvent.KEYCODE_BACK &&  event.getAction()== KeyEvent.ACTION_DOWN))
        {
            // To dismiss the fragment when the back-button is pressed.
            val imm = getSystemService(Context.INPUT_METHOD_SERVICE) as? 
           InputMethodManager
           imm?.hideSoftInputFromWindow(view.windowToken, 0)
            dismiss();
            
            return true;
        }
1
On
override fun onBackPressed() {
    super.onBackPressed()
    val imm = getSystemService(Context.INPUT_METHOD_SERVICE) as? 
       InputMethodManager
    imm?.hideSoftInputFromWindow(view.windowToken, 0)
    dismiss();
 }