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

如何在Android上使用MvvmCross fluent API将RecyclerView项的TextView绑定到其ViewModel的属性?

  •  8
  • lauxjpn  · 技术社区  · 8 年前

    MvvmCross 在我 Xamarin安卓 项目我有一个 MvxActivity 带着 MvxRecyclerView ,我已在其布局文件中指定了项模板。

    <MvxRecyclerView
        android:id="@+id/my_recycler_view"
        local:MvxItemTemplate="@layout/item_recycler_view" />
    

    ViewModel非常简单,它只包含一个属性,该属性保存要在 RecyclerView :

    public class MainViewModel : MvxViewModel
    {
        private IEnumerable<ViewModelItem> _viewModelItems;
        public IEnumerable<ViewModelItem> ViewModelItems
        {
            get { return _viewModelItems; }
            set { SetProperty(ref _viewModelItems, value); }
        }    
    }
    

    通常,我喜欢尽可能多地使用MvvmCross fluent API,因为它支持隐式重构。 所以在我的活动中,我绑定了 MvxRecyclerView 这样地:

    var recyclerView = View.FindViewById<MvxRecyclerView>(Resource.Id.my_recycler_view);
    var set = this.CreateBindingSet<MainView, MainViewModel>();
    set.Bind(recyclerView)
        .For(v => v.ItemsSource)
        .To(vm => vm.ViewModelItems);
    set.Apply();
    

    到现在为止,一直都还不错。现在,项模板的布局文件基本上只包含 TextView :

    <LinearLayout>
        <TextView
            android:id="@+id/innerText" />
    </LinearLayout>
    

    还有我的 ViewModelItem

    public class ViewModelItem
    {
        public string Title { get; set; }
    }
    

    我现在的问题是,如何将 TextView.Text 财产 ViewModelItem.Title 属性是否使用fluent API?

    我知道,通过提供一个 MvxBind 属性,但我更喜欢流畅的API解决方案。

    2 回复  |  直到 8 年前
        1
  •  17
  •   Ken Kosmowski    8 年前

    继承MvxRecyclerAdapter并为您的RecyclerView创建自定义适配器。推翻 OnCreateViewHolder 并返回自定义ViewHolder。

    public class MyAdapter : MvxRecyclerAdapter
    {
        public MyAdapter(IMvxAndroidBindingContext bindingContext)
            : base(bindingContext)
        {
        }
    
        public override RecyclerView.ViewHolder OnCreateViewHolder(ViewGroup parent, int viewType)
        {
            var itemBindingContext = new MvxAndroidBindingContext(parent.Context, this.BindingContext.LayoutInflaterHolder);
            var view = this.InflateViewForHolder(parent, viewType, itemBindingContext);
    
            return new MyViewHolder(view, itemBindingContext);
        }
    }
    

    在这个ViewHolder中,您可以使用Fluent API进行绑定。

    public class MyViewHolder : MvxRecyclerViewHolder
    {
        private readonly TextView textView;
    
        public MyViewHolder(View itemView, IMvxAndroidBindingContext context)
            : base(itemView, context)
        {
            this.textView = itemView.FindViewById<TextView>(Android.Resource.Id.Text1);
    
            this.DelayBind(() =>
            {
                var set = this.CreateBindingSet<MyViewHolder, ViewModelItem>();
                set.Bind(this.textView).To(x => x.Title);
                set.Apply();
            });
        }
    }
    

    在您的活动中,创建适配器并将其添加到您的RecyclerView:

    var adapter = new MyAdapter((IMvxAndroidBindingContext)this.BindingContext);
    recyclerView.Adapter = adapter;
    

    set.Bind(this.adapter).For(x => x.ItemsSource).To(x => x.ViewModelItems);
    
        2
  •  2
  •   lauxjpn    8 年前

    基于Ken的回答,我创建了几个支持类和扩展来概括项的绑定,并将它们与使用示例一起推送到github:

    https://github.com/lauxjpn/MvxItemBinder

    它允许您编写如下项绑定:

    var recyclerView = FindViewById<MvxRecyclerView>(Resource.Id.RecyclerView);
    
    var set = this.CreateBindingSet<MainActivity, MainViewModel>();
    set.Bind(recyclerView)
        .For(v => v.ItemsSource)
        .To(vm => vm.Items);
    set.Apply();
    
    recyclerView.BindItems<ItemViewModel>(this, (itemView, itemSet) =>
        itemSet.Bind(itemView.FindViewById<TextView>(Resource.Id.item_template))
            .For(v => v.Text)
            .To(vm => vm.Title)
    );
    

    或甚至更短:

    var recyclerView = FindViewById<MvxRecyclerView>(Resource.Id.RecyclerView);
    
    var set = this.CreateBindingSet<MainActivity, MainViewModel>();
    set.Bind(recyclerView.BindItems<ItemViewModel>(this, (itemView, itemSet) =>
            itemSet.Bind(itemView.FindViewById<TextView>(Resource.Id.item_template))
                .For(v => v.Text)
                .To(vm => vm.Title)))
        .For(v => v.ItemsSource)
        .To(vm => vm.Items);
    set.Apply();