84

我在新的Android KitKat(4.4)和windowSoftInputMode="adjustResize"中的半透明操作欄/導航欄存在問題。windowSoftInputMode =「adjustResize」不支持半透明動作/導航欄

通常將InputMode更改爲adjustResize,應用程序應該在顯示鍵盤時調整自身的大小......但在這裏它不會!如果我刪除透明效果的行,調整大小正在工作。

因此,如果鍵盤可見,我的ListView是在它下面,我無法訪問最後幾項。 (僅通過隱藏手動鍵盤)

的AndroidManifest.xml用於固定

<?xml version="1.0" encoding="utf-8"?> 
<manifest xmlns:android="http://schemas.android.com/apk/res/android" 
package="XYZ" 
android:versionCode="23" 
android:versionName="0.1" > 

<uses-sdk 
    android:minSdkVersion="9" 
    android:targetSdkVersion="19" /> 

<application 
    android:allowBackup="true" 
    android:icon="@drawable/ic_launcher" 
    android:label="@string/app_name" 
    android:theme="@style/Theme.XYZStyle" > 
    <activity 
     android:name="XYZ" 
     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> 

值-V19/styles.xml

<?xml version="1.0" encoding="utf-8"?> 
<resources> 

<style name="Theme.XYZStyle" parent="@style/Theme.AppCompat.Light"> 
    <item name="android:windowTranslucentStatus">true</item> 
    <item name="android:windowTranslucentNavigation">true</item> 
</style> 

</resources> 

fragment.xml之

<?xml version="1.0" encoding="utf-8"?> 
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 
android:id="@+id/main" 
android:layout_width="match_parent" 
android:layout_height="match_parent" 
android:orientation="vertical" > 

<ListView 
    android:id="@+id/listView_contacts" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent" 
    android:clipToPadding="false" 
    android:divider="@null" 
    android:dividerHeight="0dp" 
    android:drawSelectorOnTop="true" 
    android:fastScrollAlwaysVisible="true" 
    android:fastScrollEnabled="true" 
    android:paddingBottom="@dimen/navigationbar__height" > 
</ListView> 

</RelativeLayout> 

任何想法這個?

+0

