从视图 Inflation 进程中获取 IAttributeSet

Getting IAttributeSet From View Inflation Process

我正在开发一个依赖于用户向其 Android 布局元素添加自定义属性的项目。就像 MvvmCross 的 app:MvxBind。没有自定义视图 类,因为用户可以使用普通的 Android 视图。

问题是,为了获取此标记的值,我需要获取在视图 inflation 过程中使用的 IAttributeSet,但我找不到适合我的方法需要。

我有一个使用 LayoutInflater.IFactory 的工作示例,但是,这需要我设置自己的 LayoutInflater/factory,如果用户使用诸​​如 MvvmCross 之类的库,则会导致问题,因为只有一个工厂可以立即设置。

我正在寻找一种方法,以便在视图膨胀时获取 IAttributeSet 对象,以检查我的属性是否不干扰标准 LayoutInflater 或来自其他库的 LayoutInflater。或者有什么方法可以在视图膨胀后获取我的属性。

提前致谢!

编辑

我希望能够从视图中获取 MyAttribute 的值,而无需子类化视图或创建自定义视图。这很容易用 LayoutInflater.IFactory 完成,但这种方法会干扰 MvvmCross 等库。

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

我不确定我是否理解你的意思。请参考以下内容:

您可以创建一个自定义视图并在 attrs.xml 中定义它的属性,当它是从 XML 布局创建时,XML 标签中的所有属性都是从资源包并作为 IAttributeSet

传递给视图的构造函数

例如,这里有一个名为 IconView 的自定义视图

public class IconView : View
{
}

attrs.xml(Resources/values/attrs.xml)

中定义一些属性
<declare-styleable name="IconView">
  <attr name="bg_color" format="color" />
  <attr name="src" format="integer" />
  <attr name="showIconLabel" format="boolean" />
  <attr name="iconLabelTextColor" format="color" />
  <attr name="iconLabelText" format="string" />
</declare-styleable>

然后我们可以在创建视图构造函数时处理属性(这里定义一个Initialize方法):

public IconView(Context context) : base(context)
{
  Initialize(context);
}

public IconView(Context context, IAttributeSet attrs) : base(context, attrs)
{
  Initialize(context, attrs);
}

private void Initialize(Context context, IAttributeSet attrs = null)
{
  if (attrs != null)
  {
    // Contains the values set for the styleable attributes you declared in your attrs.xml
    var array = context.ObtainStyledAttributes(attrs, Resource.Styleable.IconView, 0, 0);

    iconBackgroundColor = array.GetColor(Resource.Styleable.IconView_bg_color, Color.Gray);
    iconLabelTextColor = array.GetColor(Resource.Styleable.IconView_iconLabelTextColor, Color.ParseColor("#D9000000"));
    _labelText = array.GetString(Resource.Styleable.IconView_iconLabelText);
    _showIconLabel = array.GetBoolean(Resource.Styleable.IconView_showIconLabel, false);

    var iconResId = array.GetResourceId(Resource.Styleable.IconView_src, 0);
    if (iconResId != 0) // If the user actually set a drawable
        _icon = AppCompatDrawableManager.Get().GetDrawable(context, iconResId);

    // If the users sets text for the icon without setting the showIconLabel attr to true
    // set it to true for the user anyways
    if (_labelText != null) 
        _showIconLabel = true;

    // Very important to recycle the array after use
    array.Recycle();
  }

     ...
}

详情可以参考make custom view

我终于弄明白了。

非 MvvmCross 解决方案

我偶然发现了一篇名为 "Layout Inflater: Friend or Foe?" 的文章。我认为 this is the link 但在发布此答案时它不起作用。

作者对 LayoutInflater 进行了精彩的演讲,以及他如何更改 Android LayoutInflater 进程以便他可以为他的库拦截它 Calligraphy. The resulting solution is called ViewPump 并且它是用 Kotlin 编写的。

我已经在 Xamarin 中编写了用于非 MvvmCross 项目的 ViewPump 库:https://github.com/lewisbennett/viewpump

MvvmCross 解决方案

MvvmCross 使用基于 InflationX 的 ViewPump 的解决方案来进行绑定,我们可以通过首先创建以下 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)
    {
    }
}

然后在您的 MvxAndroidSetupMvxAppCompatSetup class 添加以下内容:

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

完成!我希望这对某人有所帮助:)