記事ディレクトリ
序文
インターネット上の多くの記事を読んだところ、windowSoftInputMode のいくつかのモードの機能があまり正確に説明されておらず、ほとんどの記事は原理を分析するためのソース コードの詳細な分析を行わずに、使い方を分析するだけでした。今日この記事では、Android ソフト キーボード windowSoftInputMode の各モードの機能と原理を分析します。
1. モデルの紹介
android:windowSoftInputMode=["stateUnspecified",
"stateUnchanged", "stateHidden",
"stateAlwaysHidden", "stateVisible",
"stateAlwaysVisible", "adjustUnspecified",
"adjustResize", "adjustPan"]
上記のように、windowSoftInputMode には6 つの stateXXX値と3 つのAdjustXXX値があります。簡単に言うと、state という接頭辞が付いた値は、アクティビティがユーザーのフォーカスになったときのソフトウェア入力メソッドの可視性を制御し、adjust という接頭辞が付いた接頭辞は、ウィンドウ内での表示方法を制御します。
両方の値を組み合わせて使用できます。
表示方法には非表示と表示の 2 種類が
あり、ソフト ウィンドウが表示されたときにコンテンツ部分を縮小するか、フォーカス コントロールが表示ウィンドウ内に収まるようにコンテンツの位置を調整する 2 つの表示方法があります。
各値の意味は以下の通り(公式)
価値 | 説明する |
---|---|
「stateUnspecified 」 |
ソフト キーボードの状態 (非表示または表示) は指定されません。適切な状態を選択するか、テーマの設定に依存するかはシステム次第です。 これは、ソフト キーボードの動作のデフォルト設定です。 |
「stateUnchanged 」 |
アクティビティが表示または非表示にされたときに、ソフト キーボードが最後にあった状態を保持します。 |
「stateHidden 」 |
ユーザーがアクティビティを選択するとき、つまりユーザーが別のアクティビティから戻るのではなく、実際にアクティビティに進むとき、ソフト キーボードは非表示になります。 |
「stateAlwaysHidden 」 |
アクティビティのメイン ウィンドウに入力フォーカスがあるときは、常にソフト キーボードを非表示にします。 |
「stateVisible 」 |
ユーザーがアクティビティを選択するとき、つまりユーザーが別のアクティビティから戻るのではなく、実際にアクティビティに進むとき、ソフト キーボードが表示されます。 |
「stateAlwaysVisible 」 |
アクティビティのメイン ウィンドウに入力フォーカスがある場合は、常にソフト キーボードを表示します。 |
「adjustUnspecified 」 |
アクティビティのメイン ウィンドウのサイズを変更してソフト キーボード用のスペースを確保するかどうか、またはウィンドウの内容をパンして画面上の現在のフォーカスを表示するかどうかは指定しません。システムは、ウィンドウのコンテンツにコンテンツをスクロールできるレイアウト ビューがあるかどうかに基づいて、これらのモードの 1 つを自動的に選択します。このようなビューが存在する場合、スクロールすることでウィンドウのすべての内容がより狭い領域内に表示される場合、ウィンドウのサイズが変更されます。 これは、メイン ウィンドウの動作のデフォルト設定です。 |
「adjustResize 」 |
常にアクティビティのメイン ウィンドウのサイズを変更して、画面上のソフト キーボード用のスペースを確保してください。 |
「adjustPan 」 |
アクティビティのメイン ウィンドウのサイズを調整してソフト キーボード用のスペースを確保するのではなく、ウィンドウのコンテンツが自動的にパンされ、現在のフォーカスがキーボードで覆われないようにするため、ユーザーは入力したコンテンツを常に確認できます。ユーザーはウィンドウの覆われた部分にアクセスしたり操作したりするためにソフト キーボードを閉じる必要がある場合があるため、これは一般にサイズ変更ほど望ましくありません。 |
2. 可視性の利用
上記の説明だけでは各属性の具体的な役割がよくわかりませんので、それぞれの属性の役割を一つずつ詳しく紹介していきます。
表示しやすいように、InputActivity1 と InputActivity2 の 2 つのアクティビティを作成し、操作用のプロパティを設定します。
1.状態は変化なし
アクティビティが表示または非表示にされたときに、ソフト キーボードが最後にあった状態を保持します。
この文をどう理解すればいいでしょうか?両方のアクティビティの windowSoftInputMode を stateUnchanged に設定します。次に、以下の操作例を見てください。
現象を説明すると、まず、InputActivity1に入るとソフトキーボードが表示されず、InputActivity1でソフトキーボードを吐き、InputActivity2にジャンプすると、InputActivity2にもソフトキーボードが表示されることがわかります。引き続き、次の操作例を確認してください。
InputActivity1ではソフトキーボードが呼び出されず、InputActivity2にジャンプしますが、このときInputActivity2ではソフトキーボードが表示されていないことが分かります。これら 2 つの例を通じて、stateUnchanged はソフト キーボードの表示状態を変更しないと結論付けます。ソフト キーボードが以前に表示されていた場合は、他のアクティビティでも表示され、その逆も同様です
。
今の例はすべて別のアクティビティに進んでいますが、前のアクティビティに戻る場合はどうなるでしょうか?
まず、InputActivity1 でソフトキーボードが表示されない場合は、InputActivity2 にジャンプし、InputActivity2 で手動でソフトキーボードを起動してから、InputActivity1 に戻ると、この時点でソフトキーボードも表示されていることがわかりますので、 forward であっても、backward であっても、 stateUnchanged が適用されることがわかります。
2.状態非表示
ユーザーがアクティビティを選択するとき、つまりユーザーが別のアクティビティから戻るのではなく、実際にアクティビティに進むとき、ソフト キーボードは非表示になります。
同様に、両方のアクティビティの windowSoftInputMode を stateHidden に設定します。次に、次の操作例を見てください:
まず、InputActivity1 を入力するときにソフト キーボードは表示されず、手動でソフト キーボードを起動した後、InputActivity2 にジャンプします。ソフト キーボードは、InputActivity2 で非表示になります。これは、stateHidden が機能することを意味します。ユーザーがアクティビティに進むと、ソフト キーボードは非表示になります。
次に、InputActivity2 でソフト キーボードを呼び出して、InputActivity1 に戻りますが、この時点では、InputActivity1 のソフト キーボードが表示されていることがわかり、進行中のみ stateUnchanged が有効であることがわかります。
3.常に非表示の状態
アクティビティのメイン ウィンドウに入力フォーカスがあるときは、常にソフト キーボードを非表示にします。
ソフト キーボードも非表示になりますが、stateAlwaysHidden の修飾子は少なくなります。前述したように、stateHidden を有効にするにはアクティビティまで前方に移動する必要がありますが、stateAlwaysHidden にはこの制限がなく、前方または後方に関係なく有効になります。実際の動作を見てみますと、同様に2つのアクティビティのwindowSoftInputModeをstateAlwaysHiddenに設定しており、
上記のstateHiddenと同じ動作ですが、InputActivity2でソフトキーボードを起動してInputActivity1に戻ると、この時点でInputActivity1のソフトキーボードが非表示になっていることが分かり、順方向でも逆方向でもstateAlwaysHiddenが適用できることがわかります。
4.stateVisible
ユーザーがアクティビティを選択するとき、つまりユーザーが別のアクティビティから戻るのではなく、実際にアクティビティに進むとき、ソフト キーボードが表示されます。VisibleとHiddenは同じ意味ですが、その機能をよりわかりやすくするために、今回はInputActivity1をstateVisible、InputActivity2をstateHiddenにして、以下の操作例を見ていきます:InputActivity1に入るとソフトキーボードが自動的に表示されますInputActivity2に入るとstateHiddenが設定されているため非表示になります再度InputActivity1に戻ると表示されず、stateVisibleは先に
進んだときのみ有効であることが分かります。
Visible の 2 つのプロパティについては注意すべき点があります。Android
9.0 より上のバージョン、つまり API28 では、表示される 2 つのプロパティは無効になります。これについては、ソース コードのコメントで説明されています。
/**
* Visibility state for {@link #softInputMode}: please show the soft
* input area when normally appropriate (when the user is navigating
* forward to your window).
*
* <p>Applications that target {@link android.os.Build.VERSION_CODES#P} and later, this flag
* is ignored unless there is a focused view that returns {@code true} from
* {@link View#isInEditMode()} when the window is focused.</p>
*/
public static final int SOFT_INPUT_STATE_VISIBLE = 4;
/**
* Visibility state for {@link #softInputMode}: please always make the
* soft input area visible when this window receives input focus.
*
* <p>Applications that target {@link android.os.Build.VERSION_CODES#P} and later, this flag
* is ignored unless there is a focused view that returns {@code true} from
* {@link View#isInEditMode()} when the window is focused.</p>
*/
public static final int SOFT_INPUT_STATE_ALWAYS_VISIBLE = 5;
{@link android.os.Build.VERSION_CODES#P} 以降をターゲットとするアプリケーションでは、ウィンドウがフォーカスされているときに {@link View#isInEditMode()} から {@code true} を返すフォーカスされたビューがない限り、このフラグは無視されます。
この記事では失敗の原因を詳しく紹介しています: https://blog.csdn.net/weimingjue/article/details/99738271
簡単に言えば、Android 9.0 より前では、アクティビティの開始後に自動的にフォーカスが取得されますが、その後はフォーカスが取得されず、ソフト キーボードの表示が失敗します。解決策は、アクティビティの開始後に入力が必要なコントロールのフォーカス view.requestFocus() を手動で取得することです。
5.常に表示される状態
アクティビティのメイン ウィンドウに入力フォーカスがある場合は、常にソフト キーボードを表示します。
これについては特に説明する必要はありません。操作例を見てください。stateVisible
と比較すると、InputActivity1 に戻るときにソフト キーボードが自動的に表示されます。stateAlwaysVisible が前方でも後方でも適用できることがわかります。
6.状態未指定
ソフト キーボードの状態 (非表示または表示) は指定されません。適切な状態を選択するか、テーマの設定に依存するかはシステム次第です。
これは、ソフト キーボードの動作のデフォルト設定です。
この設定については具体的な方法を指定せずに詳しく説明しませんが (非常に複雑です)、上記の他の設定をマスターできれば、日常の開発ニーズを満たすことができるはずです。
3. 表示方法
まず簡単な分析:
ソフト キーボードが実際にはダイアログであることがわかります。キーボードがポップアップすると、実際には 2 つのウィンドウが表示されます: 1 はアクティビティのウィンドウ、2 はダイアログのウィンドウです。問題の核心は、Dialog の Window 表示が、Activity の Window 領域の一部を覆っていることです。EditText を表示するために、Activity のレイアウトが押し上げられています。Window プロパティには、押し上げられるかどうかを制御するパラメーターがあると推測されます。実際、このパラメータは WindowManager.LayoutParams.softInputMode です。
1.調整サイズ変更
常にアクティビティのメイン ウィンドウのサイズを変更して、画面上のソフト キーボード用のスペースを確保してください。
実際には、adjustResize はルート レイアウトにパディングを追加します。パディングの値はソフト キーボードの高さになります。ルート レイアウトにパディングを直接追加できますが、効果は同じです。
テストには次のレイアウト コードを使用します。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/myviewgroup"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<ImageView
android:id="@+id/iv"
android:src="@drawable/test"
android:layout_marginTop="20dp"
android:layout_marginBottom="20dp"
android:layout_width="match_parent"
android:layout_height="300dp">
</ImageView>
<EditText
android:hint="输入框2"
android:id="@+id/et2"
android:layout_gravity="bottom"
android:layout_marginTop="200dp"
android:layout_width="100dp"
android:layout_height="40dp">
</EditText>
</LinearLayout>
ActivityをadjustResizeに設定すると、実行後の効果は以下のようになります:
レイアウトが変更されておらず、入力ボックスが非表示になっていることがわかります。これは、レイアウト内のコントロールの長さが固定されており、パディングを追加しても効果がないためです。次に、レイアウトを変更し、残りの画面を埋めるために
Imageview の高さをlayout_weight=1に設定します。
修正内容は以下の通りです。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/myviewgroup"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<ImageView
android:id="@+id/iv"
android:src="@drawable/test"
android:layout_marginTop="20dp"
android:layout_marginBottom="20dp"
android:layout_weight="1"
android:layout_width="match_parent"
android:scaleType="fitXY"
android:layout_height="0dp">
</ImageView>
<EditText
android:hint="输入框2"
android:id="@+id/et2"
android:layout_width="100dp"
android:layout_height="40dp">
</EditText>
</LinearLayout>
固定長ではないレイアウト内容が押し上げられ、高さが圧縮されていることがわかります。
2.パンを調整する
アクティビティのメイン ウィンドウのサイズを調整してソフト キーボード用のスペースを確保するのではなく、ウィンドウのコンテンツが自動的にパンされ、現在のフォーカスがキーボードで覆われないようにするため、ユーザーは入力したコンテンツを常に確認できます。ユーザーはウィンドウの覆われた部分にアクセスしたり操作したりするためにソフト キーボードを閉じる必要がある場合があるため、これは一般にサイズ変更ほど望ましくありません。
ここでのエフェクトがどのような種類のレイアウトであっても、adjustPan は実際には全体としてレイアウトを上にスクロールしますが、スクロールするレイアウトは現在のフォーカスの位置に応じて決定されることに注意してください。
AdjustResizeと同じレイアウトでテストする
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/myviewgroup"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center_vertical"
android:background="@color/white"
android:orientation="vertical"
tools:context=".MainActivity">
<ImageView
android:id="@+id/iv"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:scaleType="fitXY"
android:layout_marginTop="20dp"
android:layout_marginBottom="20dp"
android:src="@drawable/test"></ImageView>
<EditText
android:id="@+id/et2"
android:layout_width="100dp"
android:layout_height="40dp"
android:layout_marginTop="100dp"
android:hint="输入框2"></EditText>
</LinearLayout>
3.未指定の調整
アクティビティのメイン ウィンドウのサイズを変更してソフト キーボード用のスペースを確保するかどうか、またはウィンドウの内容をパンして画面上の現在のフォーカスを表示するかどうかは指定しません。システムは、ウィンドウのコンテンツにコンテンツをスクロールできるレイアウト ビューがあるかどうかに基づいて、これらのモードの 1 つを自動的に選択します。このようなビューが存在する場合、スクロールすることでウィンドウのすべての内容がより狭い領域内に表示される場合、ウィンドウのサイズが変更されます。これは、メイン ウィンドウの動作のデフォルト設定です。
「adjustUnspecified」とは、実際には「adjustResize」と「adjustPan」のどちらかを選択することですが、レイアウトコンテンツにスクロール可能なレイアウトがある場合は「adjustResize」を選択し、そうでない場合は「adjustPan」を選択します。実際には、主にisScrollContainer属性によって定義されており、レイアウトの属性 isScrollContainer が true に設定されている場合、レイアウトはスクロール可能であることを意味します。
私たちが通常使用する RecyclerView と ScrollView はすべてスクロール可能なコンテンツであり、初期化時にこのプロパティを設定します。
public RecyclerView(Context context, @android.annotation.Nullable AttributeSet attrs, int defStyle) {
...
setScrollContainer(true);
...
}
通常のビューでこのプロパティを設定するには、次の 2 つの方法があります。
- コード内: View.setScrollContainer(true)
- xml里: android:isScrollContainer=“true”
上記のレイアウトでもテストしました
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/myviewgroup"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center_vertical"
android:background="@color/white"
android:orientation="vertical"
tools:context=".MainActivity">
<ImageView
android:id="@+id/iv"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:scaleType="fitXY"
android:layout_marginTop="20dp"
android:layout_marginBottom="20dp"
android:src="@drawable/test"></ImageView>
<EditText
android:id="@+id/et2"
android:layout_width="100dp"
android:layout_height="40dp"
android:layout_marginTop="100dp"
android:hint="输入框2"></EditText>
</LinearLayout>
スライド レイアウトがないため、ここでは、レイアウト全体を上に押し出すために、adjustPan が選択されていることがわかります。次に、isScrollContainer 属性を ImageView に追加します。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/myviewgroup"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center_vertical"
android:background="@color/white"
android:orientation="vertical"
tools:context=".MainActivity">
<ImageView
android:id="@+id/iv"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:scaleType="fitXY"
android:isScrollContainer="true"
android:layout_marginTop="20dp"
android:layout_marginBottom="20dp"
android:src="@drawable/test"></ImageView>
<EditText
android:id="@+id/et2"
android:layout_width="100dp"
android:layout_height="40dp"
android:layout_marginTop="100dp"
android:hint="输入框2"></EditText>
</LinearLayout>
ここでは「AdjustResize」を選択しました。
4.何も調整しない
何もしない。
要約する
上記は、ソフト キーボードのポップアップ、閉じる、表示するかどうか、ソフト キーボードの高さ、ソフト キーボード モードおよびその他の効果の分析です。
上記の効果の実現原理を理解したい場合は、次の記事、Android ソフト キーボード windowSoftInputMode の使い方と原理 (原理)を読むことができます。