简体   繁体   English

软键盘在Android中的活动中打开和关闭监听器

[英]Soft keyboard open and close listener in an activity in Android

I have an Activity where there are 5 EditText s.我有一个Activity ,其中有 5 个EditText When the user clicks on the first EditText , the soft keyboard opens to enter some value in it.当用户单击第一个EditText时,软键盘会打开以在其中输入一些值。 I want to set some other View 's visibility to Gone when the soft keyboard opens and also when the user clicks on the first EditText and also when the soft keyboard closes from the same EditText on the back button press.我想将其他一些View的可见性设置为Gone当软键盘打开时以及当用户单击第一个EditText以及软键盘从按下后退按钮时从同一个EditText关闭时。 Then I want to set some other View 's visibility to visible.然后我想将其他一些View的可见性设置为可见。

Is there any listener or callback or any hack for when the soft keyboard opens from a click on the first EditText in Android?当通过单击 Android 中的第一个EditText打开软键盘时,是否有任何侦听器或回调或任何黑客攻击?

Piece of cake with the awesome KeyboardVisibilityEvent library令人敬畏的KeyboardVisibilityEvent 库小菜一碟

KeyboardVisibilityEvent.setEventListener(
    getActivity(),
    new KeyboardVisibilityEventListener() {
        @Override
        public void onVisibilityChanged(boolean isOpen) {
            // Ah... at last. do your thing :)
        }
    });

Credits for Yasuhiro SHIMIZU Yasuhiro SHIMIZU 学分

This only works when android:windowSoftInputMode of your activity is set to adjustResize in the manifest.这仅在您的活动的android:windowSoftInputMode在清单中设置为adjustResize时有效。 You can use a layout listener to see if the root layout of your activity is resized by the keyboard.您可以使用布局侦听器来查看您的活动的根布局是否通过键盘调整大小。

I use something like the following base class for my activities:我为我的活动使用类似以下基类的东西:

public class BaseActivity extends Activity {
    private ViewTreeObserver.OnGlobalLayoutListener keyboardLayoutListener = new ViewTreeObserver.OnGlobalLayoutListener() {
        @Override
        public void onGlobalLayout() {
            int heightDiff = rootLayout.getRootView().getHeight() - rootLayout.getHeight();
            int contentViewTop = getWindow().findViewById(Window.ID_ANDROID_CONTENT).getTop();

            LocalBroadcastManager broadcastManager = LocalBroadcastManager.getInstance(BaseActivity.this);

            if(heightDiff <= contentViewTop){
                onHideKeyboard();

                Intent intent = new Intent("KeyboardWillHide");
                broadcastManager.sendBroadcast(intent);
            } else {
                int keyboardHeight = heightDiff - contentViewTop;
                onShowKeyboard(keyboardHeight);

                Intent intent = new Intent("KeyboardWillShow");
                intent.putExtra("KeyboardHeight", keyboardHeight);
                broadcastManager.sendBroadcast(intent);
            }
        }
    };

    private boolean keyboardListenersAttached = false;
    private ViewGroup rootLayout;

    protected void onShowKeyboard(int keyboardHeight) {}
    protected void onHideKeyboard() {}

    protected void attachKeyboardListeners() {
        if (keyboardListenersAttached) {
            return;
        }

        rootLayout = (ViewGroup) findViewById(R.id.rootLayout);
        rootLayout.getViewTreeObserver().addOnGlobalLayoutListener(keyboardLayoutListener);

        keyboardListenersAttached = true;
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();

        if (keyboardListenersAttached) {
            rootLayout.getViewTreeObserver().removeGlobalOnLayoutListener(keyboardLayoutListener);
        }
    }
}

The following example activity uses this to hide a view when the keyboard is shown and show it again when the keyboard is hidden.以下示例活动使用它在显示键盘时隐藏视图,并在隐藏键盘时再次显示它。

The xml layout: xml布局:

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:id="@+id/rootLayout"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              android:orientation="vertical">              

    <ScrollView
        android:id="@+id/scrollView"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        >

        <!-- omitted for brevity -->

    </ScrollView>

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

        <!-- omitted for brevity -->

    </LinearLayout>

</LinearLayout>

And the activity:和活动:

public class TestActivity extends BaseActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.test_activity);

        attachKeyboardListeners();
    }

    @Override
    protected void onShowKeyboard(int keyboardHeight) {
        // do things when keyboard is shown
        bottomContainer.setVisibility(View.GONE);
    }

    @Override
    protected void onHideKeyboard() {
        // do things when keyboard is hidden
        bottomContainer.setVisibility(View.VISIBLE);
    }        
}

As Vikram pointed out in the comments, detecting whether the softkeyboard is shown or has disappeared is only possible with some ugly hacks.正如 Vikram 在评论中指出的那样,只有通过一些丑陋的 hack 才能检测软键盘是显示还是消失。

Maybe it is enough to set a focus listener on the edittext :也许在 edittext 上设置一个焦点监听器就足够了:

yourEditText.setOnFocusChangeListener(new OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
        if (hasFocus) {
            //got focus
        } else {
            //lost focus
        }
   }
});

For Activity:对于活动:

    final View activityRootView = findViewById(R.id.activityRoot);
activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                Rect r = new Rect();

                activityRootView.getWindowVisibleDisplayFrame(r);

                int heightDiff = view.getRootView().getHeight() - (r.bottom - r.top);
                if (heightDiff > 100) { 
                 //enter your code here
                }else{
                 //enter code for hid
                }
            }
        });

For Fragment:对于片段:

    view = inflater.inflate(R.layout.live_chat_fragment, null);
view.getViewTreeObserver().addOnGlobalLayoutListener(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.
                view.getWindowVisibleDisplayFrame(r);

                int heightDiff = view.getRootView().getHeight() - (r.bottom - r.top);
                if (heightDiff > 500) { // if more than 100 pixels, its probably a keyboard...

                }
            }
        });

Jaap's answer won't work for AppCompatActivity. Jaap 的回答不适用于 AppCompatActivity。 Instead get the height of the Status Bar and Navigation bar etc and compare to your app's window size.而是获取状态栏和导航栏等的高度,并与您的应用程序的窗口大小进行比较。

