Superdry Memorandom :-p

旧「superdry memorandum :-D」です

ソフトキーボードの表示を検知してレイアウトを変更する

入力時にはEditTextにフォーカスがあたりソフトキーボードが起動しますが、その挙動に影響されて、以下のようにEditTextがつぶれて使いづらくなることがあります。意外とリリースされてるソフトでも結構見かけます。
 こういうレイアウトで、
 文字入力しようとすると…あらら
 変換候補が表示されたあかつきには…ぐぬぬ

対処方法

このような場合、色々な対処法がありますが、ソフトキーボードの表示/非表示を検知してレイアウトを変えるというのも一つの手です。現状ソフトキーボードの表示/非表示を検知するようなAPIはありません。一般的にはカスタムViewを作成しその縦幅からソフトキーボードの表示/非表示を判断し、レイアウトを変更するのが通例のようです。意外と簡単なのでココにメモします。

構成

  • /src/org/superdry/sample/gui/DetectableSoftKeyLayout.java
    • カスタムレイアウトクラス
  • /src/org/superdry/sample/gui/MainActivity.java
    • 起動Activity
  • /res/layout/main.xml
    • MainActivityのレイアウトxml
  • AndroidManifest.xml
DetectableSoftKeyLayout.java

今回はLinearLayoutを拡張し、カスタムレイアウトを作成しました。このレイアウトの縦幅を監視するリスナーを実装します。ここでは例として、ソフトキーボードの表示によってLinearLayoutの縦幅100px縮んだ場合、ソフトキーボードが表示されているとして、onSoftKeyShownにtrueをセットしてます。

package org.superdry.sample.gui;

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

public class DetectableSoftKeyLayout extends LinearLayout {

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

	public interface OnSoftKeyShownListener {
		public void onSoftKeyShown(boolean isShown);
	}

	private OnSoftKeyShownListener listener;

	public void setListener(OnSoftKeyShownListener listener) {
		this.listener = listener;
	}

	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		// (a)Viewの高さ
		int viewHeight = MeasureSpec.getSize(heightMeasureSpec);
		// (b)ステータスバーの高さ
		Activity activity = (Activity) getContext();
		Rect rect = new Rect();
		activity.getWindow().getDecorView().getWindowVisibleDisplayFrame(rect);
		int statusBarHeight = rect.top;
		// (c)ディスプレイサイズ
		int screenHeight = activity.getWindowManager().getDefaultDisplay()
				.getHeight();
		// (a)-(b)-(c)>100ピクセルとなったらソフトキーボードが表示されてると判断
		//(ソフトキーボードはどんなものでも最低100ピクセルあると仮定)
		int diff = (screenHeight - statusBarHeight) - viewHeight;
		if (listener != null) {
			listener.onSoftKeyShown(diff > 100);
		}
		super.onMeasure(widthMeasureSpec, heightMeasureSpec);
	}
}
MainActivity.java

ここではリスナーの登録と、ソフトキーボードが表示の場合/非表示の場合の処理を実装しています。ここでは例としてソフトキーボード表示の場合はPostボタンを非表示にしています。

package org.superdry.sample.gui;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;

import org.superdry.sample.gui.R;

public class MainActivity extends Activity {

	private Button post;
	private DetectableSoftKeyLayout layout;

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
		layout = (DetectableSoftKeyLayout) findViewById(R.id.detectable_layout);
		layout.setListener(listner);
		post = (Button) findViewById(R.id.post_btn);

	}

	DetectableSoftKeyLayout.OnSoftKeyShownListener listner = new DetectableSoftKeyLayout.OnSoftKeyShownListener() {
		@Override
		public void onSoftKeyShown(boolean isShown) {
			if (isShown) {
				// ソフトキーボードが表示されている場合
				// postボタンを非表示にする
				post.setVisibility(View.GONE);
			} else {
				// ソフトキーボードが表示されてなければ、表示する
				post.setVisibility(View.VISIBLE);
			}
		}
	};
}
main.xml

MainActivityのレイアウトファイルです。

<?xml version="1.0" encoding="utf-8"?>
<org.superdry.sample.gui.DetectableSoftKeyLayout
	xmlns:android="http://schemas.android.com/apk/res/android"
	android:layout_width="fill_parent" android:orientation="vertical"
	android:layout_height="match_parent" android:id="@+id/detectable_layout">
	<TextView android:id="@+id/title" android:layout_width="wrap_content"
		android:layout_height="wrap_content" android:text="Input Message"
		android:textSize="20dp"/>
	<EditText android:id="@+id/edittext" android:inputType="textMultiLine"
		android:layout_height="match_parent" android:layout_width="match_parent"
		android:layout_weight="1">
	</EditText>
	<Button android:text="Post" android:id="@+id/post_btn"
		android:layout_width="wrap_content" android:layout_height="wrap_content" >
		<requestFocus /></Button>
</org.superdry.sample.gui.DetectableSoftKeyLayout>
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="org.superdry.sample.gui"
      android:versionCode="1"
      android:versionName="1.0">
    <uses-sdk android:minSdkVersion="4" />

    <application android:icon="@drawable/icon" android:label="@string/app_name">
        <activity android:name="org.superdry.sample.gui.MainActivity"
                  android:label="@string/app_name" android:windowSoftInputMode="adjustResize">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

    </application>
</manifest>

対応後

こんな挙動になります。EditTextがつぶれなくなった!わーい!