的可能重複[機器人:windowSoftInputMode = 「adjustResize」 沒有任何差別(http://stackoverflow.com/問題/ 8398102/androidwindowsoftinputmode-adjustresize - 犯規 - 化妝任何差) – mikepenz

回答

124

你缺少以下屬性:

android:fitsSystemWindows="true" 

片段 .XML佈局的根RelativeLayout

更新:

去年有由克里斯·貝恩一個有趣的談話,說明在良好的細節是如何工作的:

https://www.youtube.com/watch?v=_mGDMVRO3iE

+1

我想它'東西全屏政策 –

+1

你是男人!我只在棒棒糖版本上遇到過這個問題,它解決了它。 – David

+1

這看起來只適用於API 19+。 以前的版本和使用RelativeLayout adjustResize似乎就夠了。 – Davidea

26

有一個相關的bug報告here。我發現了一個解決方法,從有限的測試中,似乎可以做到這一點,而不會產生影響。用下面的邏輯添加一個自定義實現的根ViewGroup(我幾乎總是使用FrameLayout,所以這是我測試過的)。然後,使用此自定義佈局來代替您的根佈局,並確保您設置了android:fitsSystemWindows="true"。如果需要,您可以在佈局後隨時調用getInsets()(例如,添加一個OnPreDrawListener)來調整佈局的其餘部分以考慮系統插入。

import android.content.Context; 
import android.graphics.Rect; 
import android.os.Build; 
import android.util.AttributeSet; 
import android.widget.FrameLayout; 
import org.jetbrains.annotations.NotNull; 

/** 
* @author Kevin 
*   Date Created: 3/7/14 
* 
* https://code.google.com/p/android/issues/detail?id=63777 
* 
* When using a translucent status bar on API 19+, the window will not 
* resize to make room for input methods (i.e. 
* {@link android.view.WindowManager.LayoutParams#SOFT_INPUT_ADJUST_RESIZE} and 
* {@link android.view.WindowManager.LayoutParams#SOFT_INPUT_ADJUST_PAN} are 
* ignored). 
* 
* To work around this; override {@link #fitSystemWindows(android.graphics.Rect)}, 
* capture and override the system insets, and then call through to FrameLayout's 
* implementation. 
* 
* For reasons yet unknown, modifying the bottom inset causes this workaround to 
* fail. Modifying the top, left, and right insets works as expected. 
*/ 
public final class CustomInsetsFrameLayout extends FrameLayout { 
    private int[] mInsets = new int[4]; 

    public CustomInsetsFrameLayout(Context context) { 
     super(context); 
    } 

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

    public CustomInsetsFrameLayout(Context context, AttributeSet attrs, int defStyle) { 
     super(context, attrs, defStyle); 
    } 

    public final int[] getInsets() { 
     return mInsets; 
    } 

    @Override 
    protected final boolean fitSystemWindows(@NotNull Rect insets) { 
     if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { 
      // Intentionally do not modify the bottom inset. For some reason, 
      // if the bottom inset is modified, window resizing stops working. 
      // TODO: Figure out why. 

      mInsets[0] = insets.left; 
      mInsets[1] = insets.top; 
      mInsets[2] = insets.right; 

      insets.left = 0; 
      insets.top = 0; 
      insets.right = 0; 
     } 

     return super.fitSystemWindows(insets); 
    } 
} 

由於fitSystemWindow S爲過時,請參閱下面的答案來完成的解決方法。

+1

實際上,根據我的經驗,SOFT_INPUT_ADJUST_PAN似乎不會被忽略 - 它會將整個屏幕向上移動,包括聚焦視圖下的系統欄和移位鍵盤。 – sealskej

+0

謝謝 - 您對SOFT_INPUT_ADJUST_PAN正確。我用我的片段:getActivity()。getWindow()。setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN); – Simon

+0

這是我可以實現調整活動的adjustResize(顯示鍵盤時滾動視圖所需的唯一方法),fitSystemWindows設置爲true,以便滾動實際發生在> = Lollipop並具有半透明狀態欄。非常感謝。 – Lucas

4

我有同樣的問題, 我的活動有一個ScrollView作爲根視圖,並激活半透明狀態欄,它沒有正確調整,當鍵盤顯示...正確的屏幕沒有滾動隱藏輸入視圖。

解決方案: 將所有內容(佈局和活動邏輯)都移動到一個新的片段中。 然後改變活動只包含這個片段。現在一切都按預期工作!

這是活動的佈局:

<?xml version="1.0" encoding="utf-8"?> 
<FrameLayout 
    xmlns:android="http://schemas.android.com/apk/res/android" 

    android:id="@+id/contentView" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent" 
    android:fitsSystemWindows="true" /> 
+0

對我不起作用 – Alex

+0

對我來說就像一個魅力 –

24

@kcoppock答案是真正有用的,但fitSystemWindows在API層面棄用20

如此以來,API 20(KITKAT_WATCH),你應該重寫onApplyWindowInsets

@Override 
public final WindowInsets onApplyWindowInsets(WindowInsets insets) { 
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH) { 
     return super.onApplyWindowInsets(insets.replaceSystemWindowInsets(0, 0, 0, 
       insets.getSystemWindowInsetBottom())); 
    } else { 
     return insets; 
    } 
} 
+0

在什麼類應該我們重寫這個? –

+0

@ Ben-J在擴展原始類的類中 – Victor91

+0

我不知道爲什麼在未使用時設置mInsets數組元素,但它起作用 – Buckstabue

2

基於約瑟夫·約翰遜的Android How to adjust layout in Full Screen Mode when softkeyboard is visible

解決方法

在您的活動之後在onCreate()之後致電setContentView()

AndroidBug5497Workaround.assistActivity(this);

從原來的不同豆蔻在computeUsableHeight()

return r.bottom;取代return (r.bottom - r.top);出於某種原因,我必須屬性設置我的活動fitsSystemWindowsfalse

此變通辦法救了我。這對我來說很好。希望可以幫助你。

實現類是:

public class AndroidBug5497Workaround { 

// 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 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; 
} 

} 
0

AndroidBug5497Workaround.java照顧內存泄漏。需要下面的代碼

getViewTreeObserver().removeOnGlobalLayoutListener(listener); 

我使用RxJava樣品

public class MyActivity extends RxAppCompatActivity { 
    // ... 

protected void onStart(){ 
    super.onStart(); 

     TRSoftKeyboardVisibility 
      .changes(this) // activity 
      .compose(this.<TRSoftKeyboardVisibility.ChangeEvent>bindUntilEvent(ActivityEvent.PAUSE)) 
      .subscribe(keyboardEvent -> { 
       FrameLayout content = (FrameLayout) findViewById(android.R.id.content); 
       View firstChildView = content.getChildAt(0); 
       firstChildView.getLayoutParams().height = keyboardEvent.viewHeight(); 
       firstChildView.requestLayout(); 

       // keyboardEvent.isVisible  = keyboard visible or not 
       // keyboardEvent.keyboardHeight = keyboard height 
       // keyboardEvent.viewHeight  = fullWindowHeight - keyboardHeight 
      }); 
    //... 
} 





package commonlib.rxjava.keyboard; 

import android.app.Activity; 
import android.view.View; 
import android.widget.FrameLayout; 
import kr.ohlab.android.util.Assert; 
import rx.Observable; 

public class TRSoftKeyboardVisibility { 

    public static Observable<ChangeEvent> changes(Activity activity) { 
     Assert.notNull(activity, "activity == null"); 
     FrameLayout content = (FrameLayout) activity.findViewById(android.R.id.content); 
     View childOfContent = content.getChildAt(0); 
     return Observable.create(
      new TRSoftKeyboardVisibilityEventOnSubscribe(childOfContent)); 
    } 

    public static final class ChangeEvent { 
     private final int keyboardHeight; 
     private final boolean visible; 
     private final int viewHeight; 

     public static ChangeEvent create(boolean visible, int keyboardHeight, 
      int windowDisplayHeight) { 
      return new ChangeEvent(visible, keyboardHeight, windowDisplayHeight); 
     } 

     private ChangeEvent(boolean visible, int keyboardHeight, int viewHeight) { 
      this.keyboardHeight = keyboardHeight; 
      this.visible = visible; 
      this.viewHeight = viewHeight; 
     } 

     public int keyboardHeight() { 
      return keyboardHeight; 
     } 

     public boolean isVisible() { 
      return this.visible; 
     } 

     public int viewHeight() { 
      return viewHeight; 
     } 

     @Override 
     public boolean equals(Object o) { 
      if (this == o) return true; 
      if (!(o instanceof ChangeEvent)) return false; 

      ChangeEvent that = (ChangeEvent) o; 

      if (keyboardHeight != that.keyboardHeight) return false; 
      if (visible != that.visible) return false; 
      return viewHeight == that.viewHeight; 
     } 

     @Override 
     public int hashCode() { 
      int result = keyboardHeight; 
      result = 31 * result + (visible ? 1 : 0); 
      result = 31 * result + viewHeight; 
      return result; 
     } 

     @Override 
     public String toString() { 
      return "ChangeEvent{" + 
       "keyboardHeight=" + keyboardHeight + 
       ", visible=" + visible + 
       ", viewHeight=" + viewHeight + 
       '}'; 
     } 
    } 
} 


package commonlib.rxjava.keyboard; 

import android.graphics.Rect; 
import android.view.View; 
import android.view.ViewTreeObserver; 
import kr.ohlab.android.util.Assert; 
import rx.Observable; 
import rx.Subscriber; 
import rx.android.MainThreadSubscription; 
import timber.log.Timber; 

public class TRSoftKeyboardVisibilityEventOnSubscribe 
    implements Observable.OnSubscribe<TRSoftKeyboardVisibility.ChangeEvent> { 
    private final View mTopView; 
    private int mLastVisibleDecorViewHeight; 
    private final Rect mWindowVisibleDisplayFrame = new Rect(); 

    public TRSoftKeyboardVisibilityEventOnSubscribe(View topView) { 
     mTopView = topView; 
    } 

    private int computeWindowFrameHeight() { 
     mTopView.getWindowVisibleDisplayFrame(mWindowVisibleDisplayFrame); 
     return (mWindowVisibleDisplayFrame.bottom - mWindowVisibleDisplayFrame.top); 
    } 

    private TRSoftKeyboardVisibility.ChangeEvent checkKeyboardVisibility() { 
     int windowFrameHeightNow = computeWindowFrameHeight(); 
     TRSoftKeyboardVisibility.ChangeEvent event = null; 
     if (windowFrameHeightNow != mLastVisibleDecorViewHeight) { 
      int mTopViewHeight = mTopView.getHeight(); 
      int heightDiff = mTopViewHeight - windowFrameHeightNow; 
      Timber.e("XXX heightDiff=" + heightDiff); 
      if (heightDiff > (mTopViewHeight/4)) { 
       event = TRSoftKeyboardVisibility.ChangeEvent.create(true, heightDiff, windowFrameHeightNow); 
      } else { 
       event = TRSoftKeyboardVisibility.ChangeEvent.create(false, 0, windowFrameHeightNow); 
      } 
      mLastVisibleDecorViewHeight = windowFrameHeightNow; 
      return event; 
     } 

     return null; 
    } 

    public void call(final Subscriber<? super TRSoftKeyboardVisibility.ChangeEvent> subscriber) { 
     Assert.checkUiThread(); 

     final ViewTreeObserver.OnGlobalLayoutListener listener = 
      new ViewTreeObserver.OnGlobalLayoutListener() { 
       @Override 
       public void onGlobalLayout() { 
        TRSoftKeyboardVisibility.ChangeEvent event = checkKeyboardVisibility(); 
        if(event == null) 
         return; 
        if (!subscriber.isUnsubscribed()) { 
         subscriber.onNext(event); 
        } 
       } 
      }; 

     mTopView.getViewTreeObserver().addOnGlobalLayoutListener(listener); 

     subscriber.add(new MainThreadSubscription() { 
      @Override 
      protected void onUnsubscribe() { 
       mTopView.getViewTreeObserver().removeOnGlobalLayoutListener(listener); 
      } 
     }); 
    } 
} 
0

我有過一個問題,即自動呼叫removeOnGlobalLayoutListener()時的onPause()在活動的整個生命週期。

我將windowDrawsSystemBarBackgrounds設置爲'true',我的應用應該顯示在狀態欄下。

這是我的活動主題。

<item name="android:windowTranslucentStatus" tools:targetApi="KITKAT">false</item> 
<item name="android:windowDrawsSystemBarBackgrounds">true</item> 
<item name="android:windowTranslucentNavigation">true</item> 
<item name="android:statusBarColor">@android:color/transparent</item> 

我從jianshu's blog得到了幫助。 你可以閱讀代碼,但像我這樣的文字。 我增加了幾個代碼。

public final class ZeroInsetsFrameLayout extends FrameLayout { 
    private int[] mInsets = new int[4]; 

    public ZeroInsetsFrameLayout(Context context) { 
     super(context); 
    } 

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

    public ZeroInsetsFrameLayout(Context context, AttributeSet attrs, int defStyle) { 
     super(context, attrs, defStyle); 
    } 

    public final int[] getInsets() { 
     return mInsets; 
    } 

    @Override 
    public WindowInsets computeSystemWindowInsets(WindowInsets in, Rect outLocalInsets) { 
     outLocalInsets.left = 0; 
     outLocalInsets.top = 0; 
     outLocalInsets.right = 0; 

     return super.computeSystemWindowInsets(in, outLocalInsets); 
    } 

    @Override 
    protected final boolean fitSystemWindows(@NonNull Rect insets) { 
     if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { 
      // Intentionally do not modify the bottom inset. For some reason, 
      // if the bottom inset is modified, window resizing stops working. 
      // TODO: Figure out why. 

      mInsets[0] = insets.left; 
      mInsets[1] = insets.top; 
      mInsets[2] = insets.right; 

      insets.left = 0; 
      insets.top = 0; 
      insets.right = 0; 
     } 

     return super.fitSystemWindows(insets); 
    } 
} 

這是我的片段佈局。

<com.dhna.widget.ZeroInsetsFrameLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    xmlns:app="http://schemas.android.com/apk/res-auto" 
    xmlns:tools="http://schemas.android.com/tools" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent" 
    android:fitsSystemWindows="true" 
    android:background="@color/white"> 

    <!-- your xml code --> 

</ZeroInsetsFrameLayout> 

我希望它對您有所幫助。 祝你好運!

6

這個工作對我有半透明狀態欄和adjustResize在片段:

  1. 創建自定義的RelativeLayout爲@ Victor91和@kcoppock說。

  2. 使用CustomRelativeLayout作爲片段的父佈局。

  3. 申報主題與Android:windowTranslucentStatus =真

  4. Container活動必須在清單中聲明與 機器人:windowSoftInputMode = 「adjustResize」,並使用申報 主題

  5. 請使用fitsSystemWindows在片段根佈局!

    public class CustomRelativeLayout extends RelativeLayout { 
    
        private int[] mInsets = new int[4]; 
    
        public CustomRelativeLayout(Context context) { 
         super(context); 
        } 
    
        public CustomRelativeLayout(Context context, AttributeSet attrs) { 
         super(context, attrs); 
        } 
    
        public CustomRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) { 
         super(context, attrs, defStyleAttr); 
        } 
    
        public CustomRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { 
         super(context, attrs, defStyleAttr, defStyleRes); 
        } 
    
        @Override 
        public final WindowInsets onApplyWindowInsets(WindowInsets insets) { 
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH) { 
          mInsets[0] = insets.getSystemWindowInsetLeft(); 
          mInsets[1] = insets.getSystemWindowInsetTop(); 
          mInsets[2] = insets.getSystemWindowInsetRight(); 
          return super.onApplyWindowInsets(insets.replaceSystemWindowInsets(0, 0, 0, 
            insets.getSystemWindowInsetBottom())); 
         } else { 
          return insets; 
         } 
        } 
    } 
    

然後在XML中,

<com.blah.blah.CustomRelativeLayout 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:fitsSystemWindows="true"> 
</com.blah.blah.CustomRelativeLayout> 
0

最好的做法允許用戶滾動內容被示出鍵盤時。 所以要添加這個功能,你需要把你的根佈局ScrollView內使用windowSoftInputMode="adjustResize"活動方法。

但是,如果你想在Android上5內容與<item name="android:windowTranslucentStatus">true</item> 標誌使用此功能將不可滾動,將與鍵盤重疊。

爲了解決如果你想定製插圖和你的目標API等級> = 21,你可以做到這一點,而無需創建一個自定義視圖組這個問題檢查這個answer

0

。通過設置fitsSystemWindows默認情況下,填充將應用於您的容器視圖,您可能不需要。

版本檢查內置在該方法和設備僅> = 21將執行拉姆達內的代碼。科特林例如:

ViewCompat.setOnApplyWindowInsetsListener(container) { view, insets -> 
    insets.replaceSystemWindowInsets(0, 0, 0, insets.systemWindowInsetBottom).apply { 
    ViewCompat.onApplyWindowInsets(view, this) 
    } 
} 

確保您的佈局仍然設置fitsSystemWindows標誌,否則窗口插圖監聽器不會被調用。

<FrameLayout 
    android:id="@+id/container" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent" 
    android:fitsSystemWindows="true" 
    /> 

這些來源是有幫助的:

https://medium.com/google-developers/why-would-i-want-to-fitssystemwindows-4e26d9ce1eec https://medium.com/@azizbekian/windowinsets-24e241d4afb9