Like so:像这样:

    private ViewTreeObserver.OnGlobalLayoutListener keyboardLayoutListener = new ViewTreeObserver.OnGlobalLayoutListener() {
    @Override
    public void onGlobalLayout() {
        // navigation bar height
        int navigationBarHeight = 0;
        int resourceId = getResources().getIdentifier("navigation_bar_height", "dimen", "android");
        if (resourceId > 0) {
            navigationBarHeight = getResources().getDimensionPixelSize(resourceId);
        }

        // status bar height
        int statusBarHeight = 0;
        resourceId = getResources().getIdentifier("status_bar_height", "dimen", "android");
        if (resourceId > 0) {
            statusBarHeight = getResources().getDimensionPixelSize(resourceId);
        }

        // display window size for the app layout
        Rect rect = new Rect();
        getWindow().getDecorView().getWindowVisibleDisplayFrame(rect);

        // screen height - (user app height + status + nav) ..... if non-zero, then there is a soft keyboard
        int keyboardHeight = rootLayout.getRootView().getHeight() - (statusBarHeight + navigationBarHeight + rect.height());

        if (keyboardHeight <= 0) {
            onHideKeyboard();
        } else {
            onShowKeyboard(keyboardHeight);
        }
    }
};

You can try it:你可以试试:

private void initKeyBoardListener() {
    // Минимальное значение клавиатуры. 
    // Threshold for minimal keyboard height.
    final int MIN_KEYBOARD_HEIGHT_PX = 150;
    // Окно верхнего уровня view. 
    // Top-level window decor view.
    final View decorView = getWindow().getDecorView();
    // Регистрируем глобальный слушатель. Register global layout listener.
    decorView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
        // Видимый прямоугольник внутри окна. 
        // Retrieve visible rectangle inside window.
        private final Rect windowVisibleDisplayFrame = new Rect();
        private int lastVisibleDecorViewHeight;

        @Override
        public void onGlobalLayout() {
            decorView.getWindowVisibleDisplayFrame(windowVisibleDisplayFrame);
            final int visibleDecorViewHeight = windowVisibleDisplayFrame.height();

            if (lastVisibleDecorViewHeight != 0) {
                if (lastVisibleDecorViewHeight > visibleDecorViewHeight + MIN_KEYBOARD_HEIGHT_PX) {
                    Log.d("Pasha", "SHOW");
                } else if (lastVisibleDecorViewHeight + MIN_KEYBOARD_HEIGHT_PX < visibleDecorViewHeight) {
                    Log.d("Pasha", "HIDE");
                }
            }
            // Сохраняем текущую высоту view до следующего вызова.
            // Save current decor view height for the next call.
            lastVisibleDecorViewHeight = visibleDecorViewHeight;
        }
    });
}

I am late but I just found a very convenient dependency out there.我迟到了,但我刚刚发现了一个非常方便的依赖项。 Using it you can check the visibility of the keyboard as well as make the keyboard "Hide" and Show Whenever you want with a single Line of Code.使用它,您可以检查键盘的可见性以及使键盘“隐藏”并在任何时候使用一行代码显示。

implementation 'net.yslibrary.keyboardvisibilityevent:keyboardvisibilityevent:3.0.0-RC2'

And then you simply use this code segment to check the keyboard visibility.然后您只需使用此代码段来检查键盘的可见性。

KeyboardVisibilityEvent.setEventListener(this, new KeyboardVisibilityEventListener() {
        
@Override
  public void onVisibilityChanged(boolean isOpen) {

if (isOpen) 
  Toast.makeText(MainActivity.this, "keyboard opened",Toast.LENGTH_SHORT).show();
else 
  Toast.makeText(MainActivity.this, "keyboard hidden", Toast.LENGTH_SHORT).show();
}
});

Then if you want to Hide/Show keyboard at any point of time then you can just write one of these single lines to achieve it.然后,如果您想在任何时候隐藏/显示键盘,那么您只需编写这些单行之一即可实现它。

        UIUtil.showKeyboard(this,edittext_to_be_focused);
        UIUtil.hideKeyboard(this);

The below code is working for me,下面的代码对我有用,

mainLayout.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
        @Override
        public void onGlobalLayout() {
            if (mainLayout != null) {
                int heightDiff = mainLayout.getRootView().getHeight() - mainLayout.getHeight();
                if (heightDiff > dpToPx(getActivity(), 200)) { 
                   //keyboard is open
                } else {
                   //keyboard is hide
                }
            }
        }
    });

You can use my Rx extension function (Kotlin).您可以使用我的 Rx 扩展功能 (Kotlin)。

/**
 * @return [Observable] to subscribe of keyboard visibility changes.
 */
fun AppCompatActivity.keyboardVisibilityChanges(): Observable<Boolean> {

    // flag indicates whether keyboard is open
    var isKeyboardOpen = false

    val notifier: BehaviorSubject<Boolean> = BehaviorSubject.create()

    // approximate keyboard height
    val approximateKeyboardHeight = dip(100)

    // device screen height
    val screenHeight: Int = getScreenHeight()

    val visibleDisplayFrame = Rect()

    val viewTreeObserver = window.decorView.viewTreeObserver

    val onDrawListener = ViewTreeObserver.OnDrawListener {

        window.decorView.getWindowVisibleDisplayFrame(visibleDisplayFrame)

        val keyboardHeight = screenHeight - (visibleDisplayFrame.bottom - visibleDisplayFrame.top)

        val keyboardOpen = keyboardHeight >= approximateKeyboardHeight

        val hasChanged = isKeyboardOpen xor keyboardOpen

        if (hasChanged) {
            isKeyboardOpen = keyboardOpen
            notifier.onNext(keyboardOpen)
        }
    }

    val lifeCycleObserver = object : GenericLifecycleObserver {
        override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event?) {
            if (source.lifecycle.currentState == Lifecycle.State.DESTROYED) {
                viewTreeObserver.removeOnDrawListener(onDrawListener)
                source.lifecycle.removeObserver(this)
                notifier.onComplete()
            }
        }
    }

    viewTreeObserver.addOnDrawListener(onDrawListener)
    lifecycle.addObserver(lifeCycleObserver)

    return notifier
            .doOnDispose {
                viewTreeObserver.removeOnDrawListener(onDrawListener)
                lifecycle.removeObserver(lifeCycleObserver)
            }
            .onTerminateDetach()
            .hide()
}

Example:例子:

(context as AppCompatActivity)
                    .keyboardVisibilityChanges()
                    .subscribeBy { isKeyboardOpen ->
                        // your logic
                    }

For use in Kotlin inside fragment, which is a common use case it is very easy with KeyboardVisibilityEvent library.对于在 Kotlin 内部片段中使用,这是一个常见的用例,使用KeyboardVisibilityEvent库非常容易。

In build.gradle:在 build.gradle 中:

implementation 'net.yslibrary.keyboardvisibilityevent:keyboardvisibilityevent:3.0.0-RC2'

In Fragment:在片段中:

activity?.let {
    KeyboardVisibilityEvent.setEventListener(it,object: KeyboardVisibilityEventListener {
        override fun onVisibilityChanged(isOpen: Boolean) {
            if (isOpen) Toast.makeText(context,"Keyboard is opened",Toast.LENGTH_SHORT).show()
            else Toast.makeText(context,"Keyboard is closed",Toast.LENGTH_SHORT).show()
        }
    })
}

Source and credits来源和学分

in kotlin you can use this code in your activity在 kotlin 中,您可以在活动中使用此代码

window.decorView.viewTreeObserver.addOnGlobalLayoutListener{
    val r = Rect()
    window.decorView.getWindowVisibleDisplayFrame(r)
 
        val height =window.decorView.height
        if(height - r.bottom>height*0.1399){
           //keyboard is open
      }else{
            //keyboard is close
      }

If you can, try to extend EditText and override 'onKeyPreIme' method.如果可以,请尝试扩展 EditText 并覆盖 'onKeyPreIme' 方法。

@Override
public void setOnEditorActionListener(final OnEditorActionListener listener) {
    mEditorListener = listener; //keep it for later usage
    super.setOnEditorActionListener(listener);
}

@Override
public boolean onKeyPreIme(final int keyCode, final KeyEvent event) {
    if (event.getKeyCode() == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_UP) {
        if (mEditorListener != null) {
            //you can define and use custom listener,
            //OR define custom R.id.<imeId>
            //OR check event.keyCode in listener impl
            //* I used editor action because of ButterKnife @
            mEditorListener.onEditorAction(this, android.R.id.closeButton, event);
        }
    }
    return super.onKeyPreIme(keyCode, event);
}

How can you extend it:你如何扩展它:

  1. Implement onFocus listening and declare 'onKeyboardShown'实现 onFocus 监听并声明 'onKeyboardShown'
  2. declare 'onKeyboardHidden'声明'onKeyboardHidden'

I think, that recalculating of screen height is not 100% successfully as mentioned before.我认为,屏幕高度的重新计算并非如前所述 100% 成功。 To be clear, overriding of 'onKeyPreIme' is not called on 'hide soft keyboard programatically' methods, BUT if you are doing it anywhere, you should do 'onKeyboardHidden' logic there and do not create a comprehensive solutions.需要明确的是,“以编程方式隐藏软键盘”方法不会调用“onKeyPreIme”的覆盖,但是如果您在任何地方执行此操作,则应该在那里执行“onKeyboardHidden”逻辑并且不要创建全面的解决方案。

This will work without any need to change your activity's android:windowSoftInputMode这无需更改活动的android:windowSoftInputMode

step 1: extend EditText class and override these two:第 1 步:扩展 EditText 类并覆盖这两个:

@Override
public void setOnEditorActionListener(final OnEditorActionListener listener) {
    mEditorListener = listener;
    super.setOnEditorActionListener(listener);
}

@Override
public boolean onKeyPreIme(final int keyCode, final KeyEvent event) {
    if (event.getKeyCode() == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_UP) {
        if (mEditorListener != null) {
            mEditorListener.onEditorAction(this, android.R.id.closeButton, event);
        }
    }
    return super.onKeyPreIme(keyCode, event);
}

step 2: create these two in your activity:第 2 步:在您的活动中创建这两个:

private void initKeyboard() {
    final AppEditText editText = findViewById(R.id.some_id);
    editText.setOnFocusChangeListener(new OnFocusChangeListener() {
        @Override
        public void onFocusChange(View v, boolean hasFocus) {
            setKeyboard(hasFocus);
        }
    });
    editText.setOnEditorActionListener(new TextView.OnEditorActionListener() {
        @Override
        public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
            if (event == null || event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
                editText.clearFocus();
            }
            return false;
        }
    });
}

public void setKeyboard(boolean isShowing) {
    // do something
}

*** remember in order to make clearFocus work, you have to make parent or first child in the parent hierarchy focusable. *** 请记住,为了使clearFocus工作,您必须使父层次结构中的父级或第一个子级具有焦点。

    setFocusableInTouchMode(true);
    setFocusable(true);

Check my Kotlin extension View.keyboardVisibilityChanges() :检查我的 Kotlin 扩展View.keyboardVisibilityChanges()

fun View.keyboardVisibilityChanges(): Flow<Boolean>{
    return onPreDrawFlow()
        .map { isKeyboardVisible() }
        .distinctUntilChanged()
}
fun View.onPreDrawFlow(): Flow<Unit> {
    return callbackFlow {
        val onPreDrawListener = ViewTreeObserver.OnPreDrawListener {
            trySendBlocking(Unit)
            true
        }
        viewTreeObserver.addOnPreDrawListener(onPreDrawListener)
        awaitClose {
            viewTreeObserver.removeOnPreDrawListener(onPreDrawListener)
        }
    }
}
fun View.isKeyboardVisible(): Boolean = ViewCompat.getRootWindowInsets(this)
    ?.isVisible(Type.ime())
    ?: false
public class MainActivity extends BaseActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.mainactivity);
    attachKeyboardListeners();
    ....
    yourEditText1.setOnFocusChangeListener(new OnFocusChangeListener() {
    @Override
    public void onFocusChange(View v, boolean hasFocus) {
            if (hasFocus) {
                yourEditText2.setVisibility(View.GONE);
                yourEditText3.setVisibility(View.GONE);
                yourEditText4.setVisibility(View.GONE);
                yourEditText5.setVisibility(View.GONE);
            } else {
                yourEditText2.setVisibility(View.VISIBLE);
                yourEditText3.setVisibility(View.VISIBLE);
                yourEditText4.setVisibility(View.VISIBLE);
                yourEditText5.setVisibility(VISIBLE);
            }
       }
    });
    }
}

Use this class,使用这个类,

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;

import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;

