Android에서 소프트웨어 키보드의 가시성을 확인하는 방법은 무엇입니까?


516

매우 간단한 작업을 수행해야합니다. 소프트웨어 키보드가 표시되어 있는지 확인하십시오. 이것은 안드로이드에서 가능합니까?


9
Reuben Scratton의 대답은 좋지만 태블릿에서 깨지는 것 같습니다. 확인 diff> 128을 diff> screenHeight / 3로 바꿨습니다.
킹스턴

2
Reuben Scratton의 답변은 좋았지 만 실제로 사용하려면 KaChi의 조정이 필요했습니다.
Cullan

1
Google이 표준 내장 방법을 모든 키보드 앱에서 사용할 수없는 이유는 무엇입니까?
과일

4
이것은 여전히 ​​시스템 기능이 아니라는 점을 잘 알고 있습니다.
longi

1
이 API가 10 년이 지난 후에도 여전히 누락 된 것은 확실합니다 . 안드로이드에서 멀어져 서 다행입니다.
Reuben Scratton

답변:


674

NEW ANSWER 추가 2012 년 1 월 25 일

아래 답변을 작성한 후 누군가가 ViewTreeObserver 와 친구들, 버전 1 이후 SDK에 숨어있는 API가 있음을 알려주었습니다 .

사용자 정의 레이아웃 유형이 필요하지 않고 훨씬 간단한 솔루션은 활동의 루트보기에 알려진 ID를 부여하는 것입니다 @+id/activityRoot.

final View activityRootView = findViewById(R.id.activityRoot);
activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
    @Override
    public void onGlobalLayout() {
        int heightDiff = activityRootView.getRootView().getHeight() - activityRootView.getHeight();
        if (heightDiff > dpToPx(this, 200)) { // if more than 200 dp, it's probably a keyboard...
            // ... do something here
        }
     }
});

다음과 같은 유틸리티 사용

public static float dpToPx(Context context, float valueInDp) {
    DisplayMetrics metrics = context.getResources().getDisplayMetrics();
    return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, valueInDp, metrics);
}

쉬운!

참고 : 응용 프로그램은 Android Manifest 에서이 플래그를 설정해야합니다. android:windowSoftInputMode="adjustResize"그렇지 않으면 위의 솔루션이 작동하지 않습니다.

원래 답변

그렇습니다. 가능하지만 그보다 훨씬 어렵습니다.

키보드가 언제 나타나고 사라지는 지에 대해 신경 써야하는 경우 (종종 자주) 내 최상위 레이아웃 클래스를 재정의하는 클래스로 사용자 정의하는 것 onMeasure()입니다. 기본 논리는 레이아웃이 창의 전체 영역보다 훨씬 적게 채워지면 소프트 키보드가 표시된다는 것입니다.

import android.app.Activity;
import android.content.Context;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.widget.LinearLayout;

/*
 * LinearLayoutThatDetectsSoftKeyboard - a variant of LinearLayout that can detect when 
 * the soft keyboard is shown and hidden (something Android can't tell you, weirdly). 
 */

public class LinearLayoutThatDetectsSoftKeyboard extends LinearLayout {

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

    public interface Listener {
        public void onSoftKeyboardShown(boolean isShowing);
    }
    private Listener listener;
    public void setListener(Listener listener) {
        this.listener = listener;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int height = MeasureSpec.getSize(heightMeasureSpec);
        Activity activity = (Activity)getContext();
        Rect rect = new Rect();
        activity.getWindow().getDecorView().getWindowVisibleDisplayFrame(rect);
        int statusBarHeight = rect.top;
        int screenHeight = activity.getWindowManager().getDefaultDisplay().getHeight();
        int diff = (screenHeight - statusBarHeight) - height;
        if (listener != null) {
            listener.onSoftKeyboardShown(diff>128); // assume all soft keyboards are at least 128 pixels high
        }
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);       
    }

    }

그런 다음 활동 수업에서 ...

public class MyActivity extends Activity implements LinearLayoutThatDetectsSoftKeyboard.Listener {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ...
        LinearLayoutThatDetectsSoftKeyboard mainLayout = (LinearLayoutThatDetectsSoftKeyboard)findViewById(R.id.main);
        mainLayout.setListener(this);
        ...
    }


    @Override
    public void onSoftKeyboardShown(boolean isShowing) {
        // do whatever you need to do here
    }

    ...
}

62
활동에 다음 속성을 설정해야한다는 것을 깨달을 때까지 이것은 작동하지 않았습니다. android : windowSoftInputMode = "
adjustResize

9
트릭을하고있는 것 같습니다. 또한 루트 뷰의 ID를 모르는 경우 뷰를 얻는 방법은 다음과 같습니다.((ViewGroup) findViewById(android.R.id.content)).getChildAt(0)
Goldsmith

8
실제 루트보기 ( android.R.id.content)를 사용 하여이 작업을 시도하면 System응용 프로그램이 아닌 높이가 변경되는 엔티티 라고 자신있게 말할 수 있습니다 . Android 팀이 휴식을 취하고 SoftKeyboard 입력에 대해 최소한 기본 사항을 알려주는 것이 훨씬 안전합니다.
Graeme

14
그주의 heightDiff항상 작업 표시 줄의 높이를 포함 할 것이다. 높이가 일부 상수보다 크지 만 Nexus 4와 같은 xxhdpi 장치에는 100 픽셀이면 충분하지 않은 경우 테스트를 통해 무시 된 새로운 답변 에서이 해킹 된 작업을 실제로 사용하려면 해당 값을 DP로 변환하십시오 주위에.
Paul Lammertsma

8
참고 : WindowManager.LayoutParams.FLAG_FULLSCREEN 및 전체 화면 테마에서는 작동하지 않습니다.
VAV

303

희망적으로 이것은 누군가를 도와줍니다.

Reuben Scratton이 제공 한 새로운 답변은 훌륭하고 효율적이지만 windowSoftInputMode를 adjustResize로 설정 한 경우에만 작동합니다. adjustPan으로 설정 한 경우 코드 스 니펫을 사용하여 키보드가 표시되는지 여부를 여전히 감지 할 수 없습니다. 이 문제를 해결하기 위해 위 코드를 약간 수정했습니다.

final View activityRootView = findViewById(R.id.activityRoot);
activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(new 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);

    int heightDiff = activityRootView.getRootView().getHeight() - (r.bottom - r.top);
    if (heightDiff > 0.25*activityRootView.getRootView().getHeight()) { // if more than 25% of the screen, its probably a keyboard...
        ... do something here
    }
 }
}); 

1
이것은 나를 위해 일한 것입니다. 확대 / 축소에도 불구하고 stackoverflow.com/a/5224088/530513TwoDScrollerView 과 유사한 사용자 정의에서 키보드 상태를 감지하려고했습니다 . 자식은 단순하지 않지만 사용자 정의 레이아웃 (확장자 ) 이었지만 설정에도 불구하고 권장 솔루션을 사용하여 키보드를 감지 할 수 없었습니다 . 감사! ImageViewRelativeLayoutandroid:windowSoftInputMode="adjustResize"
David O'Meara

1
감사합니다 감사합니다 감사합니다! adjustResize는 내 앱에서만 사용할 수 없으며 솔루션이 완벽하게 작동했습니다.
dwemthy

1
함께 작동 ActionBar하고 ActionBarSherlock. 고마워요! 그건 그렇고, 방법이 있습니다 r.height():)
Dmitry Zaytsev

1
이 답변을 어떻게 든 표시하기 위해 23 시간 이내에 여기에 현상금을 첨부 할 것입니다.
Dmitry Zaytsev

9
heightDiff > root.getRootView().getHeight() / 4고해상도 장치와 함께 사용하기에 좋은 가치입니다. 100px는 짧습니다. 1080x1920 해상도, 1920-(996-75)의 Nexus 5에서>? 100 = 999 1920-(1776-75)>? 100 = 219 // 480x800 해상도의 은하 s2에서 키보드가 작동 중-800-(800-38)>? 100 = 38800-(410-38)>? 100 = 428 // 키보드가 작동하므로 마법 번호 100px로 충분하지 않습니다.
Flask_KR

55

그것은 컴퓨터의 관점에서 영원히 있었지만이 질문은 여전히 ​​믿을 수 없을 정도로 관련이 있습니다!

그래서 위의 답변을 취하여 조금 조합하고 개선했습니다 ...

public interface OnKeyboardVisibilityListener {


    void onVisibilityChanged(boolean visible);
}

public final void setKeyboardListener(final OnKeyboardVisibilityListener listener) {
    final View activityRootView = ((ViewGroup) getActivity().findViewById(android.R.id.content)).getChildAt(0);

    activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {

        private boolean wasOpened;

        private final int DefaultKeyboardDP = 100;

        // From @nathanielwolf answer...  Lollipop includes button bar in the root. Add height of button bar (48dp) to maxDiff
        private final int EstimatedKeyboardDP = DefaultKeyboardDP + (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP ? 48 : 0);

        private final Rect r = new Rect();

        @Override
        public void onGlobalLayout() {
            // Convert the dp to pixels.
            int estimatedKeyboardHeight = (int) TypedValue
                    .applyDimension(TypedValue.COMPLEX_UNIT_DIP, EstimatedKeyboardDP, activityRootView.getResources().getDisplayMetrics());

            // Conclude whether the keyboard is shown or not.
            activityRootView.getWindowVisibleDisplayFrame(r);
            int heightDiff = activityRootView.getRootView().getHeight() - (r.bottom - r.top);
            boolean isShown = heightDiff >= estimatedKeyboardHeight;

            if (isShown == wasOpened) {
                Log.d("Keyboard state", "Ignoring global layout change...");
                return;
            }

            wasOpened = isShown;
            listener.onVisibilityChanged(isShown);
        }
    });
}

나를 위해 작동 :)

참고 : 당신이 알 경우 DefaultKeyboardDP이 값으로 장치 플레이에 적합하고 값을해야하는지 알고 모두에 대한 소감을 게시하지 않습니다 ... 결국 우리는 모든 장치에 맞게 올바른 값을 얻을 것이다!

자세한 내용은 Cyborg 의 구현을 확인하십시오.


2
+1 감사합니다 !! 다른 답변을 시도했지만 작동하지 않습니다. 그런 다음 나는 당신을 발견했고 그것은 매력처럼 작동합니다. 멋진 코드! : D
Kevin van Mierlo

android : windowSoftInputMode = "stateHidden | adjustPan"또는 android : windowSoftInputMode = "stateHidden | adjustResize"추가하는 경우에만 작동합니다. 감사합니다 !!!!
Lena Bru

확실해? 메모리 서버 인 경우 android : windowSoftInputMode에 기본값이있을 때 이벤트가 제대로 표시됩니다. 해결되지 않은 유일한 것은 화면의 동작이므로 수동으로 축소했습니다.
TacB0sS

2
작은 수정 : private final int EstimatedKeyboardDP = DefaultKeyboardDP + (Build.VERSION.SDK_INT> = Build.VERSION_CODES.LOLLIPOP? 48 : 0);
binaryKarmic

2
우수한!! "windowSoftInputMode"가 "adjustPan"/ "adjustResize"/ "adjustPan | stateHidden"/ "adjustResize | stateHidden"으로 설정되어 있거나이 옵션이 없어도 항상 작동합니다! XiaoMi 8에서 테스트되었습니다.
Zhou Hongbo

52

죄송 후반 대답을하지만, 나는 누군가가 도움이 찾을 것이라고 할 수있다 통지 청취자 및 기타 유용한 것들과 열기 / 닫기 이벤트를 처리하기 위해 약간의 헬퍼 클래스를 생성했다 :

import android.graphics.Rect;
import android.view.View;
import android.view.ViewTreeObserver;

import java.util.LinkedList;
import java.util.List;

public class SoftKeyboardStateWatcher implements ViewTreeObserver.OnGlobalLayoutListener {

    public interface SoftKeyboardStateListener {
        void onSoftKeyboardOpened(int keyboardHeightInPx);
        void onSoftKeyboardClosed();
    }

    private final List<SoftKeyboardStateListener> listeners = new LinkedList<SoftKeyboardStateListener>();
    private final View activityRootView;
    private int        lastSoftKeyboardHeightInPx;
    private boolean    isSoftKeyboardOpened;

    public SoftKeyboardStateWatcher(View activityRootView) {
        this(activityRootView, false);
    }

    public SoftKeyboardStateWatcher(View activityRootView, boolean isSoftKeyboardOpened) {
        this.activityRootView     = activityRootView;
        this.isSoftKeyboardOpened = isSoftKeyboardOpened;
        activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(this);
    }

    @Override
    public void onGlobalLayout() {
        final Rect r = new Rect();
        //r will be populated with the coordinates of your view that area still visible.
        activityRootView.getWindowVisibleDisplayFrame(r);

        final int heightDiff = activityRootView.getRootView().getHeight() - (r.bottom - r.top);
        if (!isSoftKeyboardOpened && heightDiff > 100) { // if more than 100 pixels, its probably a keyboard...
            isSoftKeyboardOpened = true;
            notifyOnSoftKeyboardOpened(heightDiff);
        } else if (isSoftKeyboardOpened && heightDiff < 100) {
            isSoftKeyboardOpened = false;
            notifyOnSoftKeyboardClosed();
        }
    }

    public void setIsSoftKeyboardOpened(boolean isSoftKeyboardOpened) {
        this.isSoftKeyboardOpened = isSoftKeyboardOpened;
    }

    public boolean isSoftKeyboardOpened() {
        return isSoftKeyboardOpened;
    }

    /**
     * Default value is zero {@code 0}.
     *
     * @return last saved keyboard height in px
     */
    public int getLastSoftKeyboardHeightInPx() {
        return lastSoftKeyboardHeightInPx;
    }

    public void addSoftKeyboardStateListener(SoftKeyboardStateListener listener) {
        listeners.add(listener);
    }

    public void removeSoftKeyboardStateListener(SoftKeyboardStateListener listener) {
        listeners.remove(listener);
    }

    private void notifyOnSoftKeyboardOpened(int keyboardHeightInPx) {
        this.lastSoftKeyboardHeightInPx = keyboardHeightInPx;

        for (SoftKeyboardStateListener listener : listeners) {
            if (listener != null) {
                listener.onSoftKeyboardOpened(keyboardHeightInPx);
            }
        }
    }

    private void notifyOnSoftKeyboardClosed() {
        for (SoftKeyboardStateListener listener : listeners) {
            if (listener != null) {
                listener.onSoftKeyboardClosed();
            }
        }
    }
}

사용 예 :

final SoftKeyboardStateWatcher softKeyboardStateWatcher 
    = new SoftKeyboardStateWatcher(findViewById(R.id.activity_main_layout);

// Add listener
softKeyboardStateWatcher.addSoftKeyboardStateListener(...);
// then just handle callbacks

2
클래스는 거의 없지만 구현은 반드시 :)입니다. 고마워요 :)
Atul O Holic

1
당신이 그것에 문제가 있다면 핑 :) 나는 2 프로젝트에서 성공적으로 사용
Artem Zinnatullin

위의 많은 예제를 시도하고 사소한 문제가 발생한 후에 이것은 많은 다른 장치 (xxhdpi 포함)에서 나에게 가장 잘 맞는 것입니다. 또한 자체 재사용 가능한 클래스에 있습니다. 모노 드로이드에 사용되도록 변환했습니다.
kheit

일부 키보드에는 경우에 따라 추가 사용자 정의 키 행이 있습니다 (예 : 예측 단어). 를 사용하면 getLastKeyboardHeightInPx()해당 행의 높이가 포함되지 않으므로 키보드 자체의 일부 가 아닙니다. 당신도 그것을 고려하는 방법을 알고 있습니까?
ygesher

키보드가 나타날 때 레이아웃 높이 변경을 손상시킬 준비가 된 경우에만 작동합니다. 권리?
M. Usman Khan

33

고밀도 장치에서 소프트 키보드의 가시성을 잘못 감지하지 않도록하기위한 몇 가지 개선 사항 :

  1. 높이 차이의 임계 값은 128 픽셀이 아니라 128dp 로 정의해야합니다 . 참조 지표 및 그리드에 대한 구글 설계 문서 , 48 DP는 터치 개체에 대한 편안 크기이며, 32 DP는 버튼을 최소이다. 일반 소프트 키보드에는 4 행의 키 버튼이 포함되어야하므로 최소 키보드 높이는 32dp * 4 = 128dp 이어야합니다. 즉, 임계 값 크기는 장치 밀도를 곱하여 픽셀로 전송해야합니다. xxxhdpi 장치 (밀도 4)의 경우 소프트 키보드 높이 임계 값은 128 * 4 = 512 픽셀이어야합니다.

  2. 루트 뷰와 해당 가시 영역의 높이 차이 :
    루트 뷰 높이-상태 표시 줄 높이-보이는 프레임 높이 = 루트 뷰 하단-보이는 프레임 하단 (상태 표시 줄 높이는 루트 뷰 표시 프레임의 상단과 동일하므로)

    private final String TAG = "TextEditor";
    private TextView mTextEditor;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_editor);
        mTextEditor = (TextView) findViewById(R.id.text_editor);
        mTextEditor.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                isKeyboardShown(mTextEditor.getRootView());
            }
        });
    }
    
    private boolean isKeyboardShown(View rootView) {
        /* 128dp = 32dp * 4, minimum button height 32dp and generic 4 rows soft keyboard */
        final int SOFT_KEYBOARD_HEIGHT_DP_THRESHOLD = 128;
    
        Rect r = new Rect();
        rootView.getWindowVisibleDisplayFrame(r);
        DisplayMetrics dm = rootView.getResources().getDisplayMetrics();
        /* heightDiff = rootView height - status bar height (r.top) - visible frame height (r.bottom - r.top) */
        int heightDiff = rootView.getBottom() - r.bottom;
        /* Threshold size: dp to pixels, multiply with display density */
        boolean isKeyboardShown = heightDiff > SOFT_KEYBOARD_HEIGHT_DP_THRESHOLD * dm.density;
    
        Log.d(TAG, "isKeyboardShown ? " + isKeyboardShown + ", heightDiff:" + heightDiff + ", density:" + dm.density
                + "root view height:" + rootView.getHeight() + ", rect:" + r);
    
        return isKeyboardShown;
    }

4
이것은 받아 들일만한 대답이 될 만하다. 밀도를 무시하면 폼 팩터는 다르지만 픽셀 크기는 비슷한 장치에서 매우 다른 결과를 얻었습니다. 감사!
Ginger McMurray

2
감사합니다 ... 이것이 더 좋은 조건입니다!
TacB0sS

1
우수한. isKeyboardShown () 메소드가 필요합니다. 감사합니다
Danylo Volokh

2020 년에도 효과가 있습니다. 완벽한 답변. 모든 코드를 시도했지만 완벽하게 작동합니다.
Mitesh Jain

8

나는 이것을 알아 내기 위해 약간의 시간을 사용했다 ... 나는 그것을 CastExceptions를 실행했지만 layout.xml의 LinearLayout을 클래스 이름으로 바꿀 수 있다고 생각했다.

이처럼 :

<?xml version="1.0" encoding="UTF-8"?>
<LinearLayout android:layout_width="fill_parent" android:layout_height="fill_parent"
    xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/llMaster">

<com.ourshoppingnote.RelativeLayoutThatDetectsSoftKeyboard android:background="@drawable/metal_background"
    android:layout_width="fill_parent" android:layout_height="fill_parent"
    android:id="@+id/rlMaster" >
    <LinearLayout android:layout_width="fill_parent"
        android:layout_height="1dip" android:background="@drawable/line"></LinearLayout>

          ....

</com.ourshoppingnote.RelativeLayoutThatDetectsSoftKeyboard>    


</LinearLayout>

이렇게하면 캐스트 문제가 발생하지 않습니다.

... 모든 페이지에서이 작업을 수행하지 않으려면 "Android의 MasterPage"를 사용하는 것이 좋습니다. 여기 링크를 참조하십시오 : http://jnastase.alner.net/archive/2011/01/08/ldquomaster-pagesrdquo-in-android.aspx


패키지 / 클래스 이름이 같지 않으면 XML에 붙여 넣을 때주의해야합니다. 이클립스는 방금 정지하기로 결정하고 종료해야한다. 그런 전문적인 제품. / s
Spencer Ruport

5
@SpencerRuport, 그것이 무료 인 이유입니다.
Cody

@DoctorOreo-IntelliJ를 받으십시오. 무료이며 빨지 않습니다.
Mark

@Mark-내가 게시한지 몇 개월 후에 실제로 IntelliJ를 사용해 보았습니다. 이클립스보다 IMO가 훨씬 낫다. 그들의 제품은 (대부분) 훌륭하다고 생각합니다. 나는 심지어 몇 개를 샀다.
Cody

오래된 주석 스레드를 부활 시켜서 죄송합니다. 사용하고 즐기게되어 기쁩니다. 나는 IntelliJ와 iOS 용 AppCode 및 Python 작업 용 PyCharm을 사용하는 것을 좋아합니다. 건배!
Mark


5

아이디어는 키보드를 숨기고 동시에 소프트 입력 상태를 확인해야하는 경우 다음 해결 방법을 사용하는 것입니다.

public boolean hideSoftInput() {
    InputMethodManager imm = (InputMethodManager) getSystemService(Activity.INPUT_METHOD_SERVICE);
    return imm.hideSoftInputFromWindow(mViewPager.getWindowToken(), 0);
}

이 메소드는 숨기 전에 키보드가 표시되면 true를 리턴합니다.


이것은 높이와 모든 것을 사용하지 않고 작동하는 하나입니다 ... 감사합니다 ... u 내 시간을 절약 ...
Vji

4

@Reuben_Scratton의 방법과 @Yogesh의 방법의 조합이 가장 잘 작동하는 것으로 나타났습니다. 그들의 방법을 결합하면 다음과 같은 결과가 나옵니다.

