Search code examples
androidxamarinattributesmvvmcrossinflate

Getting IAttributeSet From View Inflation Process


I'm working on a project that relies on the user adding a custom attribute to their Android layout elements. Just like MvvmCross' app:MvxBind. There are no custom view classes as the idea is that the user can use the normal Android views.

The problem is that in order to get the value of this tag I need to get the IAttributeSet that is used during the view inflation process and I can't find a method of doing so that suits my needs.

I have a working example using LayoutInflater.IFactory however, this requires me to set my own LayoutInflater/factory which, if the user is using a library such as MvvmCross, causes problems as only one factory can be set at once.

I'm looking for a way that I can get the IAttributeSet object whenever a view is inflated to check for my attribute that doesn't interfere with the standard LayoutInflater or LayoutInflater's from other libraries. Or if there is any way to get my attribute after the view has been inflated.

Thanks in advance!

Edit

I want to be able to get the value of MyAttribute from a view without subclassing views or creating custom views. This is easily accomplished with LayoutInflater.IFactory but this method interferes with libraries such as MvvmCross.

<TextView
    android:layout_width="wrap_content"
    android:layout_width="wrap_content"
    app:MyAttribute="My attribute value" />

Solution

  • I finally managed to figure this out.

    Non-MvvmCross Solution

    I stumbled across an article called "Layout Inflater: Friend or Foe?". I think this is the link but at the time of posting this answer it isn't working.

    The author did an amazing talk on LayoutInflater and how he changed the Android LayoutInflater process so that he could intercept it for his library Calligraphy. The resulting solution is called ViewPump and it's written in Kotlin.

    I have written the ViewPump library in Xamarin for use with non-MvvmCross projects: https://github.com/lewisbennett/viewpump.

    MvvmCross Solution

    MvvmCross uses a solution based on InflationX' ViewPump to do its binding and we can access it by first creating the below classes:

    public class BindingBuilder : MvxAndroidBindingBuilder
    {
        protected override IMvxAndroidViewBinderFactory CreateAndroidViewBinderFactory()
        {
            return new ViewBinderFactory();
        }
    }
    
    public class ViewBinderFactory : IMvxAndroidViewBinderFactory
    {
        public IMvxAndroidViewBinder Create(object source)
        {
            return new ViewBinder(source);
        }
    }
    
    public class ViewBinder : MvxAndroidViewBinder
    {
        public override void BindView(View view, Context context, IAttributeSet attrs)
        {
            base.BindView(view, context, attrs);
    
            // Do your intercepting here.
        }
    
        public ViewBinder(object source)
            : base(source)
        {
        }
    }
    

    Then in your MvxAndroidSetup or MvxAppCompatSetup class add the following:

    protected override MvxBindingBuilder CreateBindingBuilder()
    {
        return new BindingBuilder();
    }
    

    Done! I hope this helps someone :)