public class SoftKeyboard implements View.OnFocusChangeListener
{
private static final int CLEAR_FOCUS = 0;

private ViewGroup layout;
private int layoutBottom;
private InputMethodManager im;
private int[] coords;
private boolean isKeyboardShow;
private SoftKeyboardChangesThread softKeyboardThread;
private List<EditText> editTextList;

private View tempView; // reference to a focused EditText

public SoftKeyboard(ViewGroup layout, InputMethodManager im)
{
    this.layout = layout;
    keyboardHideByDefault();
    initEditTexts(layout);
    this.im = im;
    this.coords = new int[2];
    this.isKeyboardShow = false;
    this.softKeyboardThread = new SoftKeyboardChangesThread();
    this.softKeyboardThread.start();
}

public void openSoftKeyboard()
{
    if(!isKeyboardShow)
    {
        layoutBottom = getLayoutCoordinates();
        im.toggleSoftInput(0, InputMethodManager.SHOW_IMPLICIT);
        softKeyboardThread.keyboardOpened();
        isKeyboardShow = true;
    }
}

public void closeSoftKeyboard()
{
    if(isKeyboardShow)
    {
        im.toggleSoftInput(InputMethodManager.HIDE_IMPLICIT_ONLY, 0);
        isKeyboardShow = false;
    }
}

public void setSoftKeyboardCallback(SoftKeyboardChanged mCallback)
{
    softKeyboardThread.setCallback(mCallback);
}

public void unRegisterSoftKeyboardCallback()
{
    softKeyboardThread.stopThread();
}

public interface SoftKeyboardChanged 
{
    public void onSoftKeyboardHide();
    public void onSoftKeyboardShow();   
}

private int getLayoutCoordinates()
{
    layout.getLocationOnScreen(coords);
    return coords[1] + layout.getHeight();
}

private void keyboardHideByDefault()
{
    layout.setFocusable(true);
    layout.setFocusableInTouchMode(true);
}

/*
 * InitEditTexts now handles EditTexts in nested views
 * Thanks to Francesco Verheye (verheye.francesco@gmail.com)
 */
private void initEditTexts(ViewGroup viewgroup) 
{
    if(editTextList == null)
        editTextList = new ArrayList<EditText>();

    int childCount = viewgroup.getChildCount();
    for(int i=0; i<= childCount-1;i++) 
    {
        View v = viewgroup.getChildAt(i);

        if(v instanceof ViewGroup) 
        {
            initEditTexts((ViewGroup) v);
        }

        if(v instanceof EditText) 
        {
            EditText editText = (EditText) v;
            editText.setOnFocusChangeListener(this);
            editText.setCursorVisible(true);
            editTextList.add(editText);
        }
    }
}

/*
 * OnFocusChange does update tempView correctly now when keyboard is still shown
 * Thanks to Israel Dominguez (dominguez.israel@gmail.com)
 */
@Override
public void onFocusChange(View v, boolean hasFocus) 
{
    if(hasFocus) 
    {
        tempView = v;
        if(!isKeyboardShow) 
        {
            layoutBottom = getLayoutCoordinates();
            softKeyboardThread.keyboardOpened();
            isKeyboardShow = true;
        }
    }
}

// This handler will clear focus of selected EditText
private final Handler mHandler = new Handler()
{
    @Override
    public void handleMessage(Message m)
    {
        switch(m.what)
        {
        case CLEAR_FOCUS:
            if(tempView != null)
            {
                tempView.clearFocus();
                tempView = null;
            }
            break;
        }
    }
};

private class SoftKeyboardChangesThread extends Thread
{
    private AtomicBoolean started;
    private SoftKeyboardChanged mCallback;

    public SoftKeyboardChangesThread()
    {
        started = new AtomicBoolean(true);
    }

    public void setCallback(SoftKeyboardChanged mCallback)
    {
        this.mCallback = mCallback;
    }

