【问题标题】:Mvvmcross Viewpager with list items带有列表项的 Mvvmcross Viewpager
【发布时间】:2020-06-02 23:10:02
【问题描述】:

我正在尝试在我的扁平列表上实现类似 viewpager 的效果。因此,当带有 viewpager 的片段打开时,初始屏幕显示列表中的“Item1”,当用户滑动时,屏幕变为“Item2”。现在我已经使用 viewPagerAdapter 实现了一个示例,但是我正在扩展适配器内部的视图,这不是 mvvm 方式。我希望能够从视图模型更新视图。

这是我目前实现的代码:

这是我的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;
    }
}

我的 ViewModel 如下所示:

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

还有我的片段:

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

有人可以帮助我如何使这个过程更 Mvvm。我不想从适配器更新我的项目布局,而是必须从我的视图模型更新它。

【问题讨论】:

    标签: android xamarin xamarin.android android-viewpager mvvmcross


    【解决方案1】:

    正如您在PeopleView of the StarWars sample(在官方 MVVMCross 示例存储库中)中看到的那样,魔法发生在布局文件本身中。

    所以你的片段是干净的,只需要声明它正在使用那个布局。在您的布局中,只需将 ItemSource 分配给 ViewModel 中的属性,就可以了。您只需要下面显示的MvxRecyclerView 部分

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

    让我知道这是否有意义。如果您愿意,也可以使用MvxListView

    【讨论】:

    • 我不认为,您在这里遇到了问题。在我的 PagerAdapter 中检查这一行 var view = inflater.Inflate(Resource.Layout.slidertextview, null);。看看我是如何在适配器内部膨胀视图的,这样视图就不会绑定到任何视图模型。因此,我无法更新该视图中的任何内容。
    • 我认为@Saamer 在这里有正确的想法。在删除自定义适配器并让绑定生成一个之后,您将拥有 ListItem 作为项目布局的基础,您可以在其中使用更多数据绑定来绑定其属性。 ListItems 实际上也可以是 ViewModel,因为它们具有可观察的属性,并且可以导致视图更新,而无需您触发列表上的更改。见here
    • @NickKing 是否有理由绝对需要使用自定义适配器而不是 MvvmCross 绑定自动生成的适配器?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-03-04
    • 2014-06-29
    • 2014-10-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多