Reputation: 4737
The goal is to get info from a Fragment
, so that the hosting Activity
can display it. Let's take a simple example and assume that when a Fragment
changes we want to display Fragment
's title from our Activity
's Action Bar.
Here's my old way of doing it:
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( );
}
Fragment
// implements ActionBarProvider
@Override
protected String getTitle( )
{
return "Hello world";
}
Here's what I think of doing with 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 ) );
}
MainViewModel
private MutableLiveData< String > mTitle = new MutableLiveData<>( );
public MutableLiveData< String > getTitle( )
{
return mTitle;
}
public void setTitle( String title )
{
mTitle.postValue( title );
}
Fragment
@Override
public void onActivityCreated( @Nullable Bundle savedInstanceState )
{
super.onActivityCreated( savedInstanceState );
MainViewModel viewModel = ViewModelProviders
.of( getActivity( ), mViewModelFactory )
.get( MainViewModel.class );
viewModel.setTitle( "hello world" );
}
The MVVM looks cleaner but it assumes that Fragment
knows its main Activity
ViewModel:
MainViewModel viewModel = ViewModelProviders
.of( getActivity( ), mViewModelFactory )
.get( MainViewModel.class );
So if you move Fragment
to another Activity
this won't work.
Should I keep the old way? Or have you guys any other way to do this kind of communication using MVVM?
Thx!
Upvotes: 4
Views: 2056
Reputation: 8106
There's a golden rule using ViewModels. Everything beginning with Android packages in your import is wrong except *.arch packages.
You don't set the title in your actvity, since it's bound to the fragment.
Use getActivity().setTitle()
in your onStart
/onResume
method in your fragment.
ViewModels are bound to the fragment OR to the host activity. Changing the activity will also call onCleared()
in your ViewModel. You can't share data between several activties using your ViewModel.
Creating an interface to share data is not MVVM. It's MVP and should be avoided using MVVM and Google's AAC.
Since several activities may be a host for your fragments you should never access the activity directly. Better use if (getActivity() != null) { getActivity().setTitle(...) }
in your fragment.
By the way, if you don't modify the getter/setter you should also use public final LiveData
and avoid using getters and setters. Your LiveData
instance won't change, but the data does. Means final
is just fine.
public final LiveData<String> mTitle = new MutableLiveData<String>();
You should also consider using setValue()
to set the data if you dont want to append but replace the data.
So if you move fragment to another activity this won't work. Should I keep the old way? Or have you guys any other way to do this kind of communication using MVVM?
ViewModels can ONLY be shared between fragments and the hosting activity, but it can't shared using several activities.
Upvotes: 6