    @Override
    public void run()
    {
        while(started.get())
        {
            // Wait until keyboard is requested to open
            synchronized(this)
            {
                try 
                {
                    wait();
                } catch (InterruptedException e) 
                {
                    e.printStackTrace();
                }
            }

            int currentBottomLocation = getLayoutCoordinates();

            // There is some lag between open soft-keyboard function and when it really appears.
            while(currentBottomLocation == layoutBottom && started.get())
            {
                currentBottomLocation = getLayoutCoordinates();
            }

            if(started.get())
                mCallback.onSoftKeyboardShow();

            // When keyboard is opened from EditText, initial bottom location is greater than layoutBottom
            // and at some moment equals layoutBottom.
            // That broke the previous logic, so I added this new loop to handle this.
            while(currentBottomLocation >= layoutBottom && started.get())
            {
                currentBottomLocation = getLayoutCoordinates();
            }

            // Now Keyboard is shown, keep checking layout dimensions until keyboard is gone
            while(currentBottomLocation != layoutBottom && started.get())
            {
                                    synchronized(this)
                {
                    try 
                    {
                        wait(500);
                    } catch (InterruptedException e) 
                    {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
                currentBottomLocation = getLayoutCoordinates();
            }

            if(started.get())
                mCallback.onSoftKeyboardHide();

            // if keyboard has been opened clicking and EditText.
            if(isKeyboardShow && started.get())
                isKeyboardShow = false;

            // if an EditText is focused, remove its focus (on UI thread)
            if(started.get())
                mHandler.obtainMessage(CLEAR_FOCUS).sendToTarget();
        }   
    }

    public void keyboardOpened()
    {
        synchronized(this)
        {
            notify();
        }
    }

    public void stopThread()
    {
        synchronized(this)
        {
            started.set(false);
            notify();
        }
    }

}
}

In Android Manifest , android:windowSoftInputMode="adjustResize" is necessary.Android Manifest中,需要android:windowSoftInputMode="adjustResize"

/*
Somewhere else in your code
*/
RelativeLayout mainLayout = findViewById(R.layout.main_layout); // You must use the layout root
InputMethodManager im = (InputMethodManager)getSystemService(Service.INPUT_METHOD_SERVICE);

/*
Instantiate and pass a callback
*/
SoftKeyboard softKeyboard;
softKeyboard = new SoftKeyboard(mainLayout, im);
softKeyboard.setSoftKeyboardCallback(new SoftKeyboard.SoftKeyboardChanged() {

@Override
public void onSoftKeyboardHide()  {
    // Code here
}

@Override
public void onSoftKeyboardShow() {
    // Code here
}   
});

/*
Open or close the soft keyboard easily
*/
softKeyboard.openSoftKeyboard();
softKeyboard.closeSoftKeyboard();

/* Prevent memory leaks:*/
@Override
public void onDestroy() {
    super.onDestroy();
    softKeyboard.unRegisterSoftKeyboardCallback();
}

PS - Completely taken from here . PS-完全取自这里

For the case of adjustResize and FragmentActivity accepted solution from @Jaap doesn't work for me.对于来自@Jaap 的adjustResize和FragmentActivity 接受的解决方案对我不起作用。

Here is my solution:这是我的解决方案:

private ViewTreeObserver.OnGlobalLayoutListener keyboardLayoutListener = new ViewTreeObserver.OnGlobalLayoutListener() {
    private int contentDiff;
    private int rootHeight;
    @Override
    public void onGlobalLayout() {
        View contentView = getWindow().findViewById(Window.ID_ANDROID_CONTENT);
        if (rootHeight != mDrawerLayout.getRootView().getHeight()) {
            rootHeight = mDrawerLayout.getRootView().getHeight();
            contentDiff = rootHeight - contentView.getHeight();
            return;
        }
        int newContentDiff = rootHeight - contentView.getHeight();
        if (contentDiff != newContentDiff) {
            if (contentDiff < newContentDiff) {
                onShowKeyboard(newContentDiff - contentDiff);
            } else {
                onHideKeyboard();
            }
            contentDiff = newContentDiff;
        }
    }
};

Different implementation of a soft-keyboard-listener that DOES NOT RELY ON WINDOW-RESIZING & therefore also works nicely in the multi-window world. 不依赖于窗口缩放的软键盘侦听器的不同实现,因此在多窗口环境中也可以很好地工作。 it has its own quirks of course, but imo they are nothing compared to the complete brokenness that is multi-window&window-resize-detection together. 它当然有它自己的怪癖,但与将多窗口和窗口调整大小的检测结合在一起的完全破坏相比,它们什么都不是。

https://github.com/sqrt1764/AndroidSoftKeyboardListener https://github.com/sqrt1764/AndroidSoftKeyboardListener

Would appreciate ideas for improvements! 希望有改进的想法!

A different approach would be to check when the user stopped typing...另一种方法是检查用户何时停止输入......

When a TextEdit is in focus (user is/was typing) you could hide the views (focus listener)当 TextEdit 处于焦点时(用户正在/正在键入),您可以隐藏视图(焦点侦听器)

and use a Handler + Runnable and a text change listener to close the keyboard (regardless of its visibility) and show the views after some delay.并使用 Handler + Runnable 和文本更改侦听器关闭键盘(无论其可见性如何)并在延迟后显示视图。

The main thing to look out for would be the delay you use, which would depend on the content of these TextEdits.要注意的主要是您使用的延迟,这取决于这些 TextEdits 的内容。

Handler timeoutHandler = new Handler();
Runnable typingRunnable = new Runnable() {
    public void run() {
        // current TextEdit
        View view = getCurrentFocus();

        InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
        // reset focus
        view.clearFocus();
        // close keyboard (whether its open or not)
        imm.hideSoftInputFromWindow(view.getWindowToken(), InputMethodManager.RESULT_UNCHANGED_SHOWN);

        // SET VIEWS VISIBLE
    }
};

editText.setOnFocusChangeListener(new View.OnFocusChangeListener() {
    @Override
    public void onFocusChange(View v, boolean hasFocus) {
        if (hasFocus) {
            // SET VIEWS GONE

            // reset handler
            timeoutHandler.removeCallbacks(typingRunnable);
            timeoutHandler.postDelayed(typingRunnable, TYPING_TIMEOUT);
        }
    }
});

editText.addTextChangedListener(new TextWatcher() {
    @Override
    public void beforeTextChanged(CharSequence s, int start, int count, int after) {}

    @Override
    public void onTextChanged(CharSequence s, int start, int before, int count) {
        // Reset Handler...
        timeoutHandler.removeCallbacks(typingRunnable);
    }

    @Override
    public void afterTextChanged(Editable s) {
        // Reset Handler Cont.
        if (editText.getText().toString().trim().length() > 0) {
            timeoutHandler.postDelayed(typingRunnable, TYPING_TIMEOUT);
        }
    }
});

This code works great nice这段代码很好用

use this class for root view:将此类用于根视图:

public class KeyboardConstraintLayout extends ConstraintLayout {

private KeyboardListener keyboardListener;
private EditText targetEditText;
private int minKeyboardHeight;
private boolean isShow;

public KeyboardConstraintLayout(Context context) {
    super(context);
    minKeyboardHeight = getResources().getDimensionPixelSize(R.dimen.keyboard_min_height); //128dp
}

public KeyboardConstraintLayout(Context context, AttributeSet attrs) {
    super(context, attrs);
    minKeyboardHeight = getResources().getDimensionPixelSize(R.dimen.keyboard_min_height); // 128dp
}

public KeyboardConstraintLayout(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    minKeyboardHeight = getResources().getDimensionPixelSize(R.dimen.keyboard_min_height); // 128dp
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    if (!isInEditMode()) {
        Activity activity = (Activity) getContext();
        @SuppressLint("DrawAllocation")
        Rect rect = new Rect();
        getWindowVisibleDisplayFrame(rect);

        int statusBarHeight = rect.top;
        int keyboardHeight = activity.getWindowManager().getDefaultDisplay().getHeight() - (rect.bottom - rect.top) - statusBarHeight;

        if (keyboardListener != null && targetEditText != null && targetEditText.isFocused()) {
            if (keyboardHeight > minKeyboardHeight) {
                if (!isShow) {
                    isShow = true;
                    keyboardListener.onKeyboardVisibility(true);
                }
            }else {
                if (isShow) {
                    isShow = false;
                    keyboardListener.onKeyboardVisibility(false);
                }
            }
        }
    }
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}

public boolean isShowKeyboard() {
    return isShow;
}

public void setKeyboardListener(EditText targetEditText, KeyboardListener keyboardListener) {
    this.targetEditText = targetEditText;
    this.keyboardListener = keyboardListener;
}

public interface KeyboardListener {
    void onKeyboardVisibility (boolean isVisible);
}

} }

and set keyboard listener in activity or fragment:并在活动或片段中设置键盘监听器:

        rootLayout.setKeyboardListener(targetEditText, new KeyboardConstraintLayout.KeyboardListener() {
        @Override
        public void onKeyboardVisibility(boolean isVisible) {

        }
    });

This is not working as desired...这没有按预期工作......

... have seen many use size calculations to check ... ...已经看到许多使用尺寸计算来检查...

I wanted to determine if it was open or not and I found isAcceptingText()我想确定它是否打开, 我发现isAcceptingText()

so this really does not answer the question as it does not address opening or closing rather more like is open or closed so it is related code that may help others in various scenarios...所以这真的不能回答这个问题,因为它没有解决打开或关闭问题,而更像是打开或关闭,所以它是相关代码,可以在各种情况下帮助其他人......

in an activity在一个活动中

    if (((InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE)).isAcceptingText()) {
        Log.d(TAG,"Software Keyboard was shown");
    } else {
        Log.d(TAG,"Software Keyboard was not shown");
    }

in a fragment在一个片段中

    if (((InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE)).isAcceptingText()) {
        Log.d(TAG,"Software Keyboard was shown");
    } else {
        Log.d(TAG,"Software Keyboard was not shown");

    }

您可以通过覆盖 Activity 中的两种方法来处理键盘可见性: onKeyUp()onKeyDown()此链接中的更多信息: https ://developer.android.com/training/keyboard-input/commands

Found an accurate way of telling whether or not a keyboard when using the 'adjustResize' Soft input mode (Kotlin code)在使用“adjustResize”软输入模式(Kotlin 代码)时找到了一种准确判断键盘是否存在的方法

Define a couple of activity scope variables定义几个活动范围变量

private var activityHeight = 0
private var keyboardOpen = false

Write the following code in onCreate在 onCreate 中编写如下代码

override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        
        ...

        /* Grab initial screen value */
        this@ActivityMain.window.decorView.doOnNextLayout {
            val displayFrame : Rect = Rect()
            this@ActivityMain.window.decorView.getWindowVisibleDisplayFrame(displayFrame)
            activityHeight = displayFrame.height()
        }

        /* Check for keyboard open/close */
        this@ActivityMain.window.decorView.addOnLayoutChangeListener { v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom ->
            val drawFrame : Rect = Rect()
            this@ActivityMain.window.decorView.getWindowVisibleDisplayFrame(drawFrame)
            val currentSize = drawFrame.height()

            keyboardOpen = currentSize < activityHeight
            Log.v("keyboard1","$keyboardOpen $currentSize - $activityHeight")
        }
}

You now have a boolean which accurately tracks whether or not the keyboard is open, do what you will你现在有一个布尔值,它可以准确跟踪键盘是否打开,做你想做的事

Unfortunately I do not have a sufficiently high reputation to comment on Jaap van Hengstum's answer.不幸的是,我没有足够高的声誉来评论 Jaap van Hengstum 的回答。 But I read a few comments of people, having the problem that contentViewTop is always 0 and that onShowKeyboard(...) is always called.但是我读了一些人的评论,问题是contentViewTop总是0并且总是调用onShowKeyboard(...)

I had the same issue and figured out the problem I had.我有同样的问题,并找出了我遇到的问题。 I used an AppCompatActivity instead of a 'normal' Activity .我使用了AppCompatActivity而不是“正常”的Activity In this case Window.ID_ANDROID_CONTENT refers to an ContentFrameLayout and not to the FrameLayout with the right top-value.在这种情况下, Window.ID_ANDROID_CONTENT指的是ContentFrameLayout而不是具有正确顶部值的FrameLayout In my case it was fine to use the 'normal' Activity , if you have to use another activity-type (I just tested the AppCompatActivity , maybe it's also an issue with other acitivy-types like the FragmentActivity ), you have to access the FrameLayout , which is an ancestor of the ContentFrameLayout .就我而言,使用“正常” Activity很好,如果您必须使用另一种活动类型(我刚刚测试了AppCompatActivity ,也许这也是其他活动类型(如FragmentActivity )的问题),您必须访问FrameLayout ,它是ContentFrameLayout的祖先。

when keyboard show当键盘显示

rootLayout.getHeight() < rootLayout.getRootView().getHeight() - getStatusBarHeight() 

is true,else hide是真的,否则隐藏

private boolean isKeyboardShown = false;
private int prevContentHeight = 0;
private ViewGroup contentLayout;

private ViewTreeObserver.OnGlobalLayoutListener keyboardLayoutListener =
        new ViewTreeObserver.OnGlobalLayoutListener() {

    @Override
    public void onGlobalLayout() {
        int contentHeight = contentLayout.getHeight();
        int rootViewHeight = contentLayout.getRootView().getHeight();

        if (contentHeight > 0) {

            if (!isKeyboardShown) {
                if (contentHeight < prevContentHeight) {
                    isKeyboardShown = true;
                    onShowKeyboard(rootViewHeight - contentHeight);
                }
            } else {
                if (contentHeight > prevContentHeight) {
                    isKeyboardShown = false;
                    onHideKeyboard();
                }
            }

            prevContentHeight = contentHeight;
        }
    }
};

I've modified the Jaap's accepted answer a bit.我稍微修改了 Jaap 接受的答案。 But in my case, there are few assumptions such as android:windowSoftInputMode=adjustResize and the keyboard does not show up at the beginning when the app starts.但在我的情况下,很少有假设,例如android:windowSoftInputMode=adjustResize并且键盘在应用程序启动时没有出现在开头。 And also, I assume that the screen in regard matches the parent's height.而且,我假设屏幕与父母的身高相匹配。

contentHeight > 0 this check provides me to know if the regarding screen is hidden or shown to apply keyboard event listening for this specific screen. contentHeight > 0此检查让我知道相关屏幕是否隐藏或显示以应用键盘事件侦听此特定屏幕。 Also I pass the layout view of the regarding screen in attachKeyboardListeners(<your layout view here>) in my main activity's onCreate() method.此外,我在主要活动的onCreate()方法中的attachKeyboardListeners(<your layout view here>)中传递了相关屏幕的布局视图。 Every time when the height of the regarding screen changes, I save it to prevContentHeight variable to check later whether the keyboard is shown or hidden.每次相关屏幕的高度发生变化时,我都会将其保存到prevContentHeight变量中,以便稍后检查键盘是显示还是隐藏。

For me, so far it's been worked pretty well.对我来说,到目前为止,它工作得很好。 I hope that it works for others too.我希望它也适用于其他人。

"Jaap van Hengstum"'s answer is working for me, but there is no need to set "android:windowSoftInputMode" as he just said! “Jaap van Hengstum”的答案对我有用,但没有必要像他刚才所说的那样设置“android:windowSoftInputMode”!

I've made it smaller(it now just detects what I want, actually an event on showing and hiding of keyboard):我把它变小了(它现在只检测我想要的,实际上是显示和隐藏键盘的事件):

private ViewTreeObserver.OnGlobalLayoutListener keyboardLayoutListener = new ViewTreeObserver.OnGlobalLayoutListener() {
    @Override
    public void onGlobalLayout() {
        int heightDiff = rootLayout.getRootView().getHeight() - rootLayout.getHeight();
        int contentViewTop = getWindow().findViewById(Window.ID_ANDROID_CONTENT).getTop();
        if(heightDiff <= contentViewTop){
            onHideKeyboard();
        } else {
            onShowKeyboard();
        }
    }
};

private boolean keyboardListenersAttached = false;
private ViewGroup rootLayout;

protected void onShowKeyboard() {}
protected void onHideKeyboard() {}

protected void attachKeyboardListeners() {
    if (keyboardListenersAttached) {
        return;
    }

    rootLayout = (ViewGroup) findViewById(R.id.CommentsActivity);
    rootLayout.getViewTreeObserver().addOnGlobalLayoutListener(keyboardLayoutListener);

    keyboardListenersAttached = true;
}

@Override
protected void onDestroy() {
    super.onDestroy();

    if (keyboardListenersAttached) {
        rootLayout.getViewTreeObserver().removeGlobalOnLayoutListener(keyboardLayoutListener);
    }
}

and just don't forget to add this只是不要忘记添加这个

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_comments);
    attachKeyboardListeners();}

