Skip to content

Instantly share code, notes, and snippets.

@kjeremy
Last active August 29, 2015 14:14
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save kjeremy/5b76cb2eb82ff6caa046 to your computer and use it in GitHub Desktop.
Save kjeremy/5b76cb2eb82ff6caa046 to your computer and use it in GitHub Desktop.
AppCompat-v22 Tinting for MvvmCross
/// <summary>
/// Tint-aware version of MvxSpinner so that it's styled properly
/// with AppCompat V21
/// </summary>
public class MvxAppCompatSpinner : MvxSpinner, ITintableBackgroundView
{
private static readonly int[] TintAttrs =
{
Android.Resource.Attribute.Background,
Android.Resource.Attribute.PopupBackground
};
private TintInfo _backgroundTint;
public MvxAppCompatSpinner(Context context)
: this(context, null) {}
public MvxAppCompatSpinner(Context context, IAttributeSet attributes)
: base(context, attributes)
{
if (TintManager.ShouldBeUsed)
{
TintTypedArray a = TintTypedArray.ObtainStyledAttributes(context, attributes,
TintAttrs, Android.Resource.Attribute.SpinnerStyle, 0);
if (a.HasValue(0))
{
ColorStateList tint = a.TintManager.GetTintList(a.GetResourceId(0, -1));
if (tint != null)
{
SupportBackgroundTintList = tint;
}
}
if (a.HasValue(1))
{
Drawable popupBackground = a.GetDrawable(1);
if ((int)Build.VERSION.SdkInt >= 16)
{
SetPopupBackgroundDrawable(popupBackground);
}
else if ((int)Build.VERSION.SdkInt >= 11)
{
SetPopupBackgroundDrawableV11(this, popupBackground);
}
}
a.Recycle();
}
}
[TargetApi(Value = (int)BuildVersionCodes.Honeycomb)]
private static void SetPopupBackgroundDrawableV11(Spinner view, Drawable background)
{
try
{
Field popupField = Class.FromType(typeof(Spinner)).GetDeclaredField("mPopup");
popupField.Accessible = true;
Object popup = popupField.Get(view);
if (popup is ListPopupWindow)
{
((ListPopupWindow)popup).SetBackgroundDrawable(background);
}
}
catch (NoSuchFieldException e)
{
e.PrintStackTrace();
}
catch (IllegalAccessException e)
{
e.PrintStackTrace();
}
}
public ColorStateList SupportBackgroundTintList
{
get { return _backgroundTint != null ? _backgroundTint.MTintList : null; }
set
{
if (_backgroundTint == null)
{
_backgroundTint = new TintInfo();
}
_backgroundTint.MTintList = value;
_backgroundTint.MHasTintList = true;
ApplySupportBackgroundTint();
}
}
public PorterDuff.Mode SupportBackgroundTintMode
{
get { return _backgroundTint != null ? _backgroundTint.MTintMode : null; }
set
{
if (_backgroundTint == null)
{
_backgroundTint = new TintInfo();
}
_backgroundTint.MTintMode = value;
_backgroundTint.MHasTintMode = true;
ApplySupportBackgroundTint();
}
}
protected override void DrawableStateChanged()
{
base.DrawableStateChanged();
ApplySupportBackgroundTint();
}
private void ApplySupportBackgroundTint()
{
if (Background != null && _backgroundTint != null)
TintManager.TintViewBackground(this, _backgroundTint);
}
}
public class Setup : MvxAndroidSetup
{
public Setup(Context applicationContext) : base(applicationContext)
{
}
protected override IMvxApplication CreateApp()
{
return new App();
}
protected override IMvxTrace CreateDebugTrace()
{
return new DebugTrace();
}
protected override IList<Assembly> AndroidViewAssemblies
{
get
{
var toReturn = base.AndroidViewAssemblies;
// Add assemblies with other views we use. When the XML is inflated
// MvvmCross knows about the types and won't compain about them. This
// speeds up inflation noticeably.
toReturn.Add(typeof(DrawerLayout).Assembly);
toReturn.Add(typeof(SwitchCompat).Assembly);
return toReturn;
}
}
public class AppCompatViewFactory : MvxAndroidViewFactory
{
public override View CreateView(View parent, string name, Context context, IAttributeSet attrs)
{
if (Build.VERSION.SdkInt < BuildVersionCodes.Lollipop)
{
// If we're running pre-L, we need to 'inject' our tint aware Views in place of the
// standard framework versions
switch (name)
{
case "EditText":
return new AppCompatEditText(context, attrs);
case "MvxSpinner": // Use our own tint-aware version of MvxSpinner
return new MvxAppCompatSpinner(context, attrs);
case "Spinner":
return new AppCompatSpinner(context, attrs);
case "CheckBox":
return new AppCompatCheckBox(context, attrs);
case "RadioButton":
return new AppCompatRadioButton(context, attrs);
case "CheckedTextView":
return new AppCompatCheckedTextView(context, attrs);
case "AutoCompleteTextView":
return new AppCompatAutoCompleteTextView(context, attrs);
case "MultiAutoCompleteTextView":
return new AppCompatMultiAutoCompleteTextView(context, attrs);
case "RatingBar":
return new AppCompatRatingBar(context, attrs);
case "Button":
return new AppCompatButton(context, attrs);
}
}
return base.CreateView(parent, name, context, attrs);
}
}
public class BindingBuilder : MvxAndroidBindingBuilder
{
protected override IMvxAndroidViewFactory CreateAndroidViewFactory()
{
return new AppCompatViewFactory();
}
}
protected override MvxAndroidBindingBuilder CreateBindingBuilder()
{
return new BindingBuilder();
}
}
@kjeremy
Copy link
Author

kjeremy commented Jun 2, 2015

Alternatively one can discriminate on the Context. This version is more forward compatible: for instance theme inheritance with v22.1 will work

private class AppCompat21ViewFactory : MvxAndroidViewFactory
{
            private static AppCompatDelegate GetDelegate(Context context)
            {
                // Special case this for when you know you have an AppCompatDelegate
                AppCompatActivity activity = context as AppCompatActivity;
                if (activity != null)
                {
                    return activity.Delegate;
                }

                return null;
            }

            public override View CreateView(View parent, string name, Context context, IAttributeSet attrs)
            {
                var @delegate = GetDelegate(context);
                if (@delegate != null)
                {
                    var view = @delegate.CreateView(parent, name, context, attrs);
                    if (view != null)
                        return view;

                    if (name == "MvxSpinner")
                    {
                        return new MvxAppCompatSpinner(context, attrs);
                    }
                }

                return base.CreateView(parent, name, context, attrs);
            }
        }

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