14

My app has one activity that hosts different fragments for each section. I have recently made the status bar translucent by setting fitSystemWindows to true, which has set it to the background colour of the app. This is fine for fragments that have a toolbar, where the colours match, like so:

enter image description here

However one of my fragments has a photo and a translucent toolbar, so I'd like to have the photo occupy the space of the status bar too, rather than the background colour.

I believe the solution is to set fitSystemWindows to false for that fragment only, and manually add padding to the translucent toolbar. Doing this programmatically seems to have no effect, what could I be doing wrong?

Here is my main activity layout:

<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/main_parent_view"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:fitsSystemWindows="true">

<!-- Container for various fragment layouts, including nav drawer and toolbar -->

</RelativeLayout>

And from within my fragment's onCreateView():

RelativeLayout daddyLayout = (RelativeLayout)getActivity().findViewById(R.id.main_parent_view);
daddyLayout.setFitsSystemWindows(false);
daddyLayout.invalidate();

This seems to have no effect, like so:

enter image description here

If I set fitSystemWindows to false in the main_parent_view, the status bar padding is gone and it works but obviously affects every fragment.

Rahul
  • 3,293
  • 2
  • 31
  • 43
Daniel Wilson
  • 18,838
  • 12
  • 85
  • 135
  • 1
    possible duplicate of [Android 4.4 — Translucent status/navigation bars — fitsSystemWindows/clipToPadding don't work through fragment transactions](http://stackoverflow.com/questions/20822418/android-4-4-translucent-status-navigation-bars-fitssystemwindows-cliptopaddi) – corsair992 Feb 09 '15 at 06:06
  • That user's issue is that `fitSystemWindows()` is reset after a fragment transaction occurs, whereas my issue is that I seem unable to **toggle** `fitSystemWindows()` programmatically. Still quite similar, but none of those answers have helped, very strange and annoying... – Daniel Wilson Feb 13 '15 at 01:30
  • @DanielWilson: Did you find any solution for this issue? – Roadblock Dec 12 '16 at 06:28
  • Wow this was a long time ago :) I definitely suggest looking at https://github.com/jgilfelt/SystemBarTint - although deprecated now it made a lot of this sort of stuff 'just work' for me I think – Daniel Wilson Dec 12 '16 at 11:47

5 Answers5

17

Well, you are in dilemma situation there, because from one hand you need to apply insets (because Toolbar should be correctly padded), and on the other hand you should not apply insets (because you want ImageView to be drawn under status bar).

Turns out there's a nice API provided by the framework for that case:

ViewCompat.setOnApplyWindowInsetsListener(toolbar, (v, insets) -> {
    ((ViewGroup.MarginLayoutParams) v.getLayoutParams()).topMargin =
            insets.getSystemWindowInsetTop();
    return insets.consumeSystemWindowInsets();
});

Assuming your root layout has android:fitsSystemWindows="true", now appropriate insets would be applied to your Toolbar only, and not the ImageView.

But, there's a problem.

The problem is that your root layout is RelativeLayout, which doesn't dispatch its children any information about insets. Neither do its sibling layouts (LinearLayout, FrameLayout).

If you had as a root layout one of "materialish" layouts (CoordinatorLayout, DrawerLayout), then children would be dispatched those window insets.

The other option is to subclass RelativeLayout and dispatch WindowInsets to children manually.

@TargetApi(Build.VERSION_CODES.KITKAT_WATCH)
@Override
public WindowInsets onApplyWindowInsets(WindowInsets insets) {
    int childCount = getChildCount();
    for (int index = 0; index < childCount; index++)
        getChildAt(index).dispatchApplyWindowInsets(insets); // let children know about WindowInsets

    return insets;
}

You can see this answer for a detailed explanation with precisely same requirement you have.

enter image description here

Community
  • 1
  • 1
azizbekian
  • 60,783
  • 13
  • 169
  • 249
7

I have resolve this question in 4.4

if(test){
    Log.d(TAG, "fit true ");
    relativeLayout.setFitsSystemWindows(true);
    relativeLayout.requestFitSystemWindows();
    getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
}else {
    Log.d(TAG, "fit false");
    relativeLayout.setFitsSystemWindows(false);
    relativeLayout.requestFitSystemWindows();
    getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
}
Kejie Yuan
  • 71
  • 1
  • 3
5

You can use a CoordinatorLayout as your activity root view and then setFitsSystemWindows(boolean) will work.

This is because, as explained in this blog post , DrawerLayout and CoordinatorLayout both have different rules on how fitsSystemWindows applies to them - they both use it to inset their child Views, but also call dispatchApplyWindowInsets() on each child, allowing them access to the fitsSystemWindows="true" property.

This is a difference from the default behavior with layouts such as FrameLayout where when you use fitsSystemWindows="true" is consumes all insets, blindly applying padding without informing any child views (that's the 'depth first' part of the blog post).

John
  • 1,304
  • 1
  • 9
  • 17
3

Saw the same issue. Solved it in my app by removing fitSystemWindows from the activity declaration and adding paddingTop to the fragment. Obviously not an ideal solution but seems to be working.

vkislicins
  • 3,331
  • 3
  • 32
  • 62
3

simply put

 View decorView = getActivity().getWindow().getDecorView();
    decorView.setSystemUiVisibility(
            View.SYSTEM_UI_FLAG_LAYOUT_STABLE
                    | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);

and for status bar color use:

getActivity().getWindow().setBackgroundDrawableResource(R.color.green);