final View activityRootView = findViewById(R.id.activityRoot);
activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
  @Override
  public void onGlobalLayout() {
    if (getResources().getConfiguration().keyboardHidden == Configuration.KEYBOARDHIDDEN_NO) { // Check if keyboard is not hidden
       // ... do something here
    }
  }
});

항상 Configuration.KEYBOARDHIDDEN_NO.
fantouch

4

활동의 decorView를 사용하여 소프트 키보드의 숨기기를 관찰 할 수 있습니다.

public final class SoftKeyboardUtil {
    public static final String TAG = "SoftKeyboardUtil";
    public static void observeSoftKeyBoard(Activity activity , final OnSoftKeyBoardHideListener listener){
        final View decorView = activity.getWindow().getDecorView();
        decorView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                Rect rect = new Rect();
                decorView.getWindowVisibleDisplayFrame(rect);
                int displayHight = rect.bottom - rect.top;
                int hight = decorView.getHeight();
                boolean hide = (double)displayHight / hight > 0.8 ;
                if(Log.isLoggable(TAG, Log.DEBUG)){
                    Log.d(TAG ,"DecorView display hight = "+displayHight);
                    Log.d(TAG ,"DecorView hight = "+ hight);
                    Log.d(TAG, "softkeyboard visible = " + !hide);
                }

                listener.onSoftKeyBoardVisible(!hide);

            }
        });
    }



    public interface OnSoftKeyBoardHideListener{
        void onSoftKeyBoardVisible(boolean visible);
    }
}

4

차이점 코딩을 가정하는 대신 응용 프로그램에 메뉴 옵션이 있었기 때문에 이와 같은 작업을 수행했습니다.

final View root= findViewById(R.id.myrootview); 
root.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
@Override
    public void onGlobalLayout() {
        int heightDiff = root.getRootView().getHeight() - root.getHeight();

        Rect rectgle= new Rect();
        Window window= getWindow();
        window.getDecorView().getWindowVisibleDisplayFrame(rectgle);
        int contentViewTop=                     
          window.findViewById(Window.ID_ANDROID_CONTENT).getTop();
        if(heightDiff <= contentViewTop){
            //Soft KeyBoard Hidden
        }else{
            //Soft KeyBoard Shown
        }
     }
});

android : windowSoftInputMode = "adjustPan"에서는 작동하지 않습니다. 소프트 키보드가 나타난 후 화면이 축소되지 않도록하고 싶었습니다. adjustPan에서도 작동하도록 수정 사항을 알려주시겠습니까?
Shirish Herwade

4

시스템 삽입이있는 솔루션도 있지만 API >= 21( Android L) 에서만 작동합니다 . 의 BottomNavigationView하위 항목이 LinearLayout있고 키보드가 표시되면 숨겨야 한다고 가정 해보십시오 .

> LinearLayout
  > ContentView
  > BottomNavigationView

다음 LinearLayout과 같은 방법 으로 확장 하면됩니다.

public class KeyboardAwareLinearLayout extends LinearLayout {
    public KeyboardAwareLinearLayout(Context context) {
        super(context);
    }

    public KeyboardAwareLinearLayout(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    public KeyboardAwareLinearLayout(Context context,
                                     @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    public KeyboardAwareLinearLayout(Context context, AttributeSet attrs,
                                     int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

    @Override
    public WindowInsets onApplyWindowInsets(WindowInsets insets) {
        int childCount = getChildCount();
        for (int index = 0; index < childCount; index++) {
            View view = getChildAt(index);
            if (view instanceof BottomNavigationView) {
                int bottom = insets.getSystemWindowInsetBottom();
                if (bottom >= ViewUtils.dpToPx(200)) {
                    // keyboard is shown
                    view.setVisibility(GONE);
                } else {
                    // keyboard is hidden
                    view.setVisibility(VISIBLE);
                }
            }
        }
        return insets;
    }
}

아이디어는 키보드가 표시 될 때 시스템 삽입 .bottom값 이 매우 큰 값으로 변경된다는 것입니다 .


4

이를 위해 숨겨진 방법이 있습니다 InputMethodManager.getInputMethodWindowVisibleHeight. 그러나 왜 숨겨져 있는지 모르겠습니다.

import android.content.Context
import android.os.Handler
import android.view.inputmethod.InputMethodManager

class SoftKeyboardStateWatcher(private val ctx: Context) {
  companion object {
    private const val DELAY = 10L
  }

  private val handler = Handler()
  private var isSoftKeyboardOpened: Boolean = false

  private val height: Int
    get() {
      val imm = ctx.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
      val method = imm.javaClass.getMethod("getInputMethodWindowVisibleHeight")
      method.isAccessible = true
      return method.invoke(imm) as Int
    }

  private val task: Runnable by lazy {
    Runnable {
      start()
      if (!isSoftKeyboardOpened && height > 0) {
        isSoftKeyboardOpened = true
        notifyOnSoftKeyboardOpened(height)
      } else if (isSoftKeyboardOpened && height == 0) {
        isSoftKeyboardOpened = false
        notifyOnSoftKeyboardClosed()
      }
    }
  }

  var listener: SoftKeyboardStateListener? = null

  interface SoftKeyboardStateListener {
    fun onSoftKeyboardOpened(keyboardHeightInPx: Int)
    fun onSoftKeyboardClosed()
  }

  fun start() {
    handler.postDelayed(task, DELAY)
  }

  fun stop() {
    handler.postDelayed({
      if (!isSoftKeyboardOpened) handler.removeCallbacks(task)
    }, DELAY * 10)
  }

  private fun notifyOnSoftKeyboardOpened(keyboardHeightInPx: Int) {
    listener?.onSoftKeyboardOpened(keyboardHeightInPx)
  }

  private fun notifyOnSoftKeyboardClosed() {
    listener?.onSoftKeyboardClosed()
  }
}

누군가가 이것을 원한다면-Xamarin에서도 작동합니다. 메서드 이름은 정확히 동일하며 InputMethodManager의 Class 속성을 통해 동일한 방식으로 액세스해야합니다.
Konstantin Severy

이 API는 지원되지 않는 API이며 (이유로 숨겨져 있음) 초보자에게는 KitKat에서 작동하지 않습니다.
다니엘 리치

3

이러한 솔루션 중 어느 것도 Lollipop에 그대로 작동하지 않습니다. 롤리팝 activityRootView.getRootView().getHeight()에서 버튼 막대의 높이는 포함하지만 뷰 측정에는 포함되지 않습니다. Lollipop과 함께 작동하기 위해 위의 최고 / 간단한 솔루션을 조정했습니다.

    final View activityRootView = findViewById(R.id.activityRoot);
activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(new 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);

    int heightDiff = activityRootView.getRootView().getHeight() - (r.bottom - r.top);
    Resources res = getResources();
    // The status bar is 25dp, use 50dp for assurance
    float maxDiff =
        TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 50, res.getDisplayMetrics());

    //Lollipop includes button bar in the root. Add height of button bar (48dp) to maxDiff
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
      float buttonBarHeight =
          TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 48, res.getDisplayMetrics());
      maxDiff += buttonBarHeight;
    }
    if (heightDiff > maxDiff) { // if more than 100 pixels, its probably a keyboard...
      ...do something here
    }
  }
});

