Search code examples
androidandroid-activitywindow-soft-input-mode

How to force windowSoftInputMode adjustResize when statusBar is hidden


I have been struggling to put this to work. The app resizes well as long as i keep the status bar visible. According to this post it is an android bug, but the issue is closed but not resolved.

From all that i read, playing with Insets should do the trick but i can't put it to work. Not sure even if should go this way.

Following this post i added the below code to OnCreate.

ViewCompat.setOnApplyWindowInsetsListener(getWindow().getDecorView().getRootView(), new OnApplyWindowInsetsListener() {
    @Override
    public WindowInsetsCompat onApplyWindowInsets(View view, WindowInsetsCompat insets) {

        ((ViewGroup.MarginLayoutParams) view.getLayoutParams()).topMargin =
            insets.getSystemWindowInsetTop();

        return insets.consumeSystemWindowInsets();
   }   
});     

It throws the exception:

java.lang.ClassCastException: android.view.WindowManager$LayoutParams cannot be cast to android.view.ViewGroup$MarginLayoutParams

The post goes on but i am stuck here and have doubts if i am going the right way.

Bellow the testing code i am using if someone wants to try. Works well until the statusbar is removed by adding <item name="android:windowFullscreen">true</item> to AppTheme.

AndroidManifest.xml:

<application
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:roundIcon="@mipmap/ic_launcher_round"
    android:supportsRtl="true"
    android:theme="@style/AppTheme">

    <activity android:name=".MainActivity"
        android:windowSoftInputMode="adjustResize"> 
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
</application>

activity_mail.xml:

<!-- this control should be position static -->
    <TextView
        android:layout_height="50dp"
        android:layout_width="fill_parent"
        android:background="@color/colorPrimaryDark"
        android:text="static window at top"
        android:gravity="center"
        />

<!-- the resizable view  -->
<android.support.v7.widget.RecyclerView
    android:id="@+id/my_list"
    android:layout_width="match_parent"
    android:layout_height="0dp"
    android:layout_weight="1"
    android:transcriptMode="normal">
</android.support.v7.widget.RecyclerView>

<!-- use this EditText box to call softKeyboard-->
<android.support.v7.widget.AppCompatEditText
    android:id="@+id/cv_bottom_bar_edit"
    style="@style/Widget.AppCompat.AutoCompleteTextView"
    android:layout_width="match_parent"
    android:layout_height="50dp"
    android:textSize="18sp"
    android:background="@color/colorPrimaryDark"
    android:gravity="center"
    />

row.xml:

<?xml version="1.0" encoding="utf-8"?>
<TextView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="#F06060"
    android:layout_margin="30dp"
    android:padding="5dp"
    android:textSize="25sp"
    android:gravity="center"
>  

MainActivity.java:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    mRecyclerView = findViewById(R.id.my_list);
    mRecyclerView.setHasFixedSize(true);

    mLayoutManager = new LinearLayoutManager(this);
    mRecyclerView.setLayoutManager(mLayoutManager);

    String[] myDataset = new String[]{"1", "2", "3","4","5","6","7","8","9"};

    mAdapter = new MyAdapter(myDataset);
    mRecyclerView.setAdapter(mAdapter);

}

Update: I moved the windowFullscreen attribute from manifest to AppTheme because Android was not reading it from the manifest.


Solution

  • public class AndroidBug5497Workaround extends AppCompatActivity {
    
    // 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.
    
    private ViewGroup contentContainer;
    private ViewTreeObserver viewTreeObserver;
    private Rect contentAreaOfWindowBounds = new Rect();
    private LinearLayout.LayoutParams rootViewLayout; //-->change to the view root control type of your view.
    private int usableHeightPrevious = 0;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        contentContainer = findViewById(android.R.id.content);
        rootViewLayout = (LinearLayout.LayoutParams) contentContainer.getLayoutParams();
    }
    
    @Override
    protected void onPause() {
        super.onPause();
        if (viewTreeObserver.isAlive()) {
    
            viewTreeObserver.removeOnGlobalLayoutListener(listener);
        }
    }
    
    @Override
    protected void onResume() {
        super.onResume();
        if (viewTreeObserver == null || !viewTreeObserver.isAlive()) {
            viewTreeObserver = contentContainer.getViewTreeObserver();
        }
    
        viewTreeObserver.addOnGlobalLayoutListener(listener);
    }
    
    @Override
    protected void onDestroy() {
        super.onDestroy();
        contentContainer = null;
        viewTreeObserver = null;
    }
    
    ViewTreeObserver.OnGlobalLayoutListener listener = new ViewTreeObserver.OnGlobalLayoutListener() {
        @Override
        public void onGlobalLayout() {
            contentContainer.getWindowVisibleDisplayFrame(contentAreaOfWindowBounds);
            int usableHeightNow = contentAreaOfWindowBounds.height();
    
            if (usableHeightNow != usableHeightPrevious) {
                rootViewLayout.height = usableHeightNow;
                contentContainer.layout(contentAreaOfWindowBounds.left, contentAreaOfWindowBounds.top, contentAreaOfWindowBounds.right, contentAreaOfWindowBounds.bottom);
                contentContainer.requestLayout();
    
                usableHeightPrevious = usableHeightNow;
            }
        }
    };
    

    }

    Got it from facebook/react-native issue on github. Slight simplified for a common activity. Works good with portrait and landscape, from api 21, but it not much more tested than that. Use with care.