You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于MVVMCross为Xamarin.Android和Xamarin.iOS开发3个可编辑标签页

基于MVVMCross的跨平台可编辑标签页实现示例

没问题,我帮你整理了适配Xamarin.Android和Xamarin.iOS的可编辑标签页方案,核心是用MVVMCross的共享ViewModel统一业务逻辑,再针对两个平台做视图层的实现:


1. 共享ViewModel层实现

咱们先从共享的ViewModel入手,所有平台的标签增删、选中逻辑都在这里统一处理:

using MvvmCross.ViewModels;
using System.Collections.ObjectModel;
using System.Windows.Input;

public class TagItem
{
    public string Name { get; set; }
}

public class TagsViewModel : MvxViewModel
{
    private ObservableCollection<TagItem> _tags;
    public ObservableCollection<TagItem> Tags
    {
        get => _tags;
        set => SetProperty(ref _tags, value);
    }

    private TagItem _selectedTag;
    public TagItem SelectedTag
    {
        get => _selectedTag;
        set => SetProperty(ref _selectedTag, value);
    }

    public ICommand AddTagCommand { get; }
    public ICommand RemoveTagCommand { get; }

    public TagsViewModel()
    {
        // 初始化默认的3个标签
        Tags = new ObservableCollection<TagItem>
        {
            new TagItem { Name = "标签1" },
            new TagItem { Name = "标签2" },
            new TagItem { Name = "标签3" }
        };

        AddTagCommand = new MvxCommand(AddNewTag);
        RemoveTagCommand = new MvxCommand<TagItem>(RemoveTag);
    }

    private void AddNewTag()
    {
        var newTag = new TagItem { Name = $"新标签{Tags.Count + 1}" };
        Tags.Add(newTag);
        SelectedTag = newTag;
    }

    private void RemoveTag(TagItem tag)
    {
        if (Tags.Contains(tag))
        {
            var currentIndex = Tags.IndexOf(tag);
            Tags.Remove(tag);
            // 删除后自动选中下一个或上一个标签,避免空状态
            if (Tags.Count > 0)
            {
                SelectedTag = Tags[currentIndex >= Tags.Count ? Tags.Count - 1 : currentIndex];
            }
        }
    }
}

2. Xamarin.Android 视图实现

Android这边用TabLayout + ViewPager的经典组合,配合MVVMCross绑定实现标签的增删和页面切换:

2.1 布局文件(TabsView.axml)

<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.google.android.material.appbar.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">

        <com.google.android.material.tabs.TabLayout
            android:id="@+id/tabLayout"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:tabMode="scrollable" />
    </com.google.android.material.appbar.AppBarLayout>

    <androidx.viewpager.widget.ViewPager
        android:id="@+id/viewPager"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior" />

    <com.google.android.material.floatingactionbutton.FloatingActionButton
        android:id="@+id/fabAddTag"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom|end"
        android:layout_margin="16dp"
        android:src="@drawable/ic_add" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>

2.2 代码绑定实现

using Android.App;
using Android.OS;
using Android.Views;
using MvvmCross.DroidX.Views;
using MvvmCross.Binding.BindingContext;
using AndroidX.ViewPager.Widget;
using Google.Android.Material.Tabs;
using Android.App.Alerts;

[Activity(Label = "可编辑标签页")]
public class TabsView : MvxAppCompatActivity<TagsViewModel>
{
    private TabLayout _tabLayout;
    private ViewPager _viewPager;
    private MvxPagerAdapter _pagerAdapter;

    protected override void OnCreate(Bundle savedInstanceState)
    {
        base.OnCreate(savedInstanceState);
        SetContentView(Resource.Layout.TabsView);

        _tabLayout = FindViewById<TabLayout>(Resource.Id.tabLayout);
        _viewPager = FindViewById<ViewPager>(Resource.Id.viewPager);

        // 初始化ViewPager适配器
        _pagerAdapter = new MvxPagerAdapter(Context, SupportFragmentManager, ViewModel.Tags);
        _viewPager.Adapter = _pagerAdapter;
        _tabLayout.SetupWithViewPager(_viewPager);

        // 绑定添加标签命令到悬浮按钮
        var bindingSet = this.CreateBindingSet<TabsView, TagsViewModel>();
        bindingSet.Bind(FindViewById<View>(Resource.Id.fabAddTag)).To(vm => vm.AddTagCommand);
        bindingSet.Apply();

        // 初始化标签长按删除事件
        BindTabLongClickEvents();

        // 监听标签集合变化,实时更新TabLayout
        ViewModel.Tags.CollectionChanged += (s, e) =>
        {
            _pagerAdapter.NotifyDataSetChanged();
            _tabLayout.SetupWithViewPager(_viewPager);
            BindTabLongClickEvents();
        };
    }

    private void BindTabLongClickEvents()
    {
        // 先清空旧的长按事件
        for (int i = 0; i < _tabLayout.TabCount; i++)
        {
            var tabView = _tabLayout.GetTabAt(i).View;
            tabView.LongClick -= TabView_LongClick;
            tabView.LongClick += TabView_LongClick;
        }
    }

    private void TabView_LongClick(object sender, View.LongClickEventArgs e)
    {
        var tabView = sender as View;
        var tabIndex = _tabLayout.GetTabPosition(tabView);
        var targetTag = ViewModel.Tags[tabIndex];

        new AlertDialog.Builder(Context)
            .SetTitle("删除标签")
            .SetMessage($"确定删除\"{targetTag.Name}\"吗?")
            .SetPositiveButton("确定", (dialog, args) => ViewModel.RemoveTagCommand.Execute(targetTag))
            .SetNegativeButton("取消", null)
            .Show();
    }
}

// 自定义ViewPager适配器
public class MvxPagerAdapter : PagerAdapter
{
    private Context _context;
    private FragmentManager _fragmentManager;
    private ObservableCollection<TagItem> _tags;

    public MvxPagerAdapter(Context context, FragmentManager fragmentManager, ObservableCollection<TagItem> tags)
    {
        _context = context;
        _fragmentManager = fragmentManager;
        _tags = tags;
    }

    public override int Count => _tags.Count;

    public override bool IsViewFromObject(View view, Java.Lang.Object @object)
    {
        return view == ((Fragment)@object).View;
    }

    public override Java.Lang.Object InstantiateItem(ViewGroup container, int position)
    {
        // 替换成你自己的内容Fragment
        var contentFragment = new ContentFragment();
        contentFragment.Arguments = new Bundle();
        contentFragment.Arguments.PutString("TagTitle", _tags[position].Name);
        _fragmentManager.BeginTransaction().Add(container.Id, contentFragment).Commit();
        return contentFragment;
    }

    public override void DestroyItem(ViewGroup container, int position, Java.Lang.Object @object)
    {
        _fragmentManager.BeginTransaction().Remove((Fragment)@object).Commit();
    }

    public override CharSequence GetPageTitleFormatted(int position)
    {
        return _tags[position].Name;
    }
}

// 示例内容Fragment
public class ContentFragment : Fragment
{
    public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
    {
        var view = inflater.Inflate(Resource.Layout.ContentFragment, container, false);
        var titleTxt = view.FindViewById<TextView>(Resource.Id.txtTagTitle);
        titleTxt.Text = Arguments.GetString("TagTitle");
        return view;
    }
}

3. Xamarin.iOS 视图实现

iOS这边用UIPageViewController配合自定义标签栏(UIStackView实现),结合MVVMCross绑定实现可编辑标签:

3.1 视图控制器代码

using UIKit;
using MvvmCross.iOS.Views;
using MvvmCross.Binding.BindingContext;
using System.Collections.Specialized;

[Register("TabsView")]
public class TabsView : MvxViewController<TagsViewModel>
{
    private UIPageViewController _pageViewController;
    private UIStackView _tagBarStack;
    private UIButton _addTagBtn;

    public override void ViewDidLoad()
    {
        base.ViewDidLoad();
        View.BackgroundColor = UIColor.White;

        // 初始化顶部标签栏
        SetupTagBar();
        // 初始化页面控制器
        SetupPageViewController();
        // 绑定MVVM命令
        SetupBindings();
        // 监听标签集合变化
        ViewModel.Tags.CollectionChanged += Tags_CollectionChanged;
    }

    private void SetupTagBar()
    {
        _tagBarStack = new UIStackView
        {
            Axis = UILayoutConstraintAxis.Horizontal,
            Spacing = 12,
            TranslatesAutoresizingMaskIntoConstraints = false
        };

        _addTagBtn = new UIButton(UIButtonType.System)
        {
            TranslatesAutoresizingMaskIntoConstraints = false,
            Title = "+",
            Font = UIFont.SystemFontOfSize(18)
        };

        var topContainer = new UIStackView(new[] { _tagBarStack, _addTagBtn })
        {
            Axis = UILayoutConstraintAxis.Horizontal,
            Spacing = 16,
            TranslatesAutoresizingMaskIntoConstraints = false
        };

        View.AddSubview(topContainer);
        NSLayoutConstraint.ActivateConstraints(new[]
        {
            topContainer.TopAnchor.ConstraintEqualTo(View.SafeAreaLayoutGuide.TopAnchor, 16),
            topContainer.LeadingAnchor.ConstraintEqualTo(View.LeadingAnchor, 16),
            topContainer.TrailingAnchor.ConstraintEqualTo(View.TrailingAnchor, -16)
        });

        // 初始化默认标签按钮
        RefreshTagButtons();
    }

    private void SetupPageViewController()
    {
        _pageViewController = new UIPageViewController(UIPageViewControllerTransitionStyle.Scroll, UIPageViewControllerNavigationOrientation.Horizontal);
        _pageViewController.DataSource = new PageDataSource(ViewModel.Tags);
        AddChildViewController(_pageViewController);
        View.AddSubview(_pageViewController.View);

        NSLayoutConstraint.ActivateConstraints(new[]
        {
            _pageViewController.View.TopAnchor.ConstraintEqualTo(_tagBarStack.BottomAnchor, 16),
            _pageViewController.View.LeadingAnchor.ConstraintEqualTo(View.LeadingAnchor),
            _pageViewController.View.TrailingAnchor.ConstraintEqualTo(View.TrailingAnchor),
            _pageViewController.View.BottomAnchor.ConstraintEqualTo(View.BottomAnchor)
        });

        _pageViewController.DidMoveToParentViewController(this);
        // 设置初始页面
        if (ViewModel.Tags.Count > 0)
        {
            _pageViewController.SetViewControllers(new[] { GetContentVC(0) }, UIPageViewControllerNavigationDirection.Forward, false, null);
            SetSelectedTagButton(0);
        }
    }

    private void SetupBindings()
    {
        var bindingSet = this.CreateBindingSet<TabsView, TagsViewModel>();
        bindingSet.Bind(_addTagBtn).To(vm => vm.AddTagCommand);
        bindingSet.Apply();
    }

    private void Tags_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
        RefreshTagButtons();
        // 更新页面控制器数据源
        (_pageViewController.DataSource as PageDataSource)?.UpdateTags(ViewModel.Tags);
        // 添加新标签后自动切换到新页面
        if (e.Action == NotifyCollectionChangedAction.Add)
        {
            var newIndex = e.NewStartingIndex;
            _pageViewController.SetViewControllers(new[] { GetContentVC(newIndex) }, UIPageViewControllerNavigationDirection.Forward, true, null);
            SetSelectedTagButton(newIndex);
        }
    }

    private void RefreshTagButtons()
    {
        // 清空旧标签按钮
        foreach (var view in _tagBarStack.ArrangedSubviews)
        {
            view.RemoveFromSuperview();
        }

        // 添加新标签按钮
        for (int i = 0; i < ViewModel.Tags.Count; i++)
        {
            var tag = ViewModel.Tags[i];
            var tagBtn = new UIButton(UIButtonType.System)
            {
                Title = tag.Name,
                Tag = i
            };
            tagBtn.TouchUpInside += (s, e) =>
            {
                var index = (s as UIButton).Tag;
                _pageViewController.SetViewControllers(new[] { GetContentVC(index) }, UIPageViewControllerNavigationDirection.Forward, true, null);
                SetSelectedTagButton(index);
            };

            // 长按删除标签
            var longPress = new UILongPressGestureRecognizer((gesture) =>
            {
                if (gesture.State == UIGestureRecognizerState.Began)
                {
                    var alert = UIAlertController.Create("删除标签", $"确定删除\"{tag.Name}\"吗?", UIAlertControllerStyle.Alert);
                    alert.AddAction(UIAlertAction.Create("确定", UIAlertActionStyle.Default, _ => ViewModel.RemoveTagCommand.Execute(tag)));
                    alert.AddAction(UIAlertAction.Create("取消", UIAlertActionStyle.Cancel, null));
                    PresentViewController(alert, true, null);
                }
            });
            tagBtn.AddGestureRecognizer(longPress);

            _tagBarStack.AddArrangedSubview(tagBtn);
        }
    }

    private void SetSelectedTagButton(int index)
    {
        for (int i = 0; i < _tagBarStack.ArrangedSubviews.Length; i++)
        {
            var btn = _tagBarStack.ArrangedSubviews[i] as UIButton;
            btn.TintColor = i == index ? UIColor.SystemBlueColor : UIColor.LabelColor;
        }
    }

    private UIViewController GetContentVC(int index)
    {
        var contentVC = new ContentViewController();
        contentVC.TagTitle = ViewModel.Tags[index].Name;
        contentVC.TagIndex = index;
        return contentVC;
    }
}

// 页面数据源
public class PageDataSource : UIPageViewControllerDataSource
{
    private ObservableCollection<TagItem> _tags;

    public PageDataSource(ObservableCollection<TagItem> tags)
    {
        _tags = tags;
    }

    public void UpdateTags(ObservableCollection<TagItem> tags)
    {
        _tags = tags;
    }

    public override UIViewController GetPreviousViewController(UIPageViewController pageViewController, UIViewController referenceViewController)
    {
        var index = (referenceViewController as ContentViewController).TagIndex;
        return index > 0 ? GetContentVC(index - 1) : null;
    }

    public override UIViewController GetNextViewController(UIPageViewController pageViewController, UIViewController referenceViewController)
    {
        var index = (referenceViewController as ContentViewController).TagIndex;
        return index < _tags.Count - 1 ? GetContentVC(index + 1) : null;
    }

    private UIViewController GetContentVC(int index)
    {
        var vc = new ContentViewController { TagIndex = index, TagTitle = _tags[index].Name };
        return vc;
    }
}

// 示例内容视图控制器
public class ContentViewController : UIViewController
{
    public int TagIndex { get; set; }
    public string TagTitle { get; set; }

    public override void ViewDidLoad()
    {
        base.ViewDidLoad();
        View.BackgroundColor = UIColor.White;

        var titleLabel = new UILabel
        {
            Text = $"当前标签:{TagTitle}",
            TranslatesAutoresizingMaskIntoConstraints = false
        };
        View.AddSubview(titleLabel);

        NSLayoutConstraint.ActivateConstraints(new[]
        {
            titleLabel.CenterXAnchor.ConstraintEqualTo(View.CenterXAnchor),
            titleLabel.CenterYAnchor.ConstraintEqualTo(View.CenterYAnchor)
        });
    }
}

内容的提问来源于stack exchange,提问作者Santosh

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 07:31:28