stackoverflow.com/a/18992807/2914140 의 비슷한 솔루션이 왜 효과가 없었으며 솔루션이 어떻게 다릅니 까?
CoolMind

3

고정 번호 추가를 제안하는 위의 대부분의 솔루션을 사용하는 동안 버그가 발생했습니다.

S4는 dpi가 높기 때문에 내비게이션 바의 높이가 100px이되었으므로 앱은 키보드가 항상 열려 있다고 생각합니다.

따라서 모든 새로운 고해상도 전화가 출시되면서 하드 코딩 된 값을 사용하는 것이 장기적으로 좋은 생각이 아니라고 생각합니다.

다양한 화면과 장치에서 테스트 한 후 발견 한 더 좋은 방법은 백분율을 사용하는 것입니다. decorView와 ur 앱 콘텐츠의 차이를 얻은 다음 그 차이의 백분율을 확인하십시오. 내가 얻은 통계에서 대부분의 탐색 표시 줄 (크기, 해상도 등에 관계없이)은 화면의 3 %에서 5 % 사이입니다. 키보드가 열린 것처럼 화면의 47 % ~ 55 %를 차지했습니다.

결론적으로 내 솔루션은 diff가 10 % 이상인지 확인하고 키보드가 열린 것으로 가정합니다.


3

Reuban의 답변 중 약간의 변형을 사용하여 특정 상황, 특히 고해상도 장치에서 더 도움이되는 것으로 나타났습니다.

final View activityRootView = findViewById(android.R.id.content);
activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(
        new OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                int heightView = activityRootView.getHeight();
                int widthView = activityRootView.getWidth();
                if (1.0 * widthView / heightView > 3) {
                    //Make changes for Keyboard not visible
                } else {
                    //Make changes for keyboard visible
                }
            }
        });

이 R.id.activityRoot는 무엇입니까
ranjith

2
을 만들고 사용하는 대신 정확히 필요한 것을 R.id.activityRoot사용할 수 있습니다 android.R.id.content.
Marcin Orlowski

3

그것은 컴퓨터와 관련하여 영원히 존재했지만이 질문은 여전히 ​​믿을 수 없을 정도로 관련이 있습니다! 그래서 위의 답변을 취하여 조금 조합하고 개선했습니다 ...

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

public final void setKeyboardListener(final OnKeyboardVisibilityListener listener) {
    final View activityRootView = ((ViewGroup) getActivity().findViewById(android.R.id.content)).getChildAt(0);
    activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {

        private boolean wasOpened;

    private final Rect r = new Rect();

        @Override
        public void onGlobalLayout() {
            activityRootView.getWindowVisibleDisplayFrame(r);

            int heightDiff = activityRootView.getRootView().getHeight() - (r.bottom - r.top);
            boolean isOpen = heightDiff > 100;
            if (isOpen == wasOpened) {
                logDebug("Ignoring global layout change...");
                return;
            }

            wasOpened = isOpen;
            listener.onVisibilityChanged(isOpen);
        }
    });
}

그것은 나를 위해 작동합니다.


3

이 시도:

final View activityRootView = getWindow().getDecorView().getRootView();
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);

        int heightDiff = activityRootView.getRootView().getHeight() - (r.bottom - r.top);
        if (heightDiff < activityRootView.getRootView().getHeight() / 4 ) { // if more than 100 pixels, its probably a keyboard...
             // ... do something here ... \\
        }
    }
});

2

내 대답은 기본적으로 Kachi의 대답과 동일하지만 앱 전체에서 사용되는 방식을 정리하기 위해 멋진 도우미 클래스로 래핑했습니다.

import android.app.Activity;
import android.app.Fragment;
import android.graphics.Rect;
import android.view.View;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;

/**
 * Detects Keyboard Status changes and fires events only once for each change
 */
public class KeyboardStatusDetector {
    KeyboardVisibilityListener visibilityListener;

    boolean keyboardVisible = false;

    public void registerFragment(Fragment f) {
        registerView(f.getView());
    }

    public void registerActivity(Activity a) {
        registerView(a.getWindow().getDecorView().findViewById(android.R.id.content));
    }

