How to use the MvvmCross fluent API to bind a RecyclerView item's TextView to a property of its ViewModel on Android?

匿名 (未验证) 提交于 2019-12-03 02:30:02

问题:

I am using MvvmCross in my Xamarin Android project. I have an MvxActivity with an MvxRecyclerView, that I have assigned an item template in its layout file.

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

The ViewModel is quite simple, it consists just of one property that holds the data to display in the RecyclerView:

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

Generally I like to use the MvvmCross fluent API as much as possible because of the implicit refactoring support. So in my activity, I am binding a property of the MvxRecyclerView like this:

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(); 

So far so good. Now, the layout file for the item template basically just contains a TextView:

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

And my ViewModelItem class looks like this:

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

My question now is, how and where do I bind the TextView.Text property to the ViewModelItem.Title property using the fluent API?

I know it is quite easy to do without the fluent API by supplying an MvxBind attribute in the item template layout file, but I would really prefer a fluent API solution.

回答1:

Inherit from MvxRecyclerAdapter and create a custom Adapter for your RecyclerView. Override OnCreateViewHolder and return a custom 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);     } } 

Within this ViewHolder you can use the Fluent API for binding.

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();         });     } } 

In your Activity create the Adapter and add it to your RecyclerView:

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

and bind your Items to the ItemsSource of your Adapter:

set.Bind(this.adapter).For(x => x.ItemsSource).To(x => x.ViewModelItems); 


回答2:

Based on Ken's answer, I created a couple of support classes and extensions to generalize binding of items and pushed them together with a usage sample to github:

https://github.com/lauxjpn/MvxItemBinder

It allows you to write item bindings like the following:

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) ); 

Or even shorter:

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(); 


标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!