Pop up and close the soft keyboard
- Eject soft keyboard
private val imm: InputMethodManager? bylazy { activity? .getSystemService(Context.INPUT_METHOD_SERVICE)as InputMethodManager? }
private fun showSoftInput(a){ imm? .let { binding.apply { etChat.requestFocus() it.showSoftInput(etChat, InputMethodManager.SHOW_FORCED) } } }Copy the code
- Close the soft keyboard
private fun hideSoftInput(a){ imm? .hideSoftInputFromWindow(binding.etChat.windowToken,0)}Copy the code
The soft keyboard is displayed when DialogFragment is displayed
There are two ways to pop up the soft keyboard in DialogFragment display:
1. Send a delayed task in onViewCreated
etChat.postDelayed({ showSoftInput() }, 200)
Copy the code
Note: This will not work if you call the display keyboard directly, because the view is not displayed at this time
2, Settings dialog style attribute android: windowSoftInputMode
<style name="live_editTextDialogStyle" parent="@android:style/Theme.Dialog"> <! -- Background transparency --> <item name="android:windowBackground">@android:color/transparent</item>
<item name="android:windowContentOverlay">@null</item> <! Float above the Activity --> <item name="android:windowIsFloating">true</item> <! -- border --> <item name="android:windowFrame">@null</item> <! -- Region blur outside Dialog --> <item name="android:backgroundDimEnabled">false</item> <! -- no title --> <item name="android:windowNoTitle">true</item> <! --> <item name="android:windowIsTranslucent">true</item> <! -- Display soft keyboard --> <item name="android:windowSoftInputMode">stateAlwaysVisible</item>
</style>
Copy the code
Close the soft keyboard when DialogFragment disappears
Dialog closing is divided into several cases, which are handled differently:
1. The user manually calls dialogFragment.dismiss ()
You can override the dismiss method to close the soft keyboard before calling it.
override fun dismiss(a) {
hideSoftInput()
super.dismiss()
}
Copy the code
2. The user clicks on the blank space to close the dialog
DialogFragment itself does not listen for the method before closing, only two related methods onCancel(Dialog: DialogInterface) and onDismiss(Dialog: DialogInterface).
Rewrite onCancel (dialog: DialogInterface)
override fun onCancel(dialog: DialogInterface) {
hideSoftInput()
super.onCancel(dialog)
}
Copy the code
When you do this and find that the soft keyboard is not closed, take a look at the flow: look at the hideSoftInputFromWindow method of InputMethodManager
public boolean hideSoftInputFromWindow(IBinder windowToken, int flags,
ResultReceiver resultReceiver) {
checkFocus();
synchronized (mH) {
if (mServedView == null|| mServedView.getWindowToken() ! = windowToken) {return false;
}
try {
return mService.hideSoftInput(mClient, flags, resultReceiver);
} catch (RemoteException e) {
throwe.rethrowFromSystemServer(); }}}Copy the code
HideSoftInput breakpoint: mServedView is null when the softkeyboard is closed in onCancel, so no close code is available. Where is mServedView assigned to null
void finishInputLocked() {
mNextServedView = null;
if(mServedView ! =null) {
if (DEBUG) Log.v(TAG, "FINISH INPUT: mServedView=" + dumpViewInfo(mServedView));
if(mCurrentTextBoxAttribute ! =null) {
try {
mService.finishInput(mClient);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
mServedView = null;
mCompletions = null;
mServedConnecting = false; clearConnectionLocked(); }}Copy the code
FinishInputLocked calls have two places
private boolean checkFocusNoStartInput(boolean forceNewFocus) {
// The rest of the code was omitted
if (mNextServedView == null) {
finishInputLocked();
// This method calls mservice.hidesoftinput, so it can be ruled out
closeCurrentInput();
return false;
}
return true;
}
public void windowDismissed(IBinder appWindowToken) {
checkFocus();
synchronized (mH) {
if(mServedView ! =null&& mServedView.getWindowToken() == appWindowToken) { finishInputLocked(); }}}Copy the code
So you can tell that when the callback goes to onCancel, the windowDismissed method is already called, so the soft keyboard cannot be closed. This method is eliminated, so let’s look at the onDismiss method
Rewrite onDismiss (dialog: DialogInterface)
override fun onDismiss(dialog: DialogInterface) {
hideSoftInput()
super.onDismiss(dialog)
}
Copy the code
In onDismiss call, mServedView in hideSoftInputFromWindow() is not null, but windowToken == NULL
imm? .hideSoftInputFromWindow(binding.etChat.windowToken,0)
// View.java ->
public IBinder getWindowToken() {
returnmAttachInfo ! =null ? mAttachInfo.mWindowToken : null;
}
Copy the code
In mAttachInfo dispatchDetachedFromWindow () set to null. Because the window we passed in for etchat is closed, the windowToken we get is NULL.
If mServedView is not null, it is the layout control of the activity attached to my DialogFragment. It can be regarded as the control that currently obtains the focus. Therefore, we can pass in the view of the current focus in the activity.
override fun onDismiss(dialog: DialogInterface) {
valview = activity? .window? .currentFocus view? .let { imm? .hideSoftInputFromWindow(it.windowToken,0)}super.onDismiss(dialog)
}
Copy the code
After running, the soft keyboard closes normally, OK, problem solved.
I thought the problem had been solved, but during operation, it was found that there would be occasional shutdown failure, and it was found that mServedView == NULL again. The reason is unknown, there is no way, this method is not safe enough.
A custom Dialog notifies the DialogFragment to close the soft keyboard before dismiss
In order to change the way of thinking, since DialogFragment cannot listen for dialog to close in advance, we need to customize dialog overriding method to tell DialogFragment to close the soft keyboard before dialog closes
class EditDialog(context: Context? , theme:Int) : Dialog(context, theme) {
override fun dismiss(a){ onDismissListener? .invoke()super.dismiss()
}
var onDismissListener: (() -> Unit)? = null
}
override fun onCreateDialog(savedInstanceState: Bundle?).: Dialog {
val dialog = EditDialog(context, R.style.live_editTextDialogStyle)
dialog.onDismissListener = { hideSoftInput() }
return dialog
}
Copy the code
Create a custom Dialog in DialogFragment onCreateDialog and set it to close the callback.
Finally, there is another way to use a full-screen dialog. Add a transparent View to the previously empty area and set the View’s click event to close the soft keyboard and popover, thus avoiding the problem of clicking on the blank area to close.