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" />
I finally managed to figure this out.
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 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 :)