    public KeyboardStatusDetector registerView(final View v) {
        v.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                Rect r = new Rect();
                v.getWindowVisibleDisplayFrame(r);

                int heightDiff = v.getRootView().getHeight() - (r.bottom - r.top);
                if (heightDiff > 100) { // if more than 100 pixels, its probably a keyboard...
                    /** Check this variable to debounce layout events */
                    if(!keyboardVisible) {
                        keyboardVisible = true;
                        if(visibilityListener != null) visibilityListener.onVisibilityChanged(true);
                    }
                } else {
                    if(keyboardVisible) {
                        keyboardVisible = false;
                        if(visibilityListener != null) visibilityListener.onVisibilityChanged(false);
                    }
                }
            }
        });

        return this;
    }

    public KeyboardStatusDetector setVisibilityListener(KeyboardVisibilityListener listener) {
        visibilityListener = listener;
        return this;
    }

    public static interface KeyboardVisibilityListener {
        public void onVisibilityChanged(boolean keyboardVisible);
    }
}

이를 사용하여 다음과 같이 앱 전체에서 키보드 변경을 감지 할 수 있습니다.

    new KeyboardStatusDetector()
            .registerFragment(fragment)  //register to a fragment 
            .registerActivity(activity)  //or register to an activity
            .registerView(view)          //or register to a view
            .setVisibilityListener(new KeyboardVisibilityListener() {
                @Override
                public void onVisibilityChanged(boolean keyboardVisible) {
                    if(keyboardVisible) {
                       //Do stuff for keyboard visible
                    }else {
                       //Do stuff for keyboard hidden
                    }
                }
            });

참고 : "등록"호출 중 하나만 사용하십시오. 그들은 모두 똑같이 작동하며 편의를 위해서만 있습니다.


2

당신은 이것을 시도 할 수 있습니다, 나를 위해 위대한 일 :

InputMethodManager imm = (InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE);

if (imm.isAcceptingText()) {
    //Software Keyboard was shown..
} else {
    //Software Keyboard was not shown..
}

1
이를 사용하지 마십시오 키보드 등을 사용하여 숨겨진 된 경우, 그건 사실 반환하지만보기는 적어도 산들 바람에 포커스가
Maragues

2
항상 표시됩니다
jose920405

2

뷰 페이저 내에서 조각의 방향을 변경할 때 키보드 상태를 유지하는 데 어려움이있었습니다. 왜 그런지 잘 모르겠지만, 그것은 기발한 것처럼 보이고 표준 활동과 다르게 행동합니다.

이 경우 키보드 상태를 유지하려면 먼저 키보드 상태를 추가 android:windowSoftInputMode = "stateUnchanged"해야합니다.AndroidManifest.xml . 그러나 이것이 실제로 전체 문제를 해결하지는 못합니다. 이전에 방향 변경 전에 키보드를 열면 키보드가 열리지 않았습니다. 다른 모든 경우에는 동작이 올바른 것 같습니다.

그런 다음 여기에 언급 된 솔루션 중 하나를 구현해야합니다. 내가 찾은 가장 깨끗한 것은 George Maisuradze의 것입니다. hideSoftInputFromWindow의 부울 콜백을 사용하십시오.

InputMethodManager imm = (InputMethodManager) getSystemService(Activity.INPUT_METHOD_SERVICE);
return imm.hideSoftInputFromWindow(mViewPager.getWindowToken(), 0);

이 값을 Fragment의 onSaveInstanceState메소드 에 저장 하고 검색했습니다 onCreate. 그런 다음 키보드 onCreateView값이있는 경우 키보드를 강제로 true표시했습니다 (단편이 파괴되기 전에 키보드를 실제로 숨기 전에 키보드가 표시되면 true를 반환합니다).


1

여기 내 솔루션이 있으며 작동합니다. 픽셀 크기를 찾는 대신 컨텐츠 뷰의 높이가 변경되었는지 확인하십시오.

// Scroll to the latest comment whenever the keyboard is shown
commentsContent.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {

        private int oldHeight;

        @Override
        public void onGlobalLayout() {
            int newHeight = commentsContent.getMeasuredHeight();
            if (newHeight < oldHeight) {
                // Check for the keyboard showing in case the height difference
                // is a result of orientation change
                if (isSoftKeyboardShowing(CommentsActivity.this)) {
                    // Keyboard is showing so scroll to the latest comment
                    scrollToLatestComment();
                }
            }
            oldHeight = newHeight;
        }

    });


public static boolean isSoftKeyboardShowing(Activity activity) {
    InputMethodManager inputMethodManager = (InputMethodManager) activity.getSystemService(Activity.INPUT_METHOD_SERVICE);
    return inputMethodManager.isActive();
}

inputMethodManager.isActive ()는 키보드의 작동 여부에 관계없이 항상 true를 반환합니다.
EionRobb

1

하드 코드를 만들지 마십시오. 가장 좋은 방법은 KeyBord Show를 사용하여 EditText에 초점을 맞추는 동안 뷰의 크기를 조정해야합니다. 아래 코드를 사용하여 활동의 크기 조정 속성을 매니페스트 파일에 추가 할 수 있습니다.

android:windowSoftInputMode="adjustResize"


1

이것을 알아내는 직접적인 방법이 있습니다. 또한 레이아웃을 변경할 필요가 없습니다.
따라서 몰입 형 전체 화면 모드에서도 작동합니다.

요령은 소프트 키보드를 숨기거나 표시하려고 시도하고 그 결과를 캡처하는 것입니다.
당황하지 마십시오. 실제로 키보드를 표시하거나 숨기지는 않습니다. 우리는 단지 국가를 요구합니다.

