我想根据是否显示虚拟键盘来改变布局。我已经搜索了API和各种博客,但似乎找不到任何有用的东西。

这可能吗?

谢谢!


当前回答

就像@amalBit的答案一样,注册一个全局布局的监听器,并计算dectorView的可见底部和建议底部的差值,如果差值大于某个值(猜测IME的高度),我们认为IME是up的:

    final EditText edit = (EditText) findViewById(R.id.edittext);
    edit.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
        @Override
        public void onGlobalLayout() {
            if (keyboardShown(edit.getRootView())) {
                Log.d("keyboard", "keyboard UP");
            } else {
                Log.d("keyboard", "keyboard Down");
            }
        }
    });

private boolean keyboardShown(View rootView) {

    final int softKeyboardHeight = 100;
    Rect r = new Rect();
    rootView.getWindowVisibleDisplayFrame(r);
    DisplayMetrics dm = rootView.getResources().getDisplayMetrics();
    int heightDiff = rootView.getBottom() - r.bottom;
    return heightDiff > softKeyboardHeight * dm.density;
}

高度阈值100是IME猜测的最小高度。

这适用于adjustPan和adjuststresize。

其他回答

隐藏|显示事件键盘可以通过简单的hack在OnGlobalLayoutListener监听:

 final View activityRootView = findViewById(R.id.top_root);
        activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
            public void onGlobalLayout() {
                int heightDiff = activityRootView.getRootView().getHeight() - activityRootView.getHeight();

                if (heightDiff > 100) {
                    // keyboard is up
                } else {
                    // keyboard is down
                }
            }
        });

这里activityRootView是Activity的根视图。

基于Nebojsa Tomcic的代码,我开发了以下relativelayout子类:

import java.util.ArrayList;

import android.content.Context;
import android.util.AttributeSet;
import android.widget.RelativeLayout;

public class KeyboardDetectorRelativeLayout extends RelativeLayout {

    public interface IKeyboardChanged {
        void onKeyboardShown();
        void onKeyboardHidden();
    }

    private ArrayList<IKeyboardChanged> keyboardListener = new ArrayList<IKeyboardChanged>();

    public KeyboardDetectorRelativeLayout(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

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

    public KeyboardDetectorRelativeLayout(Context context) {
        super(context);
    }

    public void addKeyboardStateChangedListener(IKeyboardChanged listener) {
        keyboardListener.add(listener);
    }

    public void removeKeyboardStateChangedListener(IKeyboardChanged listener) {
        keyboardListener.remove(listener);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        final int proposedheight = MeasureSpec.getSize(heightMeasureSpec);
        final int actualHeight = getHeight();

        if (actualHeight > proposedheight) {
            notifyKeyboardShown();
        } else if (actualHeight < proposedheight) {
            notifyKeyboardHidden();
        }
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    private void notifyKeyboardHidden() {
        for (IKeyboardChanged listener : keyboardListener) {
            listener.onKeyboardHidden();
        }
    }

    private void notifyKeyboardShown() {
        for (IKeyboardChanged listener : keyboardListener) {
            listener.onKeyboardShown();
        }
    }

}

这工作得很好… 注意,当你的活动的软输入模式设置为" windowmanager . layoutparm . soft_input_adjust_resize "时,这个解决方案才会工作

我是这样做的:

添加OnKeyboardVisibilityListener接口。

public interface OnKeyboardVisibilityListener {
    void onVisibilityChanged(boolean visible);
}

HomeActivity.java:

public class HomeActivity extends Activity implements OnKeyboardVisibilityListener {

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_sign_up);
    // Other stuff...
    setKeyboardVisibilityListener(this);
}

private void setKeyboardVisibilityListener(final OnKeyboardVisibilityListener onKeyboardVisibilityListener) {
    final View parentView = ((ViewGroup) findViewById(android.R.id.content)).getChildAt(0);
    parentView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {

        private boolean alreadyOpen;
        private final int defaultKeyboardHeightDP = 100;
        private final int EstimatedKeyboardDP = defaultKeyboardHeightDP + (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP ? 48 : 0);
        private final Rect rect = new Rect();

        @Override
        public void onGlobalLayout() {
            int estimatedKeyboardHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, EstimatedKeyboardDP, parentView.getResources().getDisplayMetrics());
            parentView.getWindowVisibleDisplayFrame(rect);
            int heightDiff = parentView.getRootView().getHeight() - (rect.bottom - rect.top);
            boolean isShown = heightDiff >= estimatedKeyboardHeight;

            if (isShown == alreadyOpen) {
                Log.i("Keyboard state", "Ignoring global layout change...");
                return;
            }
            alreadyOpen = isShown;
            onKeyboardVisibilityListener.onVisibilityChanged(isShown);
        }
    });
}


@Override
public void onVisibilityChanged(boolean visible) {
    Toast.makeText(HomeActivity.this, visible ? "Keyboard is active" : "Keyboard is Inactive", Toast.LENGTH_SHORT).show();
  }
}

希望这对你有所帮助。

尽管本页上投票最多的解决方案说,有一个setWindowInsetsAnimationCallback的ViewCompat版本一直工作到Android 21。

所以现在,这个解决方案的方法一直适用于21。

来源:https://developer.android.com/reference/androidx/core/view/ViewCompat setWindowInsetsAnimationCallback (android.view.View androidx.core.view.WindowInsetsAnimationCompat.Callback)

android 11之前的解决方案:

androidx。Core 1.5.0发布了,这是我在android 11之前的设备上监听键盘显示/隐藏事件的方法。

gradle:

implementation "androidx.core:core-ktx:1.5.0"

片段:

   override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        val view  = activity?.window?.decorView ?: return
        ViewCompat.setOnApplyWindowInsetsListener(view) { v, insets ->
            val showingKeyboard = insets.isVisible(WindowInsetsCompat.Type.ime())
            if(showingKeyboard){
                //do something
            }
            insets
        }
    }

确保在视图销毁时删除侦听器,以避免内存泄漏。这个解决方案也只适用于软件输入模式为adjuststresize时,如果它是adjustPan, setOnApplyWindowInsetsListener将不会触发,如果任何人有关于如何使它与adjustPan一起工作的想法,请分享。

注意,根据医生的说法

* When running on devices with API Level 29 and before, the returned value is an
* approximation based on the information available. This is especially true for the {@link
* Type#ime IME} type, which currently only works when running on devices with SDK level 23
* and above.
*

insets.isVisible(ime)应该只在SDK级别高于23的设备上工作