使用新的Android架构(MVVM),实现Activity和Fragment之间的通信

4
目标是从一个Fragment获取信息,以便托管的Activity可以展示它。让我们举一个简单的例子,假设当一个Fragment改变时,我们想要在我们Activity的操作栏中展示Fragment的标题。

这是我以前的做法:

Activity

@Override
protected void onCreate( @Nullable Bundle savedInstanceState )
{
    super.onCreate( savedInstanceState );

    getSupportFragmentManager().addOnBackStackChangedListener( this );
}

@Override
public void onBackStackChanged( )
{
    BaseFragment fragment = mNavigationManager.getCurrentFragment( );

    if( fragment != null && fragment instanceof ActionBarProvider )
        mActionBarTitle.setText( ( ( ActionBarProvider ) fragment ).getTitle( ) );
}

ActionBarProvider

public interface ActionBarProvider
{
    String getTitle( );
}

碎片

// implements ActionBarProvider

@Override
protected String getTitle( )
{
    return "Hello world";
}

以下是关于MVVM的想法:
活动 (Activity)
@Override
protected void onCreate( @Nullable Bundle savedInstanceState )
{
    super.onCreate( savedInstanceState );

    mViewModel = ViewModelProviders.of( this, mViewModelFactory ).get( MainViewModel.class );
    mViewModel.getTitle( ).observe( this, s -> mActionBarTitle.setText( s ) );
}

主视图模型

private MutableLiveData< String > mTitle = new MutableLiveData<>( );

public MutableLiveData< String > getTitle( )
{
    return mTitle;
}

public void setTitle( String title )
{
    mTitle.postValue( title );
}

碎片

@Override
public void onActivityCreated( @Nullable Bundle savedInstanceState )
{
    super.onActivityCreated( savedInstanceState );

    MainViewModel viewModel = ViewModelProviders
            .of( getActivity( ), mViewModelFactory )
            .get( MainViewModel.class );

    viewModel.setTitle( "hello world" );
}

MVVM看起来更干净,但它假设Fragment知道它的主Activity的ViewModel:

MainViewModel viewModel = ViewModelProviders
            .of( getActivity( ), mViewModelFactory )
            .get( MainViewModel.class );

如果你将Fragment移动到另一个Activity,这种方法就不起作用了。我应该保持旧方式吗?或者你们有其他使用MVVM进行此类通信的方法吗?
谢谢!
1个回答

6

使用ViewModel有一个黄金法则。除了*.arch包之外,以Android包开头的所有内容都是错误的。

您不需要在活动中设置标题,因为它绑定到片段。

在片段的onStart/onResume方法中使用getActivity().setTitle()

ViewModels绑定到片段或宿主活动。更改活动还将在ViewModel中调用onCleared()。您无法使用ViewModel在多个活动之间共享数据。

创建接口来共享数据不是MVVM。这是MVP,应该避免使用MVVM和Google's AAC

由于几个活动可能是片段的宿主,因此您永远不应直接访问活动。最好在片段中使用if (getActivity() != null) { getActivity().setTitle(...) }

顺便说一下,如果您不修改getter/setter,您也应该使用public final LiveData并避免使用getter和setter。您的LiveData实例不会更改,但数据会更改。意味着final就可以了。

public final LiveData<String> mTitle = new MutableLiveData<String>();

如果您不想追加数据而是要替换数据,您还应该考虑使用setValue()来设置数据。

因此,如果您将片段移动到另一个活动中,则此方法将无法正常工作。我应该继续使用旧的方式吗?或者你们有其他使用MVVM进行此类通信的方法吗?

ViewModels只能在片段和托管活动之间共享,但不能在多个活动之间共享。


谢谢你的回答(和建议),这让我清晰了很多。所以假设我们有一个MainActivity底部导航(而不是ActionBar标题),并且对于一个片段,您想要隐藏它,那么您将为每个片段调用getActivity().setBottomNavigationVisibility(true/false);吗? - Samuel Eminet
是的,这是一个可行的解决方案。使用接口在片段和活动之间进行通信是另一种方法。始终使用 getActivity != null 来双重检查活动是否仍然可用。 - Emanuel
顺便说一下,如果你使用MVVM,最好使用Databinding。这样你就可以使用bindingadapters来处理那些高度推荐的东西了。 - Emanuel
嗯,我不确定如何使用数据绑定根据片段注入操作栏标题,你有任何示例吗? - Samuel Eminet

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接