ChatGPT解决这个技术问题 Extra ChatGPT

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

我有一个 Activity,其中有 5 个 EditText。当用户点击第一个 EditText 时,软键盘打开以在其中输入一些值。当软键盘打开时以及当用户单击第一个 EditText 以及软键盘从后退按钮上的同一个 EditText 关闭时,我想将其他一些 View 的可见性设置为 Gone按。然后我想将其他一些 View 的可见性设置为可见。

当通过单击 Android 中的第一个 EditText 打开软键盘时,是否有任何侦听器或回调或任何黑客攻击?

不,没有这样的听众。 hacks来实现你想要的。这是一种可能的方法:How to send out pointer event in Android
@Vikram 我不是在寻找 trying to detect the virtual keyboard height in Android.
我知道。如果您浏览代码,您将看到高度是如何确定的。正在发送指针事件->两种情况=> 1.如果键盘打开=> &如果指针的 XY 位置落在键盘上/上方 => SecurityException =>递减 Y 并重试 =>直到没有抛出异常=>当前 Y 值是键盘高度。 2.如果键盘没有打开=>没有SecurityException
它如何应用于您的场景? 在屏幕高度的 2/3 处发送一个指针事件。如果抛出 SecurityException =>键盘打开。否则,键盘已关闭。
@Vikram 我只想要第一个 EditText 而不是其他 EditText。我怎么能区分这个?

G
Gal Rom

很棒的KeyboardVisibilityEvent library小菜一碟

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

Yasuhiro SHIMIZU 学分


这不起作用,因为键盘没有静态高度,并且此库中的高度设置为 100dp。
它仍然是硬编码的。多窗口?三星拆分视图?画中画模式?还有一个最小的单行键盘,将低于 100dp。这里没有灵丹妙药……
因为这个问题没有包罗万象,这似乎是最容易实现的,只需回到你真正想要处理的代码:)
这是迄今为止最好的答案,在任何设备上都完全可靠
令人沮丧的是,这样的基本功能在 Android 中没有原生解决方案!谷歌似乎在新的 Android 版本上塞了很多不必要的废话,但没有解决这些重要的缺点......
J
Jaap van Hengstum

这仅在清单中将您的 Activity 的 android:windowSoftInputMode 设置为 adjustResize 时有效。您可以使用布局侦听器来查看您的活动的根布局是否通过键盘调整大小。

我为我的活动使用类似以下基类的东西:

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);
        }
    }
}

以下示例活动使用它在显示键盘时隐藏视图,并在隐藏键盘时再次显示它。

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>

和活动:

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);
    }        
}

+1 是的,这是解决我问题的完美解决方案。
嗨,您在 Window.ID_ANDROID_CONTENT 上使用了 getTop()。登顶对我不起作用。这里总是 0,它的工作方式就像它应该使用 getHeight() 一样。
您从哪里获得rootLayout = (ViewGroup) findViewById(R.id.rootLayout);
由于某种原因不适合我,它总是调用 onShowKeyboard 我打开它或关闭它。我正在使用 findViewById(android.R.id.content),也许这就是问题所在?
@tsig 您的 +100 解决方案取决于特定的屏幕。在平板电脑和 hdpi 手机上失败。我使用校正作为设备高度的 10%。这意味着如果视图高度低于 screenHeight - 10%,则键盘处于打开状态。否则键盘关闭。这是我在 onGlobalLayout 中的 contentViewTop: contentViewTop = (getWindow().getDecorView().getBottom() / 10)
M
Manuel Allenspach

正如 Vikram 在评论中指出的那样,只有通过一些丑陋的 hack 才能检测软键盘是显示还是消失。

也许在edittext上设置一个焦点监听器就足够了:

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

假设我点击edittext然后它setOnFocusChangeListener监听器将被调用然后我按下然后它关闭键盘&但没有点击其他视图,现在我再次点击已经有焦点的相同编辑文本,然后会发生什么?
@Williams 我不完全确定,但我怀疑不会调用 onFocusChange()
伙计们不看这个答案,因为即使我不明白,他也在说不同的东西。
无论如何,它对我不起作用......当软键盘被隐藏时,EditText 上没有发生任何焦点变化......所以我无法从这个 Listener 获得通知。
如果用户只是关闭键盘(向下箭头)焦点仍在 EditText 但键盘已关闭
M
M Singh Karnawat

对于活动:

    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
                }
            }
        });

对于片段:

    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...

                }
            }
        });

将其用于活动,但不是与视图进行比较,而是与屏幕尺寸进行比较。效果很好
最好将 heightDiff 与 dp 中的高度而不是像素进行比较。它可以有很大的不同。
这需要清单中的 android:windowSoftInputMode="adjustResize" 吗?
android:windowSoftInputMode="adjustResize" android:configChanges="orientation|keyboard|keyboardHidden"
它对我有用,但我有一个问题。是否需要大量资源?
C
Community

Jaap's 答案不适用于 AppCompatActivity。而是获取状态栏和导航栏等的高度,并与您的应用程序的窗口大小进行比较。