check with the below code :检查以下代码:

XML CODE : XML 代码:

<android.support.constraint.ConstraintLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/coordinatorParent"
    style="@style/parentLayoutPaddingStyle"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

  .................


</android.support.constraint.ConstraintLayout>

JAVA CODE : JAVA代码:

//Global Variable
android.support.constraint.ConstraintLayout activityRootView;
boolean isKeyboardShowing = false;
private  ViewTreeObserver.OnGlobalLayoutListener onGlobalLayoutListener;
android.support.constraint.ConstraintLayout.LayoutParams layoutParams;




 //onCreate or onViewAttached
    activityRootView = view.findViewById(R.id.coordinatorParent);
        onGlobalLayoutListener = onGlobalLayoutListener();
        activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(onGlobalLayoutListener);


  //outside oncreate
  ViewTreeObserver.OnGlobalLayoutListener onGlobalLayoutListener() {
        return new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                Rect r = new Rect();
                activityRootView.getWindowVisibleDisplayFrame(r);
                int screenHeight = activityRootView.getRootView().getHeight();
                int keypadHeight = screenHeight - r.bottom;

                if (keypadHeight > screenHeight * 0.15) { // 0.15 ratio is perhaps enough to determine keypad height.
                    if (!isKeyboardShowing) {  // keyboard is opened
                        isKeyboardShowing = true;
                        onKeyboardVisibilityChanged(true);
                   }
                }
                else {
                    if (isKeyboardShowing) {   // keyboard is closed
                        isKeyboardShowing = false;
                        onKeyboardVisibilityChanged(false);
                    }
                }
            }//ends here
        };

    }


    void onKeyboardVisibilityChanged(boolean value) {
        layoutParams = (android.support.constraint.ConstraintLayout.LayoutParams)topImg.getLayoutParams();

        if(value){
           int length = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 90, getResources().getDisplayMetrics());
            layoutParams.height= length;
            layoutParams.width = length;
            topImg.setLayoutParams(layoutParams);
            Log.i("keyboard " ,""+ value);
        }else{
            int length1 = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 175, getResources().getDisplayMetrics());
            layoutParams.height= length1;
            layoutParams.width = length1;
            topImg.setLayoutParams(layoutParams);
            Log.i("keyboard " ,""+ value);
        }
    }


    @Override
    public void onDetach() {
        super.onDetach();
        if(onGlobalLayoutListener != null) {
            activityRootView.getViewTreeObserver().removeOnGlobalLayoutListener(onGlobalLayoutListener);
        }
    }

if any one is still searching then please go through this Lib it is a great lib for detecting soft keyboard open/close listener 如果仍然有人在搜索,请浏览此库,它是检测软键盘打开/关闭监听器的绝佳库

https://github.com/yshrsmz/KeyboardVisibilityEvent?utm_source=android-arsenal.com&utm_medium=referral&utm_campaign=2519 https://github.com/yshrsmz/KeyboardVisibilityEvent?utm_source=android-arsenal.com&utm_medium=referral&utm_campaign=2519

There is a keyboard dismissed listener.有一个键盘被解雇的听众。
Class SearchEditText is derived from android.widget.EditText class. SearchEditText类派生自android.widget.EditText类。 There is an interface SearchEditText.OnKeyboardDismissListener in this class.这个类中有一个接口SearchEditText.OnKeyboardDismissListener You can look docmentation:您可以查看文档:
https://developer.android.com/reference/androidx/leanback/widget/SearchEditText https://developer.android.com/reference/androidx/leanback/widget/SearchEditText

Note.笔记。 Before use SearchEditText you need to set up Gradle dependencies in the build.gradle (:app):在使用 SearchEditText 之前,您需要在 build.gradle (:app) 中设置 Gradle 依赖项:

implementation 'androidx.leanback:leanback:1.1.0-alpha05'

Maybe someone will come in handy.也许有人会派上用场。

Detailed response:详细回复:

import androidx.appcompat.app.AppCompatActivity;
import androidx.leanback.widget.SearchEditText;

import android.os.Bundle;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity
        implements SearchEditText.OnKeyboardDismissListener {

    SearchEditText searchEditText;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        searchEditText = findViewById(R.id.search_edit_text);

        searchEditText.setOnKeyboardDismissListener(this);
    }

    /**
     * Method invoked when the keyboard is dismissed.
     */
    @Override
    public void onKeyboardDismiss() {
        Toast.makeText(this, "The listener worked", Toast.LENGTH_LONG).show();
    }
}

activity_main.xml activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <androidx.leanback.widget.SearchEditText
        android:id="@+id/search_edit_text"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="12dp"
        android:textSize="20sp"
        android:focusableInTouchMode="true"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

Note: listener works with:注意:监听器适用于:

android:windowSoftInputMode="adjustPan"
android:windowSoftInputMode="adjustResize"

I built the listener using this answer and my task LoopingTask , thanks by the way.我使用这个答案和我的任务LoopingTask构建了监听器,顺便感谢一下。

Very easy to implement using SoftKeyboardListener from Viper Pack android library.使用Viper Pack android 库中的SoftKeyboardListener很容易实现。

Just install the library then add this to your code :只需安装库,然后将其添加到您的代码中:

Lava.app.addSoftKeyboardListener(context, new Lava.SoftKeyboardListener() {
@Override
public void onSoftKeyboardShow(EditText focusedview) {
// when shows
}
@Override
public void onSoftKeyboardHide(EditText focusedview) {
// when hides
}
});
Dislaimer : adding a lot of listeners may slow/crash your app, you still can use Lava.app.removeSoftKeyboardListeners() to remove all the previous listeners. 免责声明:添加大量侦听器可能会减慢/崩溃您的应用程序,您仍然可以使用Lava.app.removeSoftKeyboardListeners()删除所有以前的侦听器。 Dislaimer 2 : creating a SoftKeyboardListener directly in activity onCreate will add a new one every time the activity resume itself, so make sure to limit that using a boolean : 免责声明 2:直接在活动onCreate中创建 SoftKeyboardListener 将在每次活动恢复时添加一个新的,因此请确保使用 boolean 限制它:
...
@Override
public void onCreate(Bundle sis) {
super.onCreate(sis);
...
Lava.app.removeSoftKeyboardListeners();
// create your listener here
...
}
...

