English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
前書き
開発を続けるうちに、必ずどんな罠にも遭遇するでしょう。
そして、Android開発において「ソフトキーボードが入力フィールドを覆う」という罠は、長期間続く大きな罠です。来て、じっくり見てみましょう。
入門編
最も基本的な状況、図のように:ページの下部にEditTextがあります。何も処理しない場合、ソフトキーボードが弹出するとEditTextが隠れることがあります。
この状況の処理は非常に簡単で、AndroidManifestファイル内でactivityの設定にandroid:windowSoftInputModeの値をadjustPanまたはadjustResizeに設定するだけで良いです。こんな感じです:
<activity> android:name=".MainActivity" android:windowSoftInputMode="adjustPan" > ... </activity>
一般的には、これらは問題を解決できますが、adjustPanとadjustResizeの効果は少し異なります。
adjustPanは整个インターフェースを上にスライドさせ、入力フィールドを露出させ、インターフェースのレイアウトを変更しません;
adjustResizeは弹出したソフトキーボード後のインターフェースのサイズを再計算し、より少ないインターフェースエリアを使用してコンテンツを表示します。入力フィールドは自然とその中に含まれます。
↑↑↑ OK、これは入門レベルです。基本的には地球上のすべてのAndroidエンジニアが解決できます。
急がないで、以下を見てください~
WebViewと一緒に試してみてください?穴が現れます……
上の入門編では、ソフトキーボードは原生のEditTextによって弹出されます。そしてH5、HybridがAppの標準的な構成になっているとき、私たちがよく遭遇する状況は、ソフトキーボードがWebView内のウェブ要素によってトリガーされ弹出されることです。
状況説明
この時点で、状況は複雑になります:
まず、ページがフルスクリーンモードでない場合、activityにadjustPanを設定すると無効になります。
次に、ページがフルスクリーンモードの場合、adjustPanとadjustResizeは無効になります。
——説明しますと、ここでのフルスクリーンモードは、ページがフルスクリーンであることを意味し、ApplicationやactivityがFullscreenテーマを使用し、『状態色着色』、『沉浸式ステータスバー』、『Immersive Mode』などを使用する場合も含まれます——つまり、Appがステータスバーの制御を自分で取り扱う場合、基本的にこのような問題が発生します。
以下のテーブルは具体的な状況を簡単にリストアップできます。
なぜそれが穴だと言うのですか?issue 5497
上のテーブルのこの状況はGoogleが望んでいたものではありません。理想的な状況は、すべてが正常に機能するべきです——したがって、これは実際にはAndroidシステム自体のバグです。
なぜ記事の冒頭でこれは穴だと言ったのでしょうか?
——このバグはAndroid1.x時代(2009年)に報告され、今のAndroid7.0(2016年)の現在でも未修正です……/(^v^)/
これはただの穴ではなく、公式が掘った穴です~
issue 5497詳細は以下のリンクをクリックしてください。9758; Issue 5497 - android -WebView adjustResize windowSoftInputModeがアクティビティが全画面モードになったときに壊れます - Android Open Source Project - Issue Tracker - Google Project Hosting
もちろん、坑が誰が掘ったかに関わらず、最終的には開発者が解決する必要があります。
坑に直面したとき、二つの方法があります:避けたり、埋めたり。
避け方
前文のように、坑が発生する条件は:WebViewを持つactivityが全画面モードまたはadjustPanモードを使用している場合です。
避け方のポーズは非常にシンプルです——
activityにWebViewがある場合、全画面モードを使用しないでください。そして、windowSoftInputModeの値をadjustResizeに設定します。
どうですか、簡単ですか?
しかし、全画面モードとWebViewを同時に必要な時は、避けられない場合もあります。その場合、避け方は使えません。新しい避け方を探さなければなりません。幸運にも、開発者の知恵は無限です。この坑がこんなに長い間存在する中で、いくつかの解決策が見つかっています。
AndroidBug5497Workaround
私が思う最良の解決策はこれです:AndroidBug5497Workaround、そして魔法のAndroidBugが必要です。5497Workaroundクラス。
名前からもわかるように、これは「問題」に対して特別に使われるものです:5497「問題」の解決策を使う方法も非常に簡単です:
AndroidBug5497Workaroundクラスをプロジェクトにコピーしてください。
活動が必要なactivityのonCreateメソッドにAndroidBugを追加してください。5497Workaround.assistActivity(this)だけでできます。
テストの結果、基本的にすべてのAndroidバージョンで使用可能で、adjustResizeに設定した場合と効果がほぼ同じです。
対比図をご覧ください:
私たちのAppの某个使用WebViewの全画面モードActivityページ、左から右に順に:ソフトキーボードがないスタイル、ソフトキーボードが入力フィールドを覆う効果、そしてAndroidBugを使用しています。5497Workaroundの最終効果です。
その原理は何でしょうか?
この派手なAndroidBug5497Workaroundクラスは特に複雑ではなく、数十行のコードしかありません。まずここに貼り付けます:
public class AndroidBug5497Workaround { // さらに情報が必要な場合は、https:をご覧ください。//code.google.com/p/android/issues/detail?id=5497 // このクラスを使用するには、content viewが設定されているActivityでassistActivity()を呼び出すだけです。 public static void assistActivity (Activity activity) { new AndroidBug5497Workaround(activity); } private View mChildOfContent; private int usableHeightPrevious; private FrameLayout.LayoutParams frameLayoutParams; private AndroidBug5497Workaround(Activity activity) { FrameLayout content = (FrameLayout) activity.findViewById(android.R.id.content); mChildOfContent = content.getChildAt(0); mChildOfContent.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { public void onGlobalLayout() { possiblyResizeChildOfContent(); } }); frameLayoutParams = (FrameLayout.LayoutParams) mChildOfContent.getLayoutParams(); } private void possiblyResizeChildOfContent() { int usableHeightNow = computeUsableHeight(); if (usableHeightNow != usableHeightPrevious) { int usableHeightSansKeyboard = mChildOfContent.getRootView().getHeight(); int heightDifference = usableHeightSansKeyboard - usableHeightNow; if (heightDifference > (usableHeightSansKeyboard/4)) { // キーボードが表示されたかもしれません frameLayoutParams.height = usableHeightSansKeyboard - heightDifference; } // キーボードが隠れてしまったかもしれません frameLayoutParams.height = usableHeightSansKeyboard; } mChildOfContent.requestLayout(); usableHeightPrevious = usableHeightNow; } } private int computeUsableHeight() { Rect r = new Rect(); mChildOfContent.getWindowVisibleDisplayFrame(r); return (r.bottom - r.top);// フルスクリーンモードでは:return r.bottom } }
コードは基本的に以下のようなことを行っています:
1.activityの根Viewを探します
エントリーポイントのコードを見てみましょう:
FrameLayout content = (FrameLayout) activity.findViewById(android.R.id.content); mChildOfContent = content.getChildAt(0);
その中で、第一行のandroid.R.id.contentで指定されたViewは、AndroidのすべてのActivity画面で開発者が制御できる根Viewです。
Activityがフルスクリーンモードの場合、android.R.id.contentは全画面領域を占めます。
Activityが通常の非フルスクリーンモードの場合、android.R.id.contentはステータスバーを除くすべての領域を占めます。
他の状況、例えばActivityがポップアップ、または7.0以降の分割スクリーンスタイルなど、android.R.id.contentはポップアップの範囲または分割スクリーンの半分に該当します-これらの状況は少ないので、ここでは考慮しません。
私たちがよく使うsetContentView(View view)/setContent(int layRes)は、指定されたViewまたはlayResをandroid.R.id.contentに配置し、その子Viewにします。
したがって、次の行content.getChildAt(0)で取得されるmChildOfContentは、実際にはsetContentViewで入れたViewを取得するために使用されています。
2.Viewツリーの変更を監視するリスナーを設定します
mChildOfContent.getViewTreeObserver().addOnGlobalLayoutListener({ //書き方が簡略化されました possiblyResizeChildOfContent(); });
View.getViewTreeObserver()はViewTreeObserverオブジェクトを取得できます-このオブジェクトは観察者で、現在のViewツリーに発生する一部の変更を監視するために特別に設けられています。ここで登録されているaddOnGlobalLayoutListenerは、現在のViewツリーの全体的なレイアウト(GlobalLayout)が変更されたり、その中のViewの可視状態が変更された場合に通知コールバックを実行します。
-「ソフトウェアキーボードが弹出された」は、このイベントをトリガーするソースの1つです。(ソフトウェアキーボードの弹出によりGlobalLayoutが変更されます)
つまり、今や「ソフトウェアキーボードが弹出された」イベントを監視することができます。
3.インターフェースが変更された後、「利用可能な高さ」を取得します。
ソフトウェアキーボードが弹出された後、次にやることは、変更後のインターフェースの利用可能な高さ(開発者がコンテンツを表示する高さとして使用できます)を取得することです。
直接看代码:
private int computeUsableHeight() { Rect rect = new Rect(); mChildOfContent.getWindowVisibleDisplayFrame(rect); // rect.topは、ステータスバーの高さです。フルスクリーントピックには、直接 return rect.bottom だけで十分です}} return (rect.bottom - rect.top); }
View.getWindowVisibleDisplayFrame(Rect rect)という行のコードは、取得できるRectが-図に示されるように、タイトルバーを除いた、ソフトウェアキーボードで隠された部分を除いた、残りの矩形領域です。
Rect領域の図
以下も分かります:
rect.topの値は、タイトルバーの高さです。(実際には、これもタイトルバーの高さを取得する方法としてよく使用されます)
スクリーン高さ-rect.bottomは、キーボードの高さです。(キーボードの高さを取得する方法も登場しました)
この時、以下があります:
フルスクリーンモードでは、利用可能な高さ = rect.bottom
フルスクリーンモードではありません、利用可能な高さ = rect.bottom - rect.top
4.最後に、高さをリセット
計算した利用可能な高さは、現在視覚的に見えるUIの高さです。しかし、現在のUIの実際の高さは、利用可能な高さよりもキーボードの距離が一つあります。
最後に、UIの高さを利用可能な高さに設定する-完璧に成功しました。
private void possiblyResizeChildOfContent() { int usableHeightNow = computeUsableHeight(); if (usableHeightNow != usableHeightPrevious) { int usableHeightSansKeyboard = mChildOfContent.getRootView().getHeight(); int heightDifference = usableHeightSansKeyboard - usableHeightNow; if (heightDifference > (usableHeightSansKeyboard/4)) { // キーボードが表示されたかもしれません frameLayoutParams.height = usableHeightSansKeyboard - heightDifference; } // キーボードが隠れてしまったかもしれません frameLayoutParams.height = usableHeightSansKeyboard; } mChildOfContent.requestLayout(); usableHeightPrevious = usableHeightNow; } }
上記のコードには、"heightDifference > (usableHeightSansKeyboard"が追加されました。/4の判断を行います。これは、不要な干渉を除去するためです。OnGlobalLayoutイベントがトリガーされる原因は非常に多く、単にソフトキーボードの弹出の変化ではなく、さまざまな子Viewの隠し表示の変化なども含まれます。これらは、画面高さへの影響が限られています。この判断を追加することで、画面の高さが変化するときにのみ、画面の高さが変更されるため、干渉を最小限に抑えます。1/4のスクリーン高さが変更されると、高さの再設定が行われます。これにより、コードがソフトキーボードの弹出にのみ反応するようになります。
要約
要約すると、以下の通りです:
通常のActivity(WebViewなし)、adjustpanまたはadjustResizeを直接使用します。
WebViewが含まれている場合:
a) フルスクリーンモードでない場合、adjustResizeを使用できます。
b) フルスクリーンモードの場合、AndroidBugを使用します。5497Workaroundを用いて処理します。
以上は編集者が皆さんに紹介したAndroidソフトキーボードが入力フィールドを遮る究極の解決策です。皆さんに役立つことを願っています。何か疑問があれば、コメントを残してください。編集者はすぐに回答します。また、このサイトへのサポートに感謝しています。
声明:本文の内容はインターネットから取得しており、著作権者はすべての権利を有しています。インターネットユーザーが自発的に貢献し、自己でアップロードしたものであり、本サイトは所有権を持ちません。また、人工編集は行われておらず、関連する法的責任も負いません。著作権に疑問がある場合は、メールを送信してください:notice#oldtoolbag.com(メール送信時は、#を@に変更してください。報告を行い、関連する証拠を提供してください。一旦確認されると、本サイトは侵害された内容をすぐに削除します。)