像这样:

    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);
        }
    }
};

似乎工作得很好,但有一个例外:在分屏模式下中断。否则很棒。
P
Pavel Dolbik

你可以试试:

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;
        }
    });
}

斯帕西博,多尔比克! :)
N
Nitin Bagdi

我迟到了,但我刚刚发现了一个非常方便的依赖项。使用它,您可以检查键盘的可见性以及使键盘“隐藏”并在任何时候使用一行代码显示。

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

然后您只需使用此代码段来检查键盘的可见性。

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();
}
});

然后,如果您想在任何时候隐藏/显示键盘,那么您只需编写这些单行之一即可实现它。

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

我现在一天收到这个错误..它以前工作过。引起:java.lang.ClassNotFoundException:在路径上找不到类“net.yslibrary.android.keyboardvisibilityevent.KeyboardVisibilityEventListener”:DexPathList [[zip文件“/data/app/~~iquO8KrlQd-nWuV82FYhTA==/com.vzw .sampleappforgyde-rMZn0uD205pWrw8PzNOQ0Q==/base.apk”],nativeLibraryDirectories=[/data/app/~~iquO8KrlQd-nWuV82FYhTA==/com.vzw.sampleappforgyde-rMZn0uD205pWrw8PzNOQ0Q==/lib/arm64, /system/lib64, /system /system_ext/lib64]]
V
Vinoj Vetha

下面的代码对我有用,

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
                }
            }
        }
    });

什么是主布局?当文本提交视图顶部时,heightDiff 始终为 0。
V
Vlad

您可以使用我的 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()
}

例子:

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

对我不起作用。找不到方法 dip()getScreenHeight()
@MarcinKunert 它只是帮助您将像素转换为 dp 并获取屏幕高度的扩展功能。如果你愿意,我可以给你这样的功能的例子
GenericLifecycleObserver 已弃用?任何解决方案?
F
F.Mysir

对于在 Kotlin 内部片段中使用,这是一个常见的用例,使用 KeyboardVisibilityEvent 库非常容易。

在 build.gradle 中:

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

在片段中:

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


检测键盘何时打开但不检测何时关闭。
你试过了吗?对我来说它起作用了。尝试使用 if (!isOpen) {}
J
Jaks Blackhat

在 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
      }

为什么是 0.139?你能告诉更多关于这个数字的信息吗?
z
zegee29

如果可以,请尝试扩展 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);
}

你如何扩展它:

实现 onFocus 监听并声明 'onKeyboardShown' 声明 'onKeyboardHidden'

我认为,屏幕高度的重新计算并非如前所述 100% 成功。需要明确的是,“以编程方式隐藏软键盘”方法不会调用“onKeyPreIme”的覆盖,但是如果您在任何地方执行此操作,则应该在那里执行“onKeyboardHidden”逻辑并且不要创建全面的解决方案。


k
kasra fallen

这无需更改您的活动的 android:windowSoftInputMode

第 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);
}

第 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
}

*** 请记住,为了使 clearFocus 起作用,您必须使父层次结构中的父级或第一个子级具有焦点。

    setFocusableInTouchMode(true);
    setFocusable(true);

V
Vlad

检查我的 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

S
Sagar Pilkhwal
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);
            }
       }
    });
    }
}

假设我点击edittext然后它setOnFocusChangeListener监听器将被调用然后我按下然后它关闭键盘&但没有点击其他视图,现在我再次点击已经有焦点的相同编辑文本,然后会发生什么?
这不是我的问题。请再次阅读我的问题 - 我有 5 个 EditText 的活动。当用户单击第一个 EditText 然后打开软键盘以在其中输入一些值。当用户单击第一个 EditText 时软键盘打开时,当软键盘从同一个 EditText 关闭时,我想将其他一些视图可见性设置为 Gone,然后我想将其他一些视图可见性设置为可见。在Android中单击第一个EditText时打开软键盘时是否有任何监听器或回调或任何黑客攻击?
当您按回键时,它会在 onfocus 监听器从不调用时关闭键盘,这就是我正在寻找的不是您所建议的
S
Shubham A.

使用这个类,

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();
        }
    }

}
}

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 - 完全取自 here


A
AnoDest

对于 adjustResize 和 FragmentActivity 的情况,@Jaap 接受的解决方案对我不起作用。

这是我的解决方案:

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;
        }
    }
};

U
Ullauri

另一种方法是检查用户何时停止输入......

当 TextEdit 处于焦点时(用户正在/正在键入),您可以隐藏视图(焦点侦听器)

并使用 Handler + Runnable 和文本更改侦听器关闭键盘(无论其可见性如何)并在延迟后显示视图。

要注意的主要是您使用的延迟,这取决于这些 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);
        }
    }
});

s
saleh gholamian

这段代码很好用

将此类用于根视图:

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);
}

}

并在活动或片段中设置键盘监听器:

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

        }
    });

2
2 revs

这没有按预期工作......

...已经看到许多使用尺寸计算来检查...

