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

MvvmCross Android:多Fragment复用单一ViewModel的实现问题

MvvmCross v6.0.1共用ViewModel实现步进Fragment导航方案

当然可以让你的Activity和所有步进Fragment共用同一个ViewModel——这其实是步进式流程场景下非常理想的方案,能轻松实现步骤间的数据共享和状态同步。咱们一步步解决你的问题:

解决启动页卡住的问题

你当前的代码导致启动页卡住,大概率是因为导航逻辑的冲突:你同时给Activity和Fragment绑定了同一个ViewModel,并且Fragment使用了MvxFragmentPresentation属性指定宿主VM类型,但启动时可能错误地尝试直接导航到Fragment,或者MvvmCross的导航系统在处理同VM的宿主和Fragment时出现了冲突。

解决方法很简单:

  1. 确保你的App启动逻辑是先导航到AttendanceActivity,而不是直接导航到Fragment。
  2. 去掉Fragment的MvxFragmentPresentation属性(或者如果需要保留,调整为指定宿主Activity类型而非VM类型),改为在Activity的初始化逻辑里手动加载第一个Fragment:
protected override void OnCreate(Bundle savedInstanceState)
{
    base.OnCreate(savedInstanceState);
    // 仅在首次创建Activity时加载第一个Fragment,避免屏幕旋转等场景重复加载
    if (savedInstanceState == null)
    {
        var firstStepFragment = new AttendanceFragmentSetpOne();
        SupportFragmentManager.BeginTransaction()
            .Replace(Resource.Id.attendance_content_frame, firstStepFragment)
            .Commit();
    }
}

共用ViewModel实现步进导航的完整方案

既然要共用一个ViewModel,我们可以通过在VM中维护当前步骤的状态,来区分不同Fragment并实现切换:

1. 改造ViewModel

在AttendanceViewModel中添加当前步骤的跟踪属性,以及各步骤的共享数据和导航命令:

public class AttendanceViewModel : MvxViewModel
{
    // 跟踪当前步骤(1-4)
    private int _currentStep = 1;
    public int CurrentStep
    {
        get => _currentStep;
        set => SetProperty(ref _currentStep, value);
    }

    // 存储各步骤的共享数据,根据你的需求调整类型
    public string Step1UserInput { get; set; }
    public bool Step2AgreementAccepted { get; set; }
    public DateTime Step3SelectedDate { get; set; }

    // 下一步命令
    public ICommand NextStepCommand => new MvxCommand(GoToNextStep);
    
    // 上一步命令(可选)
    public ICommand PreviousStepCommand => new MvxCommand(GoToPreviousStep);

    private void GoToNextStep()
    {
        if (CurrentStep < 4)
        {
            CurrentStep++;
            // 通知Activity切换Fragment
            RaisePropertyChanged(nameof(CurrentStep));
        }
        // 如果是最后一步,可执行提交逻辑
        else
        {
            SubmitAttendance();
        }
    }

    private void GoToPreviousStep()
    {
        if (CurrentStep > 1)
        {
            CurrentStep--;
            RaisePropertyChanged(nameof(CurrentStep));
        }
    }

    private void SubmitAttendance()
    {
        // 处理最终提交逻辑,使用各步骤的共享数据
    }
}

2. 在Activity中监听步骤变化切换Fragment

在AttendanceActivity中订阅ViewModel的CurrentStep属性变化,根据当前步骤加载对应的Fragment:

public class AttendanceActivity : BaseActivity<AttendanceViewModel>
{
    protected override int ActivityLayoutId => Resource.Layout.activity_attendance;

    protected override void OnCreate(Bundle savedInstanceState)
    {
        base.OnCreate(savedInstanceState);

        if (savedInstanceState == null)
        {
            LoadFragmentForStep(1);
        }

        // 订阅步骤变化事件
        ViewModel.PropertyChanged += ViewModel_PropertyChanged;
    }

    private void ViewModel_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
    {
        if (e.PropertyName == nameof(ViewModel.CurrentStep))
        {
            LoadFragmentForStep(ViewModel.CurrentStep);
        }
    }

    private void LoadFragmentForStep(int step)
    {
        Android.Support.V4.App.Fragment targetFragment = step switch
        {
            1 => new AttendanceFragmentSetpOne(),
            2 => new AttendanceFragmentStepTwo(),
            3 => new AttendanceFragmentStepThree(),
            4 => new AttendanceFragmentStepFour(),
            _ => new AttendanceFragmentSetpOne()
        };

        // 执行Fragment切换,可选添加到返回栈让用户能通过返回键回退
        SupportFragmentManager.BeginTransaction()
            .Replace(Resource.Id.attendance_content_frame, targetFragment)
            .AddToBackStack(null)
            .Commit();
    }

    protected override void OnDestroy()
    {
        // 记得取消订阅,避免内存泄漏
        ViewModel.PropertyChanged -= ViewModel_PropertyChanged;
        base.OnDestroy();
    }
}

3. 在Fragment中使用共享ViewModel

每个Fragment绑定同一个ViewModel后,直接访问VM中的共享数据即可实现步骤间的数据传递。比如在第一步Fragment中:

public class AttendanceFragmentSetpOne : BaseFragment<AttendanceViewModel>, IOnClickListener
{
    protected override int FragmentLayoutId => Resource.Layout.fragment_attendance_step_one;

    public override void OnViewCreated(View view, Bundle savedInstanceState)
    {
        base.OnViewCreated(view, savedInstanceState);
        
        // 绑定输入框到ViewModel的Step1UserInput属性
        var inputEditText = view.FindViewById<EditText>(Resource.Id.step1_input);
        inputEditText.Text = ViewModel.Step1UserInput;
        inputEditText.TextChanged += (s, e) => ViewModel.Step1UserInput = inputEditText.Text;

        // 绑定下一步按钮到ViewModel的NextStepCommand
        var nextBtn = view.FindViewById<Button>(Resource.Id.btn_next);
        nextBtn.SetOnClickListener(this);
    }

    public void OnClick(View v)
    {
        if (v.Id == Resource.Id.btn_next)
        {
            ViewModel.NextStepCommand.Execute(null);
        }
    }
}

这样所有Fragment都共享同一个ViewModel的状态,步骤间的数据传递完全不需要额外的参数传递逻辑,直接读写VM的属性即可。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 09:08:01