可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
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();