我想确定它是否打开并且I found isAcceptingText()

所以这真的不能回答这个问题,因为它没有解决打开或关闭问题,而更像是打开或关闭,所以它是相关代码,可以在各种情况下帮助其他人......

在一个活动中

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

在一个片段中

    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");

    }

D
David

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


文档明确指定不应在参考软输入键盘时使用此功能。
R
Rowan Berry

在使用“adjustResize”软输入模式(Kotlin 代码)时找到了一种准确判断键盘是否存在的方法

定义几个活动范围变量

private var activityHeight = 0
private var keyboardOpen = false

在 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")
        }
}

你现在有一个布尔值,它可以准确跟踪键盘是否打开,做你想做的事


a
agi

不幸的是,我没有足够高的声誉来评论 Jaap van Hengstum 的回答。但我读了一些人的评论,有一个问题,即 contentViewTop 总是 0 并且总是调用 onShowKeyboard(...)

我有同样的问题,并找出了我遇到的问题。我使用了 AppCompatActivity 而不是“正常”Activity。在这种情况下,Window.ID_ANDROID_CONTENT 指的是一个 ContentFrameLayout,而不是指具有右顶部值的 FrameLayout。在我的情况下,如果您必须使用另一种活动类型(我刚刚测试了 AppCompatActivity,也许它也是其他活动类型(如 FragmentActivity)的问题)使用“正常”Activity ,您必须访问 FrameLayout,它是 ContentFrameLayout 的祖先。


D
Derlin

当键盘显示

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

是真的,否则隐藏


p
psychoplasma
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;
        }
    }
};

我稍微修改了 Jaap 接受的答案。但在我的情况下,几乎没有 android:windowSoftInputMode=adjustResize 之类的假设,并且键盘在应用程序启动时没有出现在开头。而且,我假设屏幕与父母的身高相匹配。

contentHeight > 0 此检查让我知道相关屏幕是否隐藏或显示以应用键盘事件侦听此特定屏幕。我还在我的主要活动的 onCreate() 方法中传递了 attachKeyboardListeners(<your layout view here>) 中有关屏幕的布局视图。每次相关屏幕的高度发生变化时,我都会将其保存到 prevContentHeight 变量中,以便稍后检查键盘是显示还是隐藏。

对我来说,到目前为止,它工作得很好。我希望它也适用于其他人。


K
Khalil Al-rahman Yossefi

“Jaap van Hengstum”的答案对我有用,但没有必要像他刚才所说的那样设置“android:windowSoftInputMode”!

我把它变小了(它现在只检测我想要的,实际上是显示和隐藏键盘的事件):

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);
    }
}

只是不要忘记添加这个

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

A
Abhishek kumar

检查以下代码:

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代码:

//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);
        }
    }

J
Justin Case

有一个键盘已关闭的侦听器。
SearchEditText 类派生自 android.widget.EditText 类。这个类中有一个接口SearchEditText.OnKeyboardDismissListener。您可以查看文档:
https://developer.android.com/reference/androidx/leanback/widget/SearchEditText

笔记。在使用 SearchEditText 之前,您需要在 build.gradle (:app) 中设置 Gradle 依赖项:

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

也许有人会派上用场。

详细回复:

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

<?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>

注意:监听器适用于:

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

我在 MainActivity 上实现了这一点,UI 中只有一个 editText,但不会在键盘关闭时调用。
@AsadMukhtar 嗨,看看我上面的详细回复。希望能帮助到你。
V
Viper

我使用 this 答案和我的任务 LoopingTask 构建了监听器,顺便感谢一下。

使用 Viper Pack android 库中的 SoftKeyboardListener 很容易实现。

只需安装库,然后将其添加到您的代码中:

Lava.app.addSoftKeyboardListener(context, new Lava.SoftKeyboardListener() {
@Override
public void onSoftKeyboardShow(EditText focusedview) {
// when shows
}
@Override
public void onSoftKeyboardHide(EditText focusedview) {
// when hides
}
});

Lava.app.removeSoftKeyboardListeners()

创建

boolean isListenerAdded;
...
@Override
public void onCreate(Bundle sis) {
super.onCreate(sis);
...
if (!isListenerAdded) {
isListenerAdded = true;
// create your listener here
}
...
}
...

或者只是删除所有以前的:

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

Lava.app.removeSoftKeyboardListeners() 删除所有以前的 SoftKeyboardListener。


您好...检查//观看...您的答案没有详细说明。插入带有代码和参考的有效响应很有用。得出一个切实有效的解决方案。这个平台不仅仅是任何论坛。我们是世界上其他程序员和开发人员最大的帮助和支持中心。查看社区条款并学习如何发布;
D
DanMan

在我的情况下,'net.yslibrary.keyboardvisibilityevent:keyboardvisibilityevent:3.0.0-RC2'库由于某些原因无法使用滚动视图。这可能是因为滚动视图中的视图高度计算错误。插图就是答案。简单的插入监听器就像一个魅力。这是代码:

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