안드로이드 키보드 높이 구하기 - andeuloideu kibodeu nop-i guhagi

때때로 소프트 키보드 위에 레이아웃을 올리고 싶은 경우가 있다. 대부분의 검색결과에서는 menifest 에서 해당 activity에 adjustResize를 걸면 해결된다고 설명하고 있고 실제로 어느 정도는 해결이 된다.

문제는 실제로 그러한 방식을 적용해 보면 EditText 가 아래에 위치한 상황에서 레이아웃에 따라 이러한 형태의 모양을 취하게 되는 경우가 있다는 것이다.

안드로이드 키보드 높이 구하기 - andeuloideu kibodeu nop-i guhagi
adjustResize를 적용하고 소프트 키보드가 올라왔을 때.

단순히 위의 사진으로는 어느 부분이 문제인지 감이 잘 잡히질 않는다. 그러나 실제의 레이아웃은 이러하다.

안드로이드 키보드 높이 구하기 - andeuloideu kibodeu nop-i guhagi
소프트 키보드가 올라오기 전 레이아웃.

소프트 키보드로 가려지기 전 레이아웃을 보면 EditText의 Height가 소프트 키보드에 가려져서 원하는 형태로 보여지지 않았고 또한 아래에 버튼이 존재했음을 볼 수 있다. 버튼과 EditText의 절반 이상이 가려지게 되므로 개발자 입장에서는 원하는 방식이 아니거나 이쁘지 않다고 느낄 수 있다. 또한 사용자 입장에서는 버튼을 누르기 위해 키보드를 내리는 귀찮음을 감수해야 한다.

 

<LinearLayout>
- <LinearLayout>
- <NestedScrollView>
-- <LinearLayout>
--- <LinearLayout>
--- <EditText>
--- <LinearLayout>
--- <LinearLayout>
---- <Button>
---- <Button>

 

현재의 레이아웃은 다음과 같은 형태이다. 저 상황에서 중점적으로 보아야 할 것은 NestedScrollView [NestedScrollView 가 아니더라도 ScrollView 형태의 View라면 상관없다] 안에 EditText가 있으며 그 아래 LinearLayout이 존재한다는 것이다.

소프트 키보드와 현재 사용하고자 하는 EditText의 Focus가 겹치게 되면 정확히 커서만큼의 공간만 키보드 위로 올라오고 나머지는 전부 키보드에 의해 가려지기 때문에 원하는 Layout이 전부 보여지려면 키보드의 높이만큼 전체 높이를 올려주면 되지 않을까? 하는 생각이 들게 된다. 그러려면 키보드의 크기를 일단 알아야 하는데 소프트 키보드의 높이를 가져다주는 api 는 제공해주지 않는다. 그러므로 간접적으로 키보드의 크기를 알아야 한다.

 

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

    public void onGlobalLayout() {

        //뷰가 불러지고 나서 처리할 코드 입력

        Rect r = new Rect(); // 키보드 위로 보여지는 공간
        rootView.getWindowVisibleDisplayFrame(r);
        int screenHeight = rootView.getRootView().getHeight(); // rootView에 대한 전체 높이

 

        // 키보드가 보여지면 현재 보여지는 rootView의 범위가 전체 rootView의 범위보다 작아지므로 전체 크기에서 현재 보여지는 크기를 빼면 키보드의 크기가 됨.

        int keypadHeight = screenHeight - r.bottom;

 

    }

 

키보드의 크기를 알아내면 해당 크기만큼 레이아웃들의 위치를 위로 올려 보여지길 원하는 레이아웃들이 키보드의 최상단 위치보다 높게 만들어야 한다. 여기서 기본적으로 떠오르게 되는 방법은 두가지이다. margin과 padding.

해당 높이만큼 레이아웃의 위치를 키보드 위로 올리면 되는 것이고, 그렇다면 원하는 레이아웃에 margin 또는 padding을 주어 레이아웃을 그만큼 위로 올리면 되지 않을까? 하는 생각이 가능하다. 

 

그렇다면 어느 것이 맞는가에 대해 테스트를 해보면 되는데 정답부터 말하자면 editText가 포함된 Layout에 Padding을 하는 방법이 옳다. 직접 테스트를 해보기를 권하며 사진을 올리기에는 부적절하기 때문에 말로 설명하자면 margin을 넣게 되면 margin을 넣음과 동시에 rootView의 레이아웃이 변경되는 것이기 때문에 rootView의 내부 Layout들이 다시 자리를 잡는 변경 과정이 잠깐동안 눈에 보여진다. 그러나 padidng을 넣게 되면 레이아웃이 변경되는 것은 아니고 위로 밀어 올리는 것이기 때문에 그 움직임이 키보드에 가려져서 보이지를 않는다.

 

 

그리하여 키보드만큼 화면을 밀어올리는 방법은 다음과 같다.

 

// 필요 전역 변수

 

boolean isKeyboardShowing = false; 
int keypadBaseHeight = 0;

 

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

    public void onGlobalLayout() {

        //뷰가 불러지고 나서 처리할 코드 입력

        Rect r = new Rect(); // 키보드 위로 보여지는 공간
        rootView.getWindowVisibleDisplayFrame(r);
        int screenHeight = rootView.getRootView().getHeight(); // rootView에 대한 전체 높이

 

        // 키보드가 보여지면 현재 보여지는 rootView의 범위가 전체 rootView의 범위보다 작아지므로 전체 크기에서 현재 보여지는 크기를 빼면 키보드의 크기가 됨.

        int keypadHeight = screenHeight - r.bottom;

 

        if (keypadBaseHeight == 0) { // 기기마다 소프트 키보드가 구현되는 방식이 다름. 화면 아래에 숨어있거나 invisible로 구현되어 있음. 그차이로 인해 기기마다 약간씩 레이아웃이 틀어지는데 그것을 방지하기 위해 필요함.
            keypadBaseHeight = keypadHeight;
        }


        if (keypadHeight > screenHeight * 0.15) { // 키보드가 대략 전체 화면의 15% 정도 높이 이상으로 올라온다.
        // 키보드 열렸을 때
           if (!isKeyboardShowing) {
                isKeyboardShowing = true;
                //params.height = keypadHeight;
                //onKeyboardVisibilityChanged(true);

                rootView.setPadding(0, 0, 0, keypadHeight);
                int height = keypadHeight - keypadBaseHeight;

            }
        } else {
            // 키보드가 닫혔을 때
            if (isKeyboardShowing) {
                isKeyboardShowing = false;
                rootView.setPadding(0, 0, 0, 0);
            }
        }

    }

 

java단에서 화면 단위는 dp, dpi가 아닌 px로 통일되고, 콘솔에 찍어보면 알겠지만 구해지는 모든 height는 px로 return되기 때문에 setpadding에 단위를 변경할 필요 없이 그대로 값을 넣어주면 된다.