최신 상태를 유지하려면 핸들러를 사용하여 작업을 예를 들어 200 밀리 초마다 반복하면됩니다. https://stackoverflow.com/a/27567074/2525452

에서 구현을 찾을 수 있습니다.


1

이 방법을 사용하면 키 보드가 표시되는지 여부를 알 수 있습니다.

 public Boolean isSoftKeyBoardVisible(){
    InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);

    if (imm.isAcceptingText()) {
        Log.d(TAG,"Software Keyboard was shown");
        return true;
    } else {
        Log.d(TAG,"Software Keyboard was not shown");
        return false;
    }

}

항상 표시됩니다
jose920405

0

int heightDiff = activityRootView.getRootView().getHeight() - activityRootView.getHeight();반투명 상태 표시 줄 모드를 설정하면 Reuben Scratton의 새 답변 (HeightDiff 계산 )이 작동하지 않습니다.

반투명 상태 표시 줄을 사용하는 경우 activityRootView.getHeight()소프트 키보드가 보이는 날씨를 변경하지 않습니다. 항상 활동 높이와 상태 표시 줄을 반환합니다.

예를 들어 Nexus 4, Android 5.0.1 android:windowTranslucentStatus은 true로 설정 하면 심지어 1ime을 열었을 수도 있습니다. android:windowTranslucentStatusfalse로 설정 하면 높이를 올바르게 반환하고 ime가 보이지 않으면 1134를 반환합니다 (상태 표시 줄을 포함하지 않음). ime을 닫으면 5xx를 반환합니다 (ime의 높이에 따라 다름)

나는 이것이 버그인지 날씨를 모른다. 나는 4.4.4와 5.0.1에서 시도했지만 결과는 동일하다.

따라서 지금까지 두 번째로 가장 합의 된 답변 인 Kachi의 솔루션은 ime 높이를 가장 안정시키는 가장 안전한 방법이 될 것입니다. 사본은 다음과 같습니다.

final View activityRootView = findViewById(R.id.activityRoot);
activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(new        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);

int heightDiff = activityRootView.getRootView().getHeight() - (r.bottom - r.top);
if (heightDiff > 100) { // if more than 100 pixels, its probably a keyboard...
    ... do something here
    }
 }
}); 

0

방법 LayoutListener이 필요하지 않습니다

필자의 경우 Fragment를 교체하기 전에 키보드 상태를 저장하고 싶습니다. hideSoftInputFromWindow from 메소드를 호출하여 onSaveInstanceState키보드를 닫고 키보드의 표시 여부를 반환합니다.

이 방법은 간단하지만 키보드 상태가 변경 될 수 있습니다.


0

나는 이것이 오래된 게시물이라는 것을 알고 있지만 이것이 내가 아는 가장 간단한 접근법이며 내 테스트 장치는 Nexus 5라고 생각합니다. 다른 장치에서는 시도하지 않았습니다. 내 코드가 좋지 않다면 다른 사람들이 접근 방식을 공유하기를 바랍니다. :)

public static boolean isKeyboardShown(Context context, View view) {
        if (context == null || view == null) {
            return false;
        }
        InputMethodManager imm = (InputMethodManager) context
                .getSystemService(Context.INPUT_METHOD_SERVICE);
        return imm.hideSoftInputFromWindow(view.getWindowToken(), 0); 
}

imm.hideSoftInputFromWindow는 부울을 반환합니다.

감사,


0
if (keyopen())
{
                InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
                imm.toggleSoftInput(InputMethodManager.HIDE_IMPLICIT_ONLY,0);            
}

위의 기능은 키보드가 보이는지 확인하는 데 사용합니다. 그렇다면 닫습니다.

아래는 필요한 두 가지 방법을 보여줍니다.

먼저 onCreate에서 실행 가능한 창 높이를 정의하십시오.

protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

//  add to onCreate method
    Rect rectgle= new Rect();
    Window window= getWindow();
    window.getDecorView().getWindowVisibleDisplayFrame(rectgle);
    sheight= rectgle.bottom;
//

} 

그런 다음 해당 인스턴스에서 Window 높이를 가져 오는 부울 메소드를 추가하십시오. 원본과 일치하지 않으면 (길을 따라 변경하지 않는다고 가정) 키보드가 열려 있습니다.

public boolean keyopen()
{
    Rect rectgle= new Rect();
    Window window= getWindow();
    window.getDecorView().getWindowVisibleDisplayFrame(rectgle);
    int curheight= rectgle.bottom;

    if (curheight!=sheight)
    {
        return true;
    }
    else
    {
        return false;
    }
}

프로 츠!


0

키보드가 숨겨져 있는지 여부를 얼마나 정확하게 결정할 수 있는지 알고 있습니다.

public int getStatusBarHeight() {
    int result = 0;
    int resourceId = getResources().getIdentifier("status_bar_height", "dimen", "android");
    if (resourceId > 0) {
        result = getResources().getDimensionPixelSize(resourceId);
    }
    return result;
}

public int getNavigationBarHeight() {
    int result = 0;
    int resourceId = getResources().getIdentifier("navigation_bar_height", "dimen", "android");
    if (resourceId > 0) {
        result = getResources().getDimensionPixelSize(resourceId);
    }
    return result;
}

public boolean isKeyboardHidden() {
    int delta = mRootView.getRootView().getHeight() - mRootView.getHeight() - getNavigationBarHeight() - getStatusBarHeight()
            - getSupportActionBar().getHeight();
    return delta <= 0;
}

이것은 태블릿에서 작동합니다. 탐색 표시 줄이 가로로 표시되는 경우

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.