私はソフトキーボードがアクティブなときにレイアウトを調整するために多くのことを研究し、正常に実装しましたが、マニフェストファイルのアクティビティタグでAndroid:theme="@Android:style/Theme.NoTitleBar.Fullscreen"
thisを使用すると問題が発生します。
このために、異なるオプションを使用してAndroid:windowSoftInputMode="adjustPan|adjustResize|stateHidden"
を使用しましたが、運はありません。
その後、プログラムでFullScreen
を実装し、FullScreen
で動作するさまざまなレイアウトを試しましたが、すべて無駄でした。
私はこれらのリンクを参照し、この問題に関連する多くの投稿を見ました。
http://Android-developers.blogspot.com/2009/04/updating-applications-for-on-screen.html
http://davidwparker.com/2011/08/30/Android-how-to-float-a-row-above-keyboard/
XMLコードは次のとおりです。
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout Android:id="@+id/masterContainerView"
Android:layout_width="fill_parent" Android:layout_height="fill_parent"
Android:orientation="vertical" xmlns:Android="http://schemas.Android.com/apk/res/Android"
Android:background="#ffffff">
<ScrollView Android:id="@+id/parentScrollView"
Android:layout_width="fill_parent" Android:layout_height="wrap_content">
<LinearLayout Android:layout_width="fill_parent"
Android:layout_height="fill_parent" Android:orientation="vertical">
<TextView Android:id="@+id/setup_txt" Android:layout_width="wrap_content"
Android:layout_height="wrap_content" Android:text="Setup - Step 1 of 3"
Android:textColor="@color/top_header_txt_color" Android:textSize="20dp"
Android:padding="8dp" Android:gravity="center_horizontal" />
<TextView Android:id="@+id/txt_header" Android:layout_width="fill_parent"
Android:layout_height="40dp" Android:text="AutoReply:"
Android:textColor="@color/top_header_txt_color" Android:textSize="14dp"
Android:textStyle="bold" Android:padding="10dp"
Android:layout_below="@+id/setup_txt" />
<EditText Android:id="@+id/edit_message"
Android:layout_width="fill_parent" Android:layout_height="wrap_content"
Android:text="Some text here." Android:textSize="16dp"
Android:textColor="@color/setting_editmsg_color" Android:padding="10dp"
Android:minLines="5" Android:maxLines="6" Android:layout_below="@+id/txt_header"
Android:gravity="top" Android:scrollbars="vertical"
Android:maxLength="132" />
<ImageView Android:id="@+id/image_bottom"
Android:layout_width="fill_parent" Android:layout_height="wrap_content"
Android:layout_below="@+id/edit_message" />
</LinearLayout>
</ScrollView>
<RelativeLayout Android:id="@+id/scoringContainerView"
Android:layout_width="fill_parent" Android:layout_height="50px"
Android:orientation="vertical" Android:layout_alignParentBottom="true"
Android:background="#535254">
<Button Android:id="@+id/btn_save" Android:layout_width="wrap_content"
Android:layout_height="wrap_content" Android:layout_alignParentRight="true"
Android:layout_marginTop="7dp" Android:layout_marginRight="15dp"
Android:layout_below="@+id/edit_message"
Android:text = "Save" />
<Button Android:id="@+id/btn_cancel" Android:layout_width="wrap_content"
Android:layout_height="wrap_content" Android:layout_marginTop="7dp"
Android:layout_marginRight="10dp" Android:layout_below="@+id/edit_message"
Android:layout_toLeftOf="@+id/btn_save" Android:text = "Cancel" />
</RelativeLayout>
</RelativeLayout>
ソフトキーボードが表示されたら、下の2つのボタンを上に移動する必要があります。
-> https://groups.google.com/group/Android-developers/msg/5690ac3a9819a53b?pli=1
->フルスクリーンモードはサイズを変更しません
Yghmの回避策に基づいて、ワンクラスで問題を解決できる便利なクラスをコーディングしました(もちろん、新しいクラスをソースコードに追加した後)。ワンライナーは次のとおりです。
AndroidBug5497Workaround.assistActivity(this);
実装クラスは次のとおりです。
public class AndroidBug5497Workaround {
// For more information, see https://issuetracker.google.com/issues/36911528
// To use this class, simply invoke assistActivity() on an Activity that already has its content view set.
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)) {
// keyboard probably just became visible
frameLayoutParams.height = usableHeightSansKeyboard - heightDifference;
} else {
// keyboard probably just became hidden
frameLayoutParams.height = usableHeightSansKeyboard;
}
mChildOfContent.requestLayout();
usableHeightPrevious = usableHeightNow;
}
}
private int computeUsableHeight() {
Rect r = new Rect();
mChildOfContent.getWindowVisibleDisplayFrame(r);
return (r.bottom - r.top);
}
}
これが誰かを助けることを願っています。
答えはすでに選ばれており、問題はバグであることがわかっているので、「可能性のある回避策」を追加すると思いました。
ソフトキーボードが表示されているときは、フルスクリーンモードを切り替えることができます。これにより、「adjustPan」が正しく機能します。
つまり、@ Android:style/Theme.Black.NoTitleBar.Fullscreenとしてアプリケーションテーマの一部とstateVisible | adjustResizeアクティビティウィンドウのソフト入力モードの一部としてそれらを連携させるには、キーボードが表示される前にフルスクリーンモードを切り替える必要があります。
次のコードを使用します。
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
注-インスピレーションは以下から得られました: フルスクリーンモードでタイトルを非表示にする
Joseph Johnsonによる解決策 を試しましたが、他の人と同じように、コンテンツとキーボードのギャップの問題に遭遇しました。この問題は、フルスクリーンモードを使用する場合、ソフト入力モードが常にpanであるために発生します。このパンは、ソフト入力によって非表示になる入力フィールドをアクティブにすると、Josephのソリューションに干渉します。
ソフト入力が表示されると、コンテンツは最初に元の高さに基づいてパンされ、次にJosephのソリューションによって要求されたレイアウトによってサイズが変更されます。サイズ変更とそれに続くレイアウトはパンを元に戻さないため、ギャップが生じます。イベントの完全な順序は次のとおりです。
パンを無効にすることはできませんが、コンテンツの高さを変更してパンオフセットを強制的に0にすることは可能です。これは、パンが実行される前に実行されるため、リスナーで実行できます。コンテンツの高さを利用可能な高さに設定すると、ユーザーエクスペリエンスが滑らかになり、ちらつきがなくなります。
私もこれらの変更を加えました。これらのいずれかが問題を引き起こす場合、私に知らせてください:
getWindowVisibleDisplayFrame
を使用するために使用可能な高さの決定を切り替えました。 Rect
は、不要なガベージを少し防ぐためにキャッシュされます。Nexus 5と、小さな画面から大きな画面までのサイズのAPIレベル16〜24を実行するエミュレータでテストされています。
コードはKotlinに移植されましたが、私の変更をJavaに移植するのは簡単です。ヘルプが必要な場合はお知らせください:
class AndroidBug5497Workaround constructor(activity: Activity) {
private val contentContainer = activity.findViewById(Android.R.id.content) as ViewGroup
private val rootView = contentContainer.getChildAt(0)
private val rootViewLayout = rootView.layoutParams as FrameLayout.LayoutParams
private val viewTreeObserver = rootView.viewTreeObserver
private val listener = ViewTreeObserver.OnGlobalLayoutListener { possiblyResizeChildOfContent() }
private val contentAreaOfWindowBounds = Rect()
private var usableHeightPrevious = 0
// I call this in "onResume()" of my fragment
fun addListener() {
viewTreeObserver.addOnGlobalLayoutListener(listener)
}
// I call this in "onPause()" of my fragment
fun removeListener() {
viewTreeObserver.removeOnGlobalLayoutListener(listener)
}
private fun possiblyResizeChildOfContent() {
contentContainer.getWindowVisibleDisplayFrame(contentAreaOfWindowBounds)
val usableHeightNow = contentAreaOfWindowBounds.height()
if (usableHeightNow != usableHeightPrevious) {
rootViewLayout.height = usableHeightNow
// Change the bounds of the root view to prevent gap between keyboard and content, and top of content positioned above top screen Edge.
rootView.layout(contentAreaOfWindowBounds.left, contentAreaOfWindowBounds.top, contentAreaOfWindowBounds.right, contentAreaOfWindowBounds.bottom)
rootView.requestLayout()
usableHeightPrevious = usableHeightNow
}
}
}
システムUIアプローチ( https://developer.Android.com/training/system-ui/immersive.html )を使用している場合、シンプルで信頼できるソリューションを見つけました。
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
を使用している場合に機能します。 CoordinatorLayout
を使用している場合。
WindowManager.LayoutParams.FLAG_FULLSCREEN
(Android:windowFullscreen
をテーマに設定することもできます)では機能しませんが、SYSTEM_UI_FLAG_LAYOUT_STABLE
(「同じ視覚効果」 ドキュメントによると )で同様の効果を得ることができます。このソリューションは再び機能するはずです。
getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION /* If you want to hide navigation */
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE)
Marshmallowを実行しているデバイスでテストしました。
重要なのは、ソフトキーボードもシステムウィンドウ(ステータスバーやナビゲーションバーなど)の1つであるため、システムによってディスパッチされるWindowInsets
には、正確で信頼できる情報が含まれているということです。
ステータスバーの背後に描画しようとしているDrawerLayout
のようなユースケースでは、上部のインセットのみを無視し、ソフトキーボードを考慮した下部のインセットを適用するレイアウトを作成できます。
カスタムFrameLayout
は次のとおりです。
/**
* Implements an effect similar to {@code Android:fitsSystemWindows="true"} on Lollipop or higher,
* except ignoring the top system window inset. {@code Android:fitsSystemWindows="true"} does not
* and should not be set on this layout.
*/
public class FitsSystemWindowsExceptTopFrameLayout extends FrameLayout {
public FitsSystemWindowsExceptTopFrameLayout(Context context) {
super(context);
}
public FitsSystemWindowsExceptTopFrameLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public FitsSystemWindowsExceptTopFrameLayout(Context context, AttributeSet attrs,
int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@RequiresApi(Build.VERSION_CODES.Lollipop)
public FitsSystemWindowsExceptTopFrameLayout(Context context, AttributeSet attrs,
int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
@Override
public WindowInsets onApplyWindowInsets(WindowInsets insets) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Lollipop) {
setPadding(insets.getSystemWindowInsetLeft(), 0, insets.getSystemWindowInsetRight(),
insets.getSystemWindowInsetBottom());
return insets.replaceSystemWindowInsets(0, insets.getSystemWindowInsetTop(), 0, 0);
} else {
return super.onApplyWindowInsets(insets);
}
}
}
そしてそれを使用するには:
<com.example.yourapplication.FitsSystemWindowsExceptTopFrameLayout
xmlns:Android="http://schemas.Android.com/apk/res/Android"
Android:layout_width="match_parent"
Android:layout_height="match_parent">
<!-- Your original layout here -->
</com.example.yourapplication.FitsSystemWindowsExceptTopFrameLayout>
これは、狂気の変更なしで、画面サイズのランダムな1/3
または1/4
を参照として取得しようとするハックよりもはるかに優れたデバイスで理論的に機能するはずです。
(API 16以降が必要ですが、ステータスバーの背後に描画するためにLollipop +でのみフルスクリーンを使用しているため、この場合に最適なソリューションです。)
私もこの問題に直面しなければならなかったので、HTC one、galaxy s1、s2、s3、note、HTC sensationをチェックする作業がありました。
レイアウトのルートビューにグローバルレイアウトリスナーを配置する
mRootView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener(){
public void onGlobalLayout() {
checkHeightDifference();
}
});
そこでは、高さの違いを確認しました。画面の高さの違いが画面の高さの3分の1より大きい場合は、キーボードが開いていると想定できます。 this answer から取得しました。
private void checkHeightDifference(){
// get screen frame rectangle
Rect r = new Rect();
mRootView.getWindowVisibleDisplayFrame(r);
// get screen height
int screenHeight = mRootView.getRootView().getHeight();
// calculate the height difference
int heightDifference = screenHeight - (r.bottom - r.top);
// if height difference is different then the last height difference and
// is bigger then a third of the screen we can assume the keyboard is open
if (heightDifference > screenHeight/3 && heightDifference != mLastHeightDifferece) {
// keyboard visiblevisible
// get root view layout params
FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mRootView.getLayoutParams();
// set the root view height to screen height minus the height difference
lp.height = screenHeight - heightDifference;
// call request layout so the changes will take affect
.requestLayout();
// save the height difference so we will run this code only when a change occurs.
mLastHeightDifferece = heightDifference;
} else if (heightDifference != mLastHeightDifferece) {
// keyboard hidden
PFLog.d("[ChatroomActivity] checkHeightDifference keyboard hidden");
// get root view layout params and reset all the changes we have made when the keyboard opened.
FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mRootView.getLayoutParams();
lp.height = screenHeight;
// call request layout so the changes will take affect
mRootView.requestLayout();
// save the height difference so we will run this code only when a change occurs.
mLastHeightDifferece = heightDifference;
}
}
これはおそらく防弾ではなく、一部のデバイスでは機能しないかもしれませんが、私にとっては機能し、あなたにも役立つことを願っています。
Android:windowSoftInputMode="adjustResize"
がアクティビティに設定されている場合、WindowManager.LayoutParams.FLAG_FULLSCREEN
は機能しないことに注意してください。 2つのオプションがあります。
アクティビティのフルスクリーンモードを無効にします。アクティビティはフルスクリーンモードではサイズ変更されません。これは、xmlで(アクティビティのテーマを変更して)またはJavaコードで実行できます。 onCreate()メソッドに次の行を追加します。
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);`
または
別の方法を使用して、フルスクリーンモードを実現します。 onCreate()メソッドに次のコードを追加します。
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
View decorView = getWindow().getDecorView();
// Hide the status bar.
int uiOptions = View.SYSTEM_UI_FLAG_FULLSCREEN;
decorView.setSystemUiVisibility(uiOptions);`
method-2はAndroid 4.1以降でのみ機能することに注意してください。
ジョセフ・ジョンソンのソリューションを実装し、うまく機能しました。このソリューションを使用した後、アプリケーションのドロワーが正しく閉じられないことがありました。ユーザーが編集テキストのあるフラグメントを閉じるときに、removeOnGlobalLayoutListenerリスナーを削除する機能を追加しました。
//when the application uses full screen theme and the keyboard is shown the content not scrollable!
//with this util it will be scrollable once again
//http://stackoverflow.com/questions/7417123/Android-how-to-adjust-layout-in-full-screen-mode-when-softkeyboard-is-visible
public class AndroidBug5497Workaround {
private static AndroidBug5497Workaround mInstance = null;
private View mChildOfContent;
private int usableHeightPrevious;
private FrameLayout.LayoutParams frameLayoutParams;
private ViewTreeObserver.OnGlobalLayoutListener _globalListener;
// For more information, see https://code.google.com/p/Android/issues/detail?id=5497
// To use this class, simply invoke assistActivity() on an Activity that already has its content view set.
public static AndroidBug5497Workaround getInstance (Activity activity) {
if(mInstance==null)
{
synchronized (AndroidBug5497Workaround.class)
{
mInstance = new AndroidBug5497Workaround(activity);
}
}
return mInstance;
}
private AndroidBug5497Workaround(Activity activity) {
FrameLayout content = (FrameLayout) activity.findViewById(Android.R.id.content);
mChildOfContent = content.getChildAt(0);
frameLayoutParams = (FrameLayout.LayoutParams) mChildOfContent.getLayoutParams();
_globalListener = new ViewTreeObserver.OnGlobalLayoutListener()
{
@Override
public void onGlobalLayout()
{
possiblyResizeChildOfContent();
}
};
}
public void setListener()
{
mChildOfContent.getViewTreeObserver().addOnGlobalLayoutListener(_globalListener);
}
public void removeListener()
{
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
mChildOfContent.getViewTreeObserver().removeOnGlobalLayoutListener(_globalListener);
} else {
mChildOfContent.getViewTreeObserver().removeGlobalOnLayoutListener(_globalListener);
}
}
private void possiblyResizeChildOfContent() {
int usableHeightNow = computeUsableHeight();
if (usableHeightNow != usableHeightPrevious) {
int usableHeightSansKeyboard = mChildOfContent.getRootView().getHeight();
int heightDifference = usableHeightSansKeyboard - usableHeightNow;
if (heightDifference > (usableHeightSansKeyboard/4)) {
// keyboard probably just became visible
frameLayoutParams.height = usableHeightSansKeyboard - heightDifference;
} else {
// keyboard probably just became hidden
frameLayoutParams.height = usableHeightSansKeyboard;
}
mChildOfContent.requestLayout();
usableHeightPrevious = usableHeightNow;
}
}
private int computeUsableHeight() {
Rect r = new Rect();
mChildOfContent.getWindowVisibleDisplayFrame(r);
return (r.bottom - r.top);
}
}
編集テキストが配置されているクラスを使用します
@Override
public void onStart()
{
super.onStart();
AndroidBug5497Workaround.getInstance(getActivity()).setListener();
}
@Override
public void onStop()
{
super.onStop();
AndroidBug5497Workaround.getInstance(getActivity()).removeListener();
}
FullScreenで動作させるには:
ionicキーボードプラグインを使用します。これにより、キーボードが表示されたり消えたりするのを聞くことができます。
OnDeviceReadyはこれらのイベントリスナーを追加します。
// Allow Screen to Move Up when Keyboard is Present
window.addEventListener('native.keyboardshow', onKeyboardShow);
// Reset Screen after Keyboard hides
window.addEventListener('native.keyboardhide', onKeyboardHide);
ロジック:
function onKeyboardShow(e) {
// Get Focused Element
var thisElement = $(':focus');
// Get input size
var i = thisElement.height();
// Get Window Height
var h = $(window).height()
// Get Keyboard Height
var kH = e.keyboardHeight
// Get Focused Element Top Offset
var eH = thisElement.offset().top;
// Top of Input should still be visible (30 = Fixed Header)
var vS = h - kH;
i = i > vS ? (vS - 30) : i;
// Get Difference
var diff = (vS - eH - i);
if (diff < 0) {
var parent = $('.myOuter-xs.myOuter-md');
// Add Padding
var marginTop = parseInt(parent.css('marginTop')) + diff - 25;
parent.css('marginTop', marginTop + 'px');
}
}
function onKeyboardHide(e) {
// Remove All Style Attributes from Parent Div
$('.myOuter-xs.myOuter-md').removeAttr('style');
}
基本的にそれらの差がマイナスの場合、それはキーボードが入力をカバーしているピクセルの量です。したがって、これによって親divを調整すると、それを相殺するはずです。
ロジックにタイムアウトを追加すると、300ミリ秒でもパフォーマンスが最適化されるはずです(これにより、キーボードの時間が表示されます)。
Android:fitsSystemWindows="true"
をレイアウトに追加すると、このレイアウトのサイズが変更されます。
ジョセフ・ジョンソンのクラスを試してみましたが、うまくいきましたが、私のニーズを十分に満たしていませんでした。 Android:windowSoftInputMode = "adjustResize"をエミュレートするのではなく、Android:windowSoftInputMode = "adjustPan"をエミュレートする必要がありました。
私はこれをフルスクリーンのウェブビューに使用しています。コンテンツビューを正しい位置にパンするには、javascriptインターフェイスを使用して、フォーカスがあり、キーボード入力を受け取っているページ要素の位置に関する詳細を提供する必要があります。私はそれらの詳細を省略しましたが、ジョセフ・ジョンソンのクラスの書き直しを提供しました。カスタムパンとリサイズを実装するための非常に強固な基盤を提供します。
package some.package.name;
import some.package.name.JavaScriptObject;
import Android.app.Activity;
import Android.graphics.Rect;
import Android.view.View;
import Android.view.ViewTreeObserver;
import Android.widget.FrameLayout;
//-------------------------------------------------------
// ActivityPanner Class
//
// Convenience class to handle Activity attributes bug.
// Use this class instead of windowSoftInputMode="adjustPan".
//
// To implement, call enable() and pass a reference
// to an Activity which already has its content view set.
// Example:
// setContentView( R.layout.someview );
// ActivityPanner.enable( this );
//-------------------------------------------------------
//
// Notes:
//
// The standard method for handling screen panning
// when the virtual keyboard appears is to set an activity
// attribute in the manifest.
// Example:
// <activity
// ...
// Android:windowSoftInputMode="adjustPan"
// ... >
// Unfortunately, this is ignored when using the fullscreen attribute:
// Android:theme="@Android:style/Theme.NoTitleBar.Fullscreen"
//
//-------------------------------------------------------
public class ActivityPanner {
private View contentView_;
private int priorVisibleHeight_;
public static void enable( Activity activity ) {
new ActivityPanner( activity );
}
private ActivityPanner( Activity activity ) {
FrameLayout content = (FrameLayout)
activity.findViewById( Android.R.id.content );
contentView_ = content.getChildAt( 0 );
contentView_.getViewTreeObserver().addOnGlobalLayoutListener(
new ViewTreeObserver.OnGlobalLayoutListener() {
public void onGlobalLayout() { panAsNeeded(); }
});
}
private void panAsNeeded() {
// Get current visible height
int currentVisibleHeight = visibleHeight();
// Determine if visible height changed
if( currentVisibleHeight != priorVisibleHeight_ ) {
// Determine if keyboard visiblity changed
int screenHeight =
contentView_.getRootView().getHeight();
int coveredHeight =
screenHeight - currentVisibleHeight;
if( coveredHeight > (screenHeight/4) ) {
// Keyboard probably just became visible
// Get the current focus elements top & bottom
// using a ratio to convert the values
// to the native scale.
float ratio = (float) screenHeight / viewPortHeight();
int elTop = focusElementTop( ratio );
int elBottom = focusElementBottom( ratio );
// Determine the amount of the focus element covered
// by the keyboard
int elPixelsCovered = elBottom - currentVisibleHeight;
// If any amount is covered
if( elPixelsCovered > 0 ) {
// Pan by the amount of coverage
int panUpPixels = elPixelsCovered;
// Prevent panning so much the top of the element
// becomes hidden
panUpPixels = ( panUpPixels > elTop ?
elTop : panUpPixels );
// Prevent panning more than the keyboard height
// (which produces an empty gap in the screen)
panUpPixels = ( panUpPixels > coveredHeight ?
coveredHeight : panUpPixels );
// Pan up
contentView_.setY( -panUpPixels );
}
}
else {
// Keyboard probably just became hidden
// Reset pan
contentView_.setY( 0 );
}
// Save usabale height for the next comparison
priorVisibleHeight_ = currentVisibleHeight;
}
}
private int visibleHeight() {
Rect r = new Rect();
contentView_.getWindowVisibleDisplayFrame( r );
return r.bottom - r.top;
}
// Customize this as needed...
private int viewPortHeight() { return JavaScriptObject.viewPortHeight(); }
private int focusElementTop( final float ratio ) {
return (int) (ratio * JavaScriptObject.focusElementTop());
}
private int focusElementBottom( final float ratio ) {
return (int) (ratio * JavaScriptObject.focusElementBottom());
}
}
実際、ソフトキーボードの外観は、Activity
モードで選択したwindowSoftInputMode
に関係なく、FullScreen
に影響を与えるようには見えません。
このプロパティに関するドキュメントはあまり見つかりませんでしたが、FullScreen
モードは、ソフトキーボードをあまり使用する必要のないゲームアプリケーション用に設計されたと思います。あなたのアクティビティがソフトキーボードを介したユーザーインタラクションを必要とするアクティビティである場合、フルスクリーン以外のテーマの使用を再検討してください。 NoTitleBar
テーマを使用してTitleBarをオフにすることができます。通知バーを非表示にしたいのはなぜですか?
Android:windowSoftInputMode="adjustResize"
のままにしてください。 "adjustResize"
と"adjustPan"
のうち1つだけを保持するように指定されているためです(ウィンドウ調整モードはadjustResizeまたはadjustPanで指定されます。常にどちらかを指定することを強くお勧めします)。ここで見つけることができます: http://developer.Android.com/resources/articles/on-screen-inputs.html
それは私にとって完璧に機能します。
私はジョセフ・ジョンソンを使用してAndroidBug5497Workaroundクラスを作成しましたが、ソフトキーボードとビューの間に黒いスペースができました。このリンクを参照しました Greg Ennis 。上記のいくつかの変更を行った後、これが私の最終的な作業コードです。
public class SignUpActivity extends Activity {
private RelativeLayout rlRootView; // this is my root layout
private View rootView;
private ViewGroup contentContainer;
private ViewTreeObserver viewTreeObserver;
private ViewTreeObserver.OnGlobalLayoutListener listener;
private Rect contentAreaOfWindowBounds = new Rect();
private FrameLayout.LayoutParams rootViewLayout;
private int usableHeightPrevious = 0;
private View mDecorView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_sign_up);
mDecorView = getWindow().getDecorView();
contentContainer =
(ViewGroup) this.findViewById(Android.R.id.content);
listener = new OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
possiblyResizeChildOfContent();
}
};
rootView = contentContainer.getChildAt(0);
rootViewLayout = (FrameLayout.LayoutParams)
rootView.getLayoutParams();
rlRootView = (RelativeLayout) findViewById(R.id.rlRootView);
rlRootView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
int heightDiff = rlRootView.getRootView().getHeight() - rlRootView.getHeight();
if (heightDiff > Util.dpToPx(SignUpActivity.this, 200)) {
// if more than 200 dp, it's probably a keyboard...
// Logger.info("Soft Key Board ", "Key board is open");
} else {
Logger.info("Soft Key Board ", "Key board is CLOSED");
hideSystemUI();
}
}
});
}
// This snippet hides the system bars.
protected void hideSystemUI() {
// Set the IMMERSIVE flag.
// Set the content to appear under the system bars so that the
content
// doesn't resize when the system bars hide and show.
mDecorView.setSystemUiVisibility(
View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
}
@Override
protected void onPause() {
super.onPause();
if (viewTreeObserver.isAlive()) {
viewTreeObserver.removeOnGlobalLayoutListener(listener);
}
}
@Override
protected void onResume() {
super.onResume();
if (viewTreeObserver == null || !viewTreeObserver.isAlive()) {
viewTreeObserver = rootView.getViewTreeObserver();
}
viewTreeObserver.addOnGlobalLayoutListener(listener);
}
@Override
protected void onDestroy() {
super.onDestroy();
rootView = null;
contentContainer = null;
viewTreeObserver = null;
}
private void possiblyResizeChildOfContent() {
contentContainer.getWindowVisibleDisplayFrame(contentAreaOfWindowBounds);
int usableHeightNow = contentAreaOfWindowBounds.height();
if (usableHeightNow != usableHeightPrevious) {
rootViewLayout.height = usableHeightNow;
rootView.layout(contentAreaOfWindowBounds.left,
contentAreaOfWindowBounds.top, contentAreaOfWindowBounds.right, contentAreaOfWindowBounds.bottom);
rootView.requestLayout();
usableHeightPrevious = usableHeightNow;
} else {
this.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);
}
}
}
https://stackoverflow.com/a/19494006/1815624 に基づいており、それを実現したい...
更新されたアイデア
からの回答を組み合わせて
関連コード:
if (heightDifference > (usableHeightSansKeyboard / 4)) {
// keyboard probably just became visible
frameLayoutParams.height = usableHeightSansKeyboard - heightDifference;
activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
} else {
// keyboard probably just became hidden
if(usableHeightPrevious != 0) {
frameLayoutParams.height = usableHeightSansKeyboard;
activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
}
https://github.com/CrandellWS/AndroidBug5497Workaround/blob/master/AndroidBug5497Workaround.Java の完全なソース
キーボードを開く前にコンテナの高さの静的な値を作成するキーボードが開いたときにusableHeightSansKeyboard - heightDifference
に基づいてコンテナの高さを設定し、閉じたときに保存された値に戻す
if (heightDifference > (usableHeightSansKeyboard / 4)) {
// keyboard probably just became visible
frameLayoutParams.height = usableHeightSansKeyboard - heightDifference;
int mStatusHeight = getStatusBarHeight();
frameLayoutParams.topMargin = mStatusHeight;
((MainActivity)activity).setMyMainHeight(usableHeightSansKeyboard - heightDifference);
if(BuildConfig.DEBUG){
Log.v("aBug5497", "keyboard probably just became visible");
}
} else {
// keyboard probably just became hidden
if(usableHeightPrevious != 0) {
frameLayoutParams.height = usableHeightSansKeyboard;
((MainActivity)activity).setMyMainHeight();
}
frameLayoutParams.topMargin = 0;
if(BuildConfig.DEBUG){
Log.v("aBug5497", "keyboard probably just became hidden");
}
}
MainActivityのメソッド
public void setMyMainHeight(final int myMainHeight) {
runOnUiThread(new Runnable() {
@Override
public void run() {
ConstraintLayout.LayoutParams rLparams = (ConstraintLayout.LayoutParams) myContainer.getLayoutParams();
rLparams.height = myMainHeight;
myContainer.setLayoutParams(rLparams);
}
});
}
int mainHeight = 0;
public void setMyMainHeight() {
runOnUiThread(new Runnable() {
@Override
public void run() {
ConstraintLayout.LayoutParams rLparams = (ConstraintLayout.LayoutParams) myContainer.getLayoutParams();
rLparams.height = mainHeight;
myContainer.setLayoutParams(rLparams);
}
});
}
コンテナXMLの例
<Android.support.constraint.ConstraintLayout
xmlns:Android="http://schemas.Android.com/apk/res/Android"
xmlns:app="http://schemas.Android.com/apk/res-auto"
Android:layout_width="match_parent"
Android:layout_height="match_parent"
>
<Android.support.constraint.ConstraintLayout
Android:id="@+id/my_container"
Android:layout_width="match_parent"
Android:layout_height="0dp"
app:layout_constraintHeight_percent=".8">
必要に応じて同様にマージンを追加できます...
別の考慮事項は、パディングの使用です。この例は次の場所にあります。
https://github.com/mikepenz/MaterialDrawer/issues/95#issuecomment-80519589
私は現在このアプローチを使用しており、それは魅力のように機能します。トリックは、21と21のさまざまなメソッドからキーボードの高さを取得し、それをアクティビティのルートビューの下部パディングとして使用することです。私はあなたのレイアウトが上部のパディングを必要としないと仮定しました(ステータスバーの下に行きます)が、もしそうなら、私の答えを更新するように私に知らせてください。
MainActivity.Java
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
RelativeLayout mainLayout = findViewById(R.id.main_layout);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Lollipop) {
ViewCompat.setOnApplyWindowInsetsListener(mainLayout , new OnApplyWindowInsetsListener() {
@Override
public WindowInsetsCompat onApplyWindowInsets(View v, WindowInsetsCompat insets) {
v.setPadding(0, 0, 0, insets.getSystemWindowInsetBottom());
return insets;
}
});
} else {
View decorView = getWindow().getDecorView();
final View contentView = mainLayout;
decorView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
Rect r = new Rect();
//r will be populated with the coordinates of your view that area still visible.
decorView.getWindowVisibleDisplayFrame(r);
//get screen height and calculate the difference with the useable area from the r
int height = decorView.getContext().getResources().getDisplayMetrics().heightPixels;
int diff = height - r.bottom;
//if it could be a keyboard add the padding to the view
if (diff != 0) {
// if the use-able screen height differs from the total screen height we assume that it shows a keyboard now
//check if the padding is 0 (if yes set the padding for the keyboard)
if (contentView.getPaddingBottom() != diff) {
//set the padding of the contentView for the keyboard
contentView.setPadding(0, 0, 0, diff);
}
} else {
//check if the padding is != 0 (if yes reset the padding)
if (contentView.getPaddingBottom() != 0) {
//reset the padding of the contentView
contentView.setPadding(0, 0, 0, 0);
}
}
}
});
}
}
...
}
ルートビューにIDを指定することを忘れないでください:
activity_main.xml
<RelativeLayout
xmlns:Android="http://schemas.Android.com/apk/res/Android"
Android:id="@+id/main_layout"
Android:layout_width="match_parent"
Android:layout_height="match_parent">
それが誰かを助けることを願っています。
adjustPanを使用するときにAndroid:windowSoftInputMode="adjustResize|stateHidden
のみを使用し、サイズ変更プロパティを無効にします
今日、Android SDKの場合、全画面でadjustResizeが機能しないという問題が発生しています。
私が見つけた答えから:
解決策 -しかし、解決策は画像の問題でこれを示しています:
私が見つけたよりも 解決策 と1つの不要なアクションを削除します:
this.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);
だから、Kotlinの私の固定ソリューションコードを参照してください:
class AndroidBug5497Workaround constructor(val activity: Activity) {
private val content = activity.findViewById<View>(Android.R.id.content) as FrameLayout
private val mChildOfContent = content.getChildAt(0)
private var usableHeightPrevious: Int = 0
private val contentContainer = activity.findViewById(Android.R.id.content) as ViewGroup
private val rootView = contentContainer.getChildAt(0)
private val rootViewLayout = rootView.layoutParams as FrameLayout.LayoutParams
private val listener = {
possiblyResizeChildOfContent()
}
fun addListener() {
mChildOfContent.apply {
viewTreeObserver.addOnGlobalLayoutListener(listener)
}
}
fun removeListener() {
mChildOfContent.apply {
viewTreeObserver.removeOnGlobalLayoutListener(listener)
}
}
private fun possiblyResizeChildOfContent() {
val contentAreaOfWindowBounds = Rect()
mChildOfContent.getWindowVisibleDisplayFrame(contentAreaOfWindowBounds)
val usableHeightNow = contentAreaOfWindowBounds.height()
if (usableHeightNow != usableHeightPrevious) {
rootViewLayout.height = usableHeightNow
rootView.layout(contentAreaOfWindowBounds.left,
contentAreaOfWindowBounds.top, contentAreaOfWindowBounds.right, contentAreaOfWindowBounds.bottom);
mChildOfContent.requestLayout()
usableHeightPrevious = usableHeightNow
}
}
}
私のバグ修正実装コード:
class LeaveDetailActivity : BaseActivity(){
private val keyBoardBugWorkaround by lazy {
AndroidBug5497Workaround(this)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
override fun onResume() {
keyBoardBugWorkaround.addListener()
super.onResume()
}
override fun onPause() {
keyBoardBugWorkaround.removeListener()
super.onPause()
}
}
ジョセフ・ジョンソンやヨハン・スタイツなど、多くの解決策を試しました。しかし、その結果、すべての場合で一部のデバイス(Lenovo s820など)でコンテンツとキーボードの間に空白ができました。だから私は彼らのコードにいくつかの変更を加え、最終的に実用的なソリューションを得た。
キーボードが表示されているときにコンテンツの上部にマージンを追加することに基づいた私の考え。
contentContainer.getWindowVisibleDisplayFrame(contentAreaOfWindowBounds);
int usableHeightNow = contentAreaOfWindowBounds.height();
if (usableHeightNow != usableHeightPrevious) {
int difference = usableHeightNow - usableHeightPrevious;
if (difference < 0 && difference < -150) {
keyboardShowed = true;
rootViewLayout.topMargin -= difference + 30;
rootViewLayout.bottomMargin += 30;
}
else if (difference < 0 && difference > -150){
rootViewLayout.topMargin -= difference + 30;
}
else if (difference > 0 && difference > 150) {
keyboardShowed = false;
rootViewLayout.topMargin = 0;
rootViewLayout.bottomMargin = 0;
}
rootView.requestLayout();
Log.e("Bug Workaround", "Difference: " + difference);
usableHeightPrevious = usableHeightNow;
}
ご覧のとおり、画面上部とコンテンツゾーンの間に余白のある小さな空白があるため、差に30ピクセルを追加します。それがどこから現れるのかわからないので、マージンを小さくすることを決めましたが、今では必要に応じて正確に機能します。
ステータスバーを非表示にするためにフルスクリーンモードのみを使用していました。ただし、キーボードが表示されたときにアプリのサイズを変更する必要があります。他のすべてのソリューション(おそらくポストの年齢が原因)は複雑であるか、使用できません(PhoneGap Buildの袋のJavaコードの変更を避けたい)。
フルスクリーンを使用する代わりに、Androidの構成をフルスクリーン以外に変更しました。
<preference name="fullscreen" value="false" />
コマンドライン経由でcordova-plugin-statusbar
を追加しました:
cordova plugin add cordova-plugin-statusbar
アプリが読み込まれたら、次のようにプラグインのメソッドを呼び出して自分自身を非表示にします。
if (window.cordova && window.cordova.platformId == 'Android' && window.StatusBar)
window.StatusBar.hide();
これは魅力のように機能します。本当の欠点は、アプリの読み込み中にステータスバーが簡単に表示されることです。私のニーズについては、それは問題ではありませんでした。
ジョセフ、答えてくれてありがとう。ただし、ResibleChildOfContent()メソッドの可能性がある部分では、
else {
// keyboard probably just became hidden
frameLayoutParams.height = usableHeightSansKeyboard;
}
ビューの下部が非表示になったため、私のために働いていませんでした。そのため、グローバル変数restoreHeightを取得する必要があり、コンストラクターに最後の行を挿入しました
restoreHeight = frameLayoutParams.height;
そして、前に言及した部分を
else {
// keyboard probably just became hidden
frameLayoutParams.height = restoreHeight;
}
しかし、なぜあなたのコードが私のために機能しなかったのか分かりません。誰かがこれに光を当てることができれば、それは大きな助けになるでしょう。
1)KeyboardHeightHelperを作成します。
public class KeyboardHeightHelper {
private final View decorView;
private int lastKeyboardHeight = -1;
public KeyboardHeightHelper(Activity activity, View activityRootView, OnKeyboardHeightChangeListener listener) {
this.decorView = activity.getWindow().getDecorView();
activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(() -> {
int keyboardHeight = getKeyboardHeight();
if (lastKeyboardHeight != keyboardHeight) {
lastKeyboardHeight = keyboardHeight;
listener.onKeyboardHeightChange(keyboardHeight);
}
});
}
private int getKeyboardHeight() {
Rect rect = new Rect();
decorView.getWindowVisibleDisplayFrame(rect);
return decorView.getHeight() - rect.bottom;
}
public interface OnKeyboardHeightChangeListener {
void onKeyboardHeightChange(int keyboardHeight);
}
}
2)アクティビティをフルスクリーンにします:
activity.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
3)キーボードの高さの変化を聞き、ビューの下部にパディングを追加します。
View rootView = activity.findViewById(R.id.root); // your root view or any other you want to resize
KeyboardHeightHelper effectiveHeightHelper = new KeyboardHeightHelper(
activity,
rootView,
keyboardHeight -> rootView.setPadding(0, 0, 0, keyboardHeight));
So、画面にキーボードが表示されるたびに-ビューの下部のパディングが変更され、コンテンツが再配置されます。
私はstackOverflowから可能な答えをすべて試してみましたが、最終的には1週間のロングサーチ後に解決しました。私は座標レイアウトを使用しましたが、これをlinearLayoutで変更すると、問題が修正されます。座標レイアウトにバグがあるか、何か間違いがあるのかはわからない。
一番下のバーをビューの一番下に固定する必要がありますが、キーボードが表示されたら、上に移動してキーボードの上に配置する必要がありますか?
このコードスニペットを試すことができます。
<RelativeLayout
xmlns:Android="http://schemas.Android.com/apk/res/Android"
...>
<RelativeLayout
Android:id="@+id/RelativeLayoutTopBar"
...>
</RelativeLayout>
<LinearLayout
Android:id="@+id/LinearLayoutBottomBar"
Android:layout_alignParentBottom = true
...>
</LinearLayout>
<LinearLayout
Android:layout_width="fill_parent"
Android:layout_height="390dp"
Android:orientation="vertical"
Android:layout_above="@+id/LinearLayoutBottomBar"
Android:layout_below="@+id/RelativeLayoutTopBar">
<ScrollView
Android:layout_width="fill_parent"
Android:layout_height="wrap_content"
Android:layout_marginTop="10dp"
Android:layout_marginBottom="10dp"
Android:id="@+id/ScrollViewBackground">
...
</ScrollView>
</LinearLayout>
</RelativeLayout>
BottomBarはビューの下部に固定され、ScrollViewを含むLinearLayoutは、上部/下部バーとキーボードが表示された後、ビューの左側に表示されます。それがあなたにとってもうまくいくかどうか教えてください。
使用しないでください:
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
うまくいかないからです。その代わりに、次を使用します。
fun setFullScreen(fullScreen: Boolean) {
val decorView = getWindow().getDecorView()
val uiOptions : Int
if(fullScreen){
uiOptions = View.SYSTEM_UI_FLAG_FULLSCREEN // this hide statusBar
toolbar.visibility = View.GONE // if you use toolbar
tabs.visibility = View.GONE // if you use tabLayout
} else {
uiOptions = View.SYSTEM_UI_FLAG_VISIBLE // this show statusBar
toolbar.visibility = View.VISIBLE
tabs.visibility = View.VISIBLE
}
decorView.setSystemUiVisibility(uiOptions)
}