ソフトウェアキーボードでレイアウトを変える

ちょっとめんどくさかったので覚書

やりたいこと

画面の下に常にボタンを表示なければならないのだが、画面には入力ボタンがあり画面全体の高さは変更してはいけない。高さを変更できないのでScrollViewは使えないので、FrameLayoutで全体を括ってソフトウェアキーボードが表示されたタイミングでボタンの位置をずらして画面下に表示しなければならない。

コード

frmWrapper.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
    @Override
    public void onGlobalLayout() {
        Rect r = new Rect();
        frmWrapper.getWindowVisibleDisplayFrame(r);
        int heightDiff = frmWrapper.getRootView().getHeight() - (r.bottom - r.top);
        lnrButtonWrapper.layout(
                lnrButtonWrapper.getLeft(),
                r.bottom - r.top - getSupportActionBar().getHeight() - lnrButtonWrapper.getHeight(),
                lnrButtonWrapper.getRight(),
                r.bottom - r.top - getSupportActionBar().getHeight());
    }
});

解説

全体を括っているFrameLayoutにLayoutListenerを追加する。こうすることで、ソフトウェアキーボードが表示されるとFrameLayoutの高さが変更されるのでこの中に入ってくる。getRootViewの高さを取得すると画面全体の高さが取得できるので、そこからアクションバー等の高さを引いてあげることで画面下にボタンが入っているLinearLayoutを表示することが出来る。

まとめ

ソフトウェアキーボードの表示、非表示を取得したいだけであれば、画面の高さから全体を括っている高さを引いた値が100を超えていたらソフトウェアキーボードが表示されていると判断していいらしい。Viewを拡張してonMeasureをOverrideする例が引っかかるけどそのためだけにカスタムビューを作るのは非常に面倒なのでこっちのほうがらくだと思う。

おまけ

出てるか出てないかの取得はこうやる

    DisplayMetrics displayMetrics = new DisplayMetrics();
    getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
    windowHeight = displayMetrics.heightPixels - getSupportActionBar().getHeight();
    lnrWrapper.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
        @Override
        public void onGlobalLayout() {
            Rect r = new Rect();
            lnrWrapper.getWindowVisibleDisplayFrame(r);
            if ((windowHeight - (r.bottom - r.top)) > 100) {
                // ソフトウェアキーボード出てる
            } else {
                // ソフトウェアキーボード出てない
            }
        }
    });

参考URL