5

Używam MvvmCross w moim projekcie Xamarin Android. Mam MvxActivity z MvxRecyclerView, który mam przypisany szablon elementu w jego pliku układu.Jak używać płynnego API MvvmCross do powiązania TextView elementu RecyclerView z właściwością ViewModel na Androidzie?

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

ViewModel jest dość prosta, składa się tylko z jednej nieruchomości, który przechowuje dane do wyświetlenia w RecyclerView:

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

Generalnie lubię używać MvvmCross biegle API jak najwięcej, bo niejawnego wsparcia dla refaktoryzacji. Więc w mojej działalności, jestem wiązania własność MvxRecyclerView tak:

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

tej pory tak dobrze. Teraz plik układ szablonu poz zasadzie tylko zawiera TextView:

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

A moja ViewModelItem klasy wygląda następująco:

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

Moje pytanie jest teraz, w jaki sposób i gdzie mogę powiązać TextView.Text właściwość do właściwości ViewModelItem.Title przy użyciu płynnego interfejsu API?

Wiem, że dość łatwo jest zrobić bez płynnego API, podając atrybut MvxBind w pliku układu szablonu przedmiotu, ale naprawdę wolałbym płynne rozwiązanie API.

Odpowiedz

10

Odziedzicz z MvxRecyclerAdapter i utwórz niestandardowy adapter do swojego RecyclerView. Zastąp OnCreateViewHolder i zwróć niestandardowy obiekt 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); 
    } 
} 

W ramach tego narzędzia ViewHolder można użyć Fluent API do wiązania.

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

W swojej działalności tworzyć adaptera i dodać go do RecyclerView:

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

i związać swoje przedmioty do ItemsSource adaptera:

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

Należy zwrócić uwagę, może chcesz przypisać polecenie przyciskiem myszy do twojego niestandardowego 'ViewHolder' inaczej żadne powiązanie z ItemClick nie będzie miało żadnego wpływu.Przykład w [this stackoverflow answer] (http://stackoverflow.com/questions/42938112/mvxrecyclerview-fluent-api-binding#answer-43055796). – Plac3Hold3r

2

oparciu o odpowiedź Kena, Stworzyłem kilka klas wsparcia i rozszerzeń, aby uogólnić wiązanie elementów i zepchnąłem je razem z próbką użycia do github:

https://github.com/lauxjpn/MvxItemBinder

To pozwala na pisanie powiązania artykuł tak:

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

lub nawet krócej:

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