5

Я использую MvvmCross в моем Xamarin Android проект. У меня есть MvxActivity с MvxRecyclerView, что я назначил шаблон элемента в его файле макета.Как использовать свободно распространяемый API MvvmCross для привязки TextView элемента RecyclerView к свойству ViewModel на Android?

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

Вообще мне нравится использовать свободно API MvvmCross как можно больше, потому что неявной поддержки рефакторинга. Так что в моей деятельности, я связывание свойства 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 с использованием свободного API?

Я знаю, что это довольно легко сделать без свободного API, предоставив атрибут MvxBind в файле макета шаблона элемента, но я бы предпочел свободное решение API.

ответ

10

Наследуйте от 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 вы можете использовать API Fluent для привязки.

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; 

и связать свои товары к ItemsSource вашего адаптера:

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

Одно замечание, вы можете назначить команду щелчком мыши над к вашему пользовательскому 'ViewHolder', то никакая привязка к ItemClick не повлияет.Пример в [этот ответ stackoverflow] (http://stackoverflow.com/questions/42938112/mvxrecyclerview-fluent-api-binding#answer-43055796). – Plac3Hold3r

2

Основываясь на ответ Кена, Я создал пару классов поддержки и расширений, чтобы обобщить привязку элементов и скопировать их вместе с образцом использования в 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();