Or just remove all the previous ones :或者只是删除所有以前的:

 ... @Override public void onCreate(Bundle sis) { super.onCreate(sis); ... Lava.app.removeSoftKeyboardListeners(); // create your listener here ... } ...

Lava.app.removeSoftKeyboardListeners() removes all previous SoftKeyboardListeners. Lava.app.removeSoftKeyboardListeners()删除所有以前的 SoftKeyboardListeners。

The newly released Window Insets and Keyboard Animations can we used for this scenario.我们可以在这个场景中使用新发布的窗口插图和键盘动画。 For more insights watch this video有关更多见解,请观看视频

ViewCompat.setOnApplyWindowInsetsListener(view) { _, insets ->
                val showingKeyboard = insets.isVisible(WindowInsetsCompat.Type.ime())
                if(showingKeyboard!=keyboardVisible && !showingKeyboard){
                    //listen to changes 
                }
                keyboardVisible = showingKeyboard
                insets
            }

In my case the 'net.yslibrary.keyboardvisibilityevent:keyboardvisibilityevent:3.0.0-RC2' library is not being worked for some reasons with a scrollview.在我的情况下,'net.yslibrary.keyboardvisibilityevent:keyboardvisibilityevent:3.0.0-RC2'库由于某些原因无法使用滚动视图。 It might be because of broken calculations of view height in the scrollview.这可能是因为滚动视图中的视图高度计算错误。 Insets are the answer.插图就是答案。 The simple inset listener works like a charm.简单的插入监听器就像一个魅力。 Here is the code:这是代码:

        ViewCompat.setOnApplyWindowInsetsListener(binding.root) { _, insets ->
          val imeVisible = insets.isVisible(WindowInsetsCompat.Type.ime())
        insets
    }

at the first create a kotlin file and add these methods:首先创建一个 kotlin 文件并添加这些方法:

fun Activity.getRootView(): View {
    return findViewById<View>(android.R.id.content)
}
fun Context.convertDpToPx(dp: Float): Float {
    return TypedValue.applyDimension(
            TypedValue.COMPLEX_UNIT_DIP, 
            dp, 
            this.resources.displayMetrics
    )
}
fun Activity.isKeyboardOpen(): Boolean {
    val visibleBounds = Rect()
    this.getRootView().getWindowVisibleDisplayFrame(visibleBounds)
    val heightDiff = getRootView().height - visibleBounds.height()
    val marginOfError = Math.round(this.convertDpToPx(50F))
    return heightDiff > marginOfError
}

fun Activity.isKeyboardClosed(): Boolean {
    return !this.isKeyboardOpen()
}

then create a listener class for checking the keyboard is open or not :然后创建一个监听器类来检查键盘是否打开:

class KeyboardEventListener(
        private val activity: AppCompatActivity,
        private val callback: (isOpen: Boolean) -> Unit
) : LifecycleObserver {
     private val listener = object : ViewTreeObserver.OnGlobalLayoutListener {
     private var lastState: Boolean = activity.isKeyboardOpen()
      override fun onGlobalLayout() {
            val isOpen = activity.isKeyboardOpen()
            if (isOpen == lastState) {
                return
            } else {
                dispatchKeyboardEvent(isOpen)
                lastState = isOpen
            }
        }
    }
    init {
        // Dispatch the current state of the keyboard
        dispatchKeyboardEvent(activity.isKeyboardOpen())
        // Make the component lifecycle aware
        activity.lifecycle.addObserver(this)
        registerKeyboardListener()
    }
    private fun registerKeyboardListener() {
        activity.getRootView().viewTreeObserver.addOnGlobalLayoutListener(listener)
    }
    private fun dispatchKeyboardEvent(isOpen: Boolean) {
        when {
            isOpen  -> callback(true)
            !isOpen -> callback(false)
        }
    }
    @OnLifecycleEvent(value = Lifecycle.Event.ON_PAUSE)
    @CallSuper
    fun onLifecyclePause() {
        unregisterKeyboardListener()
    }
    private fun unregisterKeyboardListener() {
        activity.getRootView().viewTreeObserver.removeOnGlobalLayoutListener(listener)
    }
}

and use it like this :并像这样使用它:

override fun onResume() {
      super.onResume()
      KeyboardEventListener(this) { isOpen -> // handle event }
  }

I hope you find it useful.希望对你有帮助。

Solution with extra property in Activity\Fragment, but without any hypothetical hardcoded heights (like 100 etc) .在 Activity\Fragment 中具有额外属性的解决方案,但没有任何假设的硬编码高度(如 100 等)。 Just add OnGlobalLayoutListener to your root view and save its initial height before keyboard will be shown:只需将 OnGlobalLayoutListener 添加到您的根视图并在显示键盘之前保存其初始高度:

var firstLoad         = true
var contentFullWeight = 0

override fun onViewCreated(layoutView: View, savedInstanceState: Bundle?) {
    super.onViewCreated(layoutView, savedInstanceState)

    view?.viewTreeObserver?.addOnGlobalLayoutListener(ViewTreeObserver.OnGlobalLayoutListener {

        if(firstLoad){
            contentFullWeight = view?.height!!
            firstLoad = false
        }

        if (view?.height!! < contentFullWeight) {
            Log.d("TEZT_KEYBOARD", ">> KBD OPENED")
        } else {
            Log.d("TEZT_KEYBOARD", ">> KBD closed")
        }
    })

}
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:card_view="http://schemas.android.com/tools"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/addresses_confirm_root_view"
android:orientation="vertical">

<---In the xml root use the id---> <---在 xml 根目录中使用 id--->

final LinearLayout activityRootView = view.findViewById(R.id.addresses_confirm_root_view);最终 LinearLayout activityRootView = view.findViewById(R.id.addresses_confirm_root_view); activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(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. activityRootView.getWindowVisibleDisplayFrame(r); activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { Rect r = new Rect(); //r 将填充您的视图的坐标,该区域仍然可见。activityRootView。 getWindowVisibleDisplayFrame(r);

            int heightDiff = activityRootView.getRootView().getHeight() - r.height();
            if (heightDiff > 0.25 * activityRootView.getRootView().getHeight()) {
                // if more than 25% of the screen, its probably a keyboard...
                onkeyboard();

            } else {
                //Keyboard not visible
                offkeyboard();
            }
        }
    });

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM