Reputation: 200
I am trying to implement viewpager like effect on my flattened list. So, when the fragment with the viewpager is open the initial screen shows "Item1" from the list and when the user slides, the screen changes to "Item2". Now I have implemented an example using viewPagerAdapter but I am inflating the view inside the adapter which is not the mvvm way. I want to be able to update the view from the viewmodel.
Here is the code for what I have implemented so far:
This is my PagerAdapter
public class CustomPagerAdapter : PagerAdapter
{
private readonly Context _context;
private readonly List<ListItem> _stringLst;
private readonly ViewPagerExampleViewModel _viewModel;
private readonly ViewPager _viewPager;
public CustomPagerAdapter(Context context, List<ListItem> stringLst, ViewPagerExampleViewModel viewModel, ViewPager viewPager)
{
_context = context;
_stringLst = stringLst;
_viewModel = viewModel;
_viewPager = viewPager;
}
public override bool IsViewFromObject(View view, Object @object)
{
return view == @object;
}
public override int Count
{
get
{
var count = _stringLst.Count;
return count;
}
}
public override Object InstantiateItem(ViewGroup container, int position)
{
if (_context.GetSystemService(Context.LayoutInflaterService) is LayoutInflater inflater)
{
var view = inflater.Inflate(Resource.Layout.slidertextview, null);
var child = view.FindViewById<TextView>(Resource.Id.sliderText);
var nextButton = view.FindViewById<Button>(Resource.Id.nestedNextButton);
var sectionButton = view.FindViewById<Button>(Resource.Id.nestedSectionButton);
nextButton.Click += ButtonOnClick;
sectionButton.Click += SectionButtonOnClick;
var title = _stringLst[position].Title;
child.Text = title;
container.AddView(view);
return view;
}
return null;
}
private void SectionButtonOnClick(object sender, EventArgs e)
{
_viewModel.SectionJumpCommand.Execute();
_viewPager.CurrentItem = _viewModel.Index;
}
private void ButtonOnClick(object sender, EventArgs e)
{
_viewModel.NextCommand.Execute();
_viewPager.CurrentItem = _viewModel.Index;
}
public override void DestroyItem(ViewGroup container, int position, Object @object)
{
container.RemoveView((View)@object);
}
public override void SetPrimaryItem(ViewGroup container, int position, Object @object)
{
base.SetPrimaryItem(container, position, @object);
_viewModel.Index = position;
}
}
My ViewModel looks like this:
private List<ListItem> _items;
public List<ListItem> Items
{
get => _items;
set
{
if (_items == value)
return;
_items = value;
RaisePropertyChanged(nameof(Items));
}
}
private int _index;
public int Index
{
get => _index;
set
{
if (_index == value)
return;
_index = value;
RaisePropertyChanged(nameof(Index));
}
}
public IMvxCommand NextCommand => new MvxCommand(Next);
public IMvxCommand SectionJumpCommand => new MvxCommand(SectionJump);
public ViewPagerExampleViewModel()
{
Index = 0;
Items = new List<ListItem> {
new ListItem { Title = "Item 0" },
new ListItem { Title = "Item 1" },
new ListItem { Title = "Item 2" },
new ListItem { Title = "Item 3" },
new ListItem { Title = "Item 4" },
new ListItem { Title = "Item 5" },
new ListItem { Title = "Item 6" },
new ListItem { Title = "Item 7" },
new ListItem { Title = "Item 8" },
new ListItem { Title = "Item 9" },
new ListItem { Title = "Item 10" },
new ListItem { Title = "Item 11" },
new ListItem { Title = "Item 12" }
};
}
public void Next()
{
if (Items.Count == Index + 1)
return;
Index += 1;
}
private void SectionJump()
{
if (Items.Count == Index + 1)
return;
Index += 3;
}
And my fragment:
public class ViewPagerExampleFragment : BaseFragment<ViewPagerExampleViewModel>
{
private ViewPager _viewPager;
private FloatingActionButton _nextButton;
protected override int FragmentId => Resource.Layout.viewpagerExampleView;
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
var view = base.OnCreateView(inflater, container, savedInstanceState);
_nextButton = view.FindViewById<FloatingActionButton>(Resource.Id.nextButton);
_viewPager = view.FindViewById<ViewPager>(Resource.Id.viewpager);
_viewPager.Adapter = new CustomPagerAdapter(Context, ViewModel.Items, ViewModel, _viewPager);
return view;
}
}
Can someone help me on how to make this process more Mvvm. I don't want to update my item layout from the adapter instead it has to be updated from my viewmodel.
Upvotes: 1
Views: 518
Reputation: 5109
As you can see in the PeopleView of the StarWars sample (in the official MVVMCross samples repo), the magic happens in the layout file itself.
So your fragment is clean and just needs to declare that it’s using that layout. In your layout, just assign the ItemSource to the property in your ViewModel and you’re good. You just need the MvxRecyclerView
section shown below
<MvxSwipeRefreshLayout
android:id="@+id/refresher"
android:layout_width="match_parent"
android:layout_height="match_parent"
local:layout_behavior="@string/appbar_scrolling_view_behavior"
local:MvxBind="IsRefreshing LoadPeopleTask; RefreshCommand RefreshPeopleCommand">
<MvxRecyclerView
android:id="@+id/people_recycler_view"
android:scrollbars="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
local:MvxItemTemplate="@layout/item_name"
local:MvxBind="ItemsSource People; ItemClick PersonSelectedCommand" />
</MvxSwipeRefreshLayout>
Let me know if that makes sense. You could also use the MvxListView
if you like
Upvotes: 1