Skip to content

Instantly share code, notes, and snippets.

@cbeyls
Last active February 5, 2020 10:58
Show Gist options
  • Star 34 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save cbeyls/ab6903e103475bd4d51b to your computer and use it in GitHub Desktop.
Save cbeyls/ab6903e103475bd4d51b to your computer and use it in GitHub Desktop.
A fragment container enabling the use of android:fitsSystemWindows in fragment layouts.
package be.digitalia.common.widgets;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Rect;
import android.os.Build;
import android.support.v4.util.ObjectsCompat;
import android.util.AttributeSet;
import android.view.View;
import android.view.WindowInsets;
import android.widget.FrameLayout;
/**
* A FrameLayout which memorizes the window insets and propagates them to child views before they are measured.
* You can use this layout as a fragment container in place of a standard FrameLayout to
* propagate window insets to attached fragments.
*
* @author Christophe Beyls
*/
public class WindowInsetsFrameLayout extends FrameLayout {
private Object mLastInsets;
public WindowInsetsFrameLayout(Context context) {
super(context);
}
public WindowInsetsFrameLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public WindowInsetsFrameLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@TargetApi(Build.VERSION_CODES.KITKAT_WATCH)
@Override
public WindowInsets onApplyWindowInsets(WindowInsets insets) {
if (!ObjectsCompat.equals(mLastInsets, insets)) {
mLastInsets = insets;
requestLayout();
}
return insets.consumeSystemWindowInsets();
}
@SuppressWarnings("deprecation")
@Override
protected boolean fitSystemWindows(Rect insets) {
if (!ObjectsCompat.equals(mLastInsets, insets)) {
if (mLastInsets == null) {
mLastInsets = new Rect(insets);
} else {
((Rect) mLastInsets).set(insets);
}
requestLayout();
}
return true;
}
@SuppressLint("DrawAllocation")
@SuppressWarnings("deprecation")
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (mLastInsets != null) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH) {
final WindowInsets wi = (WindowInsets) mLastInsets;
final int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
final View child = getChildAt(i);
if (child.getVisibility() != GONE) {
child.dispatchApplyWindowInsets(wi);
}
}
} else {
super.fitSystemWindows(new Rect((Rect) mLastInsets));
}
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
@IlyaEremin
Copy link

How I can use it? I need to put fragment container inside this view? Or I make root fragment view FitsSystemWindowsFrameLayout

@vnzlogin
Copy link

How I can use it? I need to put fragment container inside this view? Or I make root fragment view FitsSystemWindowsFrameLayout

<yourpackge.FitsSystemWindowsFrameLayout
...

.
..
</yourpackge.FitsSystemWindowsFrameLayout

@cbeyls
Copy link
Author

cbeyls commented Aug 27, 2016

This view itself needs to be used as Fragment container.

@shawnlinboy
Copy link

Works like magic. THX :)

@onur-ozdemir
Copy link

thank you. It's work :)

@wzhangmoumou
Copy link

it doesn't worked for me,but thinks too

@Yazon2006
Copy link

It seems to me that field private Rect tempInsets = new Rect(); is redundant. Isn't it?

@cbeyls
Copy link
Author

cbeyls commented Aug 9, 2018

@Yazon2006 First we need to make a copy of the insets as they arrive, because sibling views may modify them and we need to keep the value as we got it.
Then, when redispatching the insets, the child views may modify them as well and we need to make sure that on the next dispatch, we start with the same value as before. Hence for safety we need to make a second copy of the saved insets before each dispatch.

@cbeyls
Copy link
Author

cbeyls commented Aug 9, 2018

I rewrote this class recently, to optimize it for API 20+ while keeping compatibility with older Android versions.
On API 20+ onApplyWindowInsets() will be called. These insets are read-only so we just pass them later as-is to child views without having to do any copy or transformation.
On older versions fitSystemWindows() will be called, and the class makes two defensive copies of the insets to avoid issues if they are modified by other views.
The insets are not passed to child views anymore when they arrive initially, or when a child view is added. Instead they are now dispatched to all child views at once in onMeasure() right before measuring. This behavior is similar to the DrawerLayout implementation.

@afmjoaa
Copy link

afmjoaa commented Aug 10, 2018

Awesome work Bruh ...

@YuriPopiv
Copy link

Just amazing
one problem - onMeasure is triggered 100 times

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment