代码之家  ›  专栏  ›  技术社区  ›  wainy

MVVMCross自定义控件和绑定

  •  2
  • wainy  · 技术社区  · 10 年前

    我创建了一个自定义控件(CustomCard),它是CardView控件的子类。我希望在不同的地方在我的项目中使用此控件。

    例如,我可以手动将CustomCard放置在xml布局中,或者我可能希望CustomCard是MvxListView中的一个项目。关键是我希望尽可能多地重用代码,并从对CustomCard类的控制中获益。

    当CustomCard被实例化时,我正在使用标准布局充气器对其布局进行充气,请参见代码:

    using System;
    using Android.Animation;
    using Android.Content;
    using Android.Support.V7.Widget;
    using Android.Util;
    using Android.Views;
    using Android.Widget;
    public class Card : CardView
    {
    
        private readonly Context _context;
    
        public Card(Context context)
            : base(context)
        {
            _context = context;
            Init();
        }
    
        public Card(Context context, IAttributeSet attrs)
            : base(context, attrs)
        {
            _context = context;
            Init();
        }
    
        private void Init()
        {
            var inflater = (LayoutInflater) _context.GetSystemService(Context.LayoutInflaterService);
            CardView = inflater.Inflate(Resource.Layout.base_card, this);
        }
    }
    

    在布局base_card.xml中,我想使用MVVMCross绑定一些元素,例如,

    <?xml version="1.0" encoding="utf-8"?>
    <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:local="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@android:color/white">
    <RelativeLayout
      xmlns:android="http://schemas.android.com/apk/res/android"
      xmlns:local="http://schemas.android.com/apk/res-auto"
      android:id="@+id/basecard_title"
      android:layout_width="match_parent"
      android:layout_height="match_parent">
        <!-- Title Text-->
        <TextView
        android:id="@+id/tv_basecard_header_title"
        style="@style/card.title"
        android:text="title text"
        local:MvxBind="Text Title"
        />
        <!-- ImageView -->
        <MvxImageView
        android:id="@+id/ib_basecard_header_button_expand"
        style="@style/card.image"
        local:MvxBind="Bitmap ImageBytes,Converter=InMemoryImage"/>
      </RelativeLayout>
    </FrameLayout>
    

    我的实际base_card布局要复杂得多。

    如果我尝试在另一个XML布局中使用CustomCard,则不会发生任何绑定。我想这是因为我使用的是标准布局充气器,而不是BindingInflation(),在CustomCard中对base_card进行充气,但我不能确定。

    我在SO和论坛上搜索过,但我找不到任何使用自定义控件的人的引用,当使用MVVMCross绑定实例化时,自定义控件会膨胀自己的视图。

    有人做过吗,或者我想做一些不可能的事?

    1 回复  |  直到 10 年前
        1
  •  9
  •   Ondřej Kunc    9 年前

    我遇到了CardView控件的类似问题。由于CardView直接继承自FrameLayout,我决定使用与MvxFrameControl几乎相同的实现(感谢Stuart指出MvxFrameControl示例):

    public class MvxCardView : CardView, IMvxBindingContextOwner
        {
            private object _cachedDataContext;
            private bool _isAttachedToWindow;
            private readonly int _templateId;
            private readonly IMvxAndroidBindingContext _bindingContext;
    
            public MvxCardView(Context context, IAttributeSet attrs)
                : this(MvxAttributeHelpers.ReadTemplateId(context, attrs), context, attrs)
            {
            }
    
            public MvxCardView(int templateId, Context context, IAttributeSet attrs)
                : base(context, attrs)
            {
                _templateId = templateId;
    
                if (!(context is IMvxLayoutInflater))
                {
                    throw Mvx.Exception("The owning Context for a MvxCardView must implement LayoutInflater");
                }
    
                _bindingContext = new MvxAndroidBindingContext(context, (IMvxLayoutInflater)context);
                this.DelayBind(() =>
                {
                    if (Content == null && _templateId != 0)
                    {
                        Mvx.Trace("DataContext is {0}", DataContext == null ? "Null" : DataContext.ToString());
                        Content = _bindingContext.BindingInflate(_templateId, this);
                    }
                });
            }
    
    
            protected MvxCardView(IntPtr javaReference, JniHandleOwnership transfer)
                : base(javaReference, transfer)
            {
            }
    
            protected IMvxAndroidBindingContext AndroidBindingContext
            {
                get { return _bindingContext; }
            }
    
            public IMvxBindingContext BindingContext
            {
                get { return _bindingContext; }
                set { throw new NotImplementedException("BindingContext is readonly in the list item"); }
            }
    
            protected View Content { get; set; }
    
            protected override void Dispose(bool disposing)
            {
                if (disposing)
                {
                    this.ClearAllBindings();
                    _cachedDataContext = null;
                }
    
                base.Dispose(disposing);
            }
    
            protected override void OnAttachedToWindow()
            {
                base.OnAttachedToWindow();
                _isAttachedToWindow = true;
                if (_cachedDataContext != null
                    && DataContext == null)
                {
                    DataContext = _cachedDataContext;
                }
            }
    
            protected override void OnDetachedFromWindow()
            {
                _cachedDataContext = DataContext;
                DataContext = null;
                base.OnDetachedFromWindow();
                _isAttachedToWindow = false;
            }
    
            [MvxSetToNullAfterBinding]
            public object DataContext
            {
                get { return _bindingContext.DataContext; }
                set
                {
                    if (_isAttachedToWindow)
                    {
                        _bindingContext.DataContext = value;
                    }
                    else
                    {
                        _cachedDataContext = value;
                        if (_bindingContext.DataContext != null)
                        {
                            _bindingContext.DataContext = null;
                        }
                    }
                }
            }
        }
    

    用法:

    <YourNamespace.MvxCardView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        local:MvxTemplate="@layout/base_card"
        local:MvxBind="DataContext ." />
    

    注意:使用自定义实现也解决了我使用 local:MvxBind="Click MyCommand" ,直到CardView子类化后才开始工作。