Skip to content

Instantly share code, notes, and snippets.

@entdark
Created February 9, 2024 12:30
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save entdark/88dfb3dcb7b0065cced4555b62162f0e to your computer and use it in GitHub Desktop.
Save entdark/88dfb3dcb7b0065cced4555b62162f0e to your computer and use it in GitHub Desktop.
.NET Android MvvmCross SystemBars Insets (SafeArea) XML injector
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="View">
<attr name="paddingLeftFitsWindowInsets" format="boolean"/>
<attr name="paddingRightFitsWindowInsets" format="boolean"/>
<attr name="paddingTopFitsWindowInsets" format="boolean"/>
<attr name="paddingBottomFitsWindowInsets" format="boolean"/>
<attr name="marginLeftFitsWindowInsets" format="boolean"/>
<attr name="marginRightFitsWindowInsets" format="boolean"/>
<attr name="marginTopFitsWindowInsets" format="boolean"/>
<attr name="marginBottomFitsWindowInsets" format="boolean"/>
</declare-styleable>
</resources>
using Android.OS;
using Google.Android.Material.Internal;
using MvvmCross.Platforms.Android.Views;
namespace APP_NAMESPACE;
public class HostActivityExample : MvxActivity {
protected override void OnCreate(Bundle savedInstanceState) {
base.OnCreate(savedInstanceState);
var view = this.BindingInflate(Resource.Layout.page_example, null);
SetContentView(view);
EdgeToEdgeUtils.ApplyEdgeToEdge(this.Window, true);
}
}
<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.materialswitch.MaterialSwitch 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"
tools:text="Text"
android:textColor="?attr/colorOnSurface"
android:textAppearance="?attr/textAppearanceBodyLarge"
android:layout_width="match_parent"
android:layout_height="48dp"
android:paddingLeft="16dp"
android:paddingRight="24dp"
android:foreground="?android:attr/selectableItemBackground"
app:paddingLeftFitsWindowInsets="true"
app:paddingRightFitsWindowInsets="true"
app:MvxBind="Text Title; Checked IsChecked"/>
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout 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:background="?android:attr/colorBackground">
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:liftOnScrollTargetViewId="@id/mvxrecyclerview"
app:paddingLeftFitsWindowInsets="true"
app:paddingRightFitsWindowInsets="true"
app:paddingTopFitsWindowInsets="true">
<include layout="@layout/toolbar"/>
</com.google.android.material.appbar.AppBarLayout>
<mvvmcross.droidx.recyclerview.MvxRecyclerView
android:id="@+id/mvxrecyclerview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
app:paddingBottomFitsWindowInsets="true"
app:MvxItemTemplate="@layout/item_example"
app:MvxBind="ItemsSource Items; ItemClick ItemClickCommand"/>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
using System;
using Android.Content;
using Android.Views;
using AndroidX.Core.View;
using Google.Android.Material.Internal;
using MvvmCross.Binding;
using MvvmCross.Platforms.Android.Binding;
using MvvmCross.Platforms.Android.Binding.Binders;
using MvvmCross.Platforms.Android.Core;
namespace APP_NAMESPACE;
public class Setup : MvxAndroidSetup<App> {
protected override MvxBindingBuilder CreateBindingBuilder() {
return new InsetsAndroidBindingBuilder();
}
private class InsetsAndroidBindingBuilder : MvxAndroidBindingBuilder {
protected override IMvxLayoutInflaterHolderFactoryFactory CreateLayoutInflaterFactoryFactory() {
return new InsetsLayoutInflaterFactoryFactory();
}
private class InsetsLayoutInflaterFactoryFactory : IMvxLayoutInflaterHolderFactoryFactory {
public IMvxLayoutInflaterHolderFactory Create(object source) {
return new InsetsBindingLayoutInflaterFactory(source);
}
private class InsetsBindingLayoutInflaterFactory : MvxBindingLayoutInflaterFactory {
public InsetsBindingLayoutInflaterFactory(object source) : base(source) {
}
public override View BindCreatedView(View view, Context context, global::Android.Util.IAttributeSet attrs) {
using var typedArray = context.ObtainStyledAttributes(attrs, Resource.Styleable.View);
int numStyles = typedArray.IndexCount;
var flags = WindowInsetsFlags.None;
for (var i = 0; i < numStyles; ++i) {
var attributeId = typedArray.GetIndex(i);
bool attributeValue = typedArray.GetBoolean(attributeId, false);
if (attributeValue) {
flags |= attributeId switch {
Resource.Styleable.View_paddingLeftFitsWindowInsets => WindowInsetsFlags.PaddingLeft,
Resource.Styleable.View_paddingRightFitsWindowInsets => WindowInsetsFlags.PaddingRight,
Resource.Styleable.View_paddingTopFitsWindowInsets => WindowInsetsFlags.PaddingTop,
Resource.Styleable.View_paddingBottomFitsWindowInsets => WindowInsetsFlags.PaddingBottom,
Resource.Styleable.View_marginLeftFitsWindowInsets => WindowInsetsFlags.MarginLeft,
Resource.Styleable.View_marginRightFitsWindowInsets => WindowInsetsFlags.MarginRight,
Resource.Styleable.View_marginTopFitsWindowInsets => WindowInsetsFlags.MarginTop,
Resource.Styleable.View_marginBottomFitsWindowInsets => WindowInsetsFlags.MarginBottom,
_ => WindowInsetsFlags.None
};
}
}
if (flags != WindowInsetsFlags.None)
ViewUtils.DoOnApplyWindowInsets(view, new OnApplyWindowInsetsListener(flags));
typedArray.Recycle();
return base.BindCreatedView(view, context, attrs);
}
}
}
private class OnApplyWindowInsetsListener : Java.Lang.Object, ViewUtils.IOnApplyWindowInsetsListener {
private readonly WindowInsetsFlags flags;
private ViewGroup.MarginLayoutParams initialLayoutParameters;
public OnApplyWindowInsetsListener(WindowInsetsFlags flags) {
this.flags = flags;
}
public WindowInsetsCompat OnApplyWindowInsets(View view, WindowInsetsCompat insetsCompat, ViewUtils.RelativePadding initialPadding) {
bool paddingTop = flags.HasFlag(WindowInsetsFlags.PaddingTop);
bool paddingBottom = flags.HasFlag(WindowInsetsFlags.PaddingBottom);
bool paddingLeft = flags.HasFlag(WindowInsetsFlags.PaddingLeft);
bool paddingRight = flags.HasFlag(WindowInsetsFlags.PaddingRight);
var insets = insetsCompat.GetInsets(WindowInsetsCompat.Type.SystemBars());
int insetTop = insets.Top;
int insetBottom = insets.Bottom;
int insetLeft = insets.Left;
int insetRight = insets.Right;
bool isRtl = ViewCompat.GetLayoutDirection(view) == ViewCompat.LayoutDirectionRtl;
initialPadding.Top += paddingTop ? insetTop : 0;
initialPadding.Bottom += paddingBottom ? insetBottom : 0;
int systemWindowInsetLeft = paddingLeft ? insetLeft : 0;
int systemWindowInsetRight = paddingRight ? insetRight : 0;
initialPadding.Start += isRtl ? systemWindowInsetRight : systemWindowInsetLeft;
initialPadding.End += isRtl ? systemWindowInsetLeft : systemWindowInsetRight;
initialPadding.ApplyToView(view);
if (view.LayoutParameters is ViewGroup.MarginLayoutParams marginLayoutParameters && initialLayoutParameters == null) {
initialLayoutParameters = new ViewGroup.MarginLayoutParams(marginLayoutParameters);
}
if (initialLayoutParameters != null) {
bool marginTop = flags.HasFlag(WindowInsetsFlags.MarginTop);
bool marginBottom = flags.HasFlag(WindowInsetsFlags.MarginBottom);
bool marginLeft = flags.HasFlag(WindowInsetsFlags.MarginLeft);
bool marginRight = flags.HasFlag(WindowInsetsFlags.MarginRight);
if (marginTop || marginBottom || marginLeft || marginRight) {
var newLayoutParameters = view.LayoutParameters as ViewGroup.MarginLayoutParams;
newLayoutParameters.TopMargin = initialLayoutParameters.TopMargin + (marginTop ? insetTop : 0);
newLayoutParameters.BottomMargin = initialLayoutParameters.BottomMargin + (marginBottom ? insetBottom : 0);
newLayoutParameters.LeftMargin = initialLayoutParameters.LeftMargin + (marginLeft ? insetLeft : 0);
newLayoutParameters.RightMargin = initialLayoutParameters.RightMargin + (marginRight ? insetRight : 0);
view.LayoutParameters = newLayoutParameters;
}
}
return insetsCompat;
}
}
[Flags]
private enum WindowInsetsFlags {
None = 0,
PaddingLeft = 1 << 0,
PaddingRight = 1 << 1,
PaddingTop = 1 << 2,
PaddingBottom = 1 << 3,
MarginLeft = 1 << 4,
MarginRight = 1 << 5,
MarginTop = 1 << 6,
MarginBottom = 1 << 7
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment