Reuse single ViewModel with multiple fragments. MvvmCross Android - android

I'm newbie with MvvmCross. I'm developing an app using Mvx v6.0.1.
I want to make a kind of stepper using fragments (specifically 4 fragments or 4 steps), all of these fragments are embedded into an main activity. The main activity layout has a button, when I tap these button fragment is switched to the next step. I have created one viewmodel for each fragment and one more for the main activity. The problem I have is that I need to get some values from each step and pass to the next step.
I'm trying to share only one viewmodel for every view (fragments and activity). Is there any way to achieve this?
If yes, How can I distinguish each steps/fragments for navigate across them?
I was trying to set same viewmodel to Activity and fragment:
//Activity code
public class AttendanceActivity : BaseActivity<AttendanceViewModel>
{
protected override int ActivityLayoutId => Resource.Layout.activity_attendance;
//some logic
}
//Fragment code
[MvxFragmentPresentation(ActivityHostViewModelType = typeof(AttendanceViewModel),
FragmentContentId = Resource.Id.attendance_content_frame,
AddToBackStack = true)]
public class AttendanceFragmentSetpOne : BaseFragment<AttendanceViewModel>, IOnClickListener
{
protected override int FragmentLayoutId => Resource.Layout.fragment_attendance_step_one;
//Some logic
}
But when I make this, the app never pass from splash screen.
I hope explain myself and somebody help me with this "problem".
Thanks in advance.

Related

Shared ViewModel to help communication between fragments and parent activity

While Navigation component of JetPack looks pretty promising I got to a place where I could not find a way to implement something I wanted.
Let's take a look at a sample app screen:
The app has one main activity, a top toolbar, a bottom toolbar with fab attached.
There are 2 challenges that I am facing and I want to make them the right way.
1. I need to implement fragment transactions in order to allow replacing the fragment on the screen, based on the user interaction.
There are three ways I can think of and have this implemented:
the callbacks way. Having a interface onFragmentAction callback in fragment and have activity implement it. So basically when user presses a button in FragmentA I can call onFragmentAction with params so the activity will trigger and start for example transaction to replace it with FragmentB
implement Navigation component from JetPack. While I've tried it and seems pretty straightforward, I had a problem by not being able to retrieve the current fragment.
Use a shared ViewModel between fragment and activity, update it from the fragment and observe it in the activity. This would be a "replacement" of the callbacks
2. Since the FAB is in the parent activity, when pressed, I need to be able to interact with the current visible fragment and do an action. For instance, add a new item in a recyclerview inside the fragment. So basically a way to communicate between the activity and fragment
There are two ways I can think of how to make this
If not using Navigation then I can use findFragmentById and retrieve the current fragment and run a public method to trigger the action.
Using a shared 'ViewMode' between fragment and activity, update it from activity and observe it in the fragment.
So, as you can see, the recommended way to do navigation would be to use the new 'Navigation' architecture component, however, at the moment it lacks a way to retrieve the current fragment instance so I don't know how to communicate between the activity and fragment.
This could be achieved with shared ViewModel but here I have a missing piece: I understand that fragment to fragment communication can be made with a shared ViewModel. I think that this makes sense when the fragments have something in common for this, like a Master/Detail scenarion and sharing the same viewmodel is very useful.
But, then talking between activity and ALL fragments, how could a shared ViewModel be used? Each fragment needs its own complex ViewModel. Could it be a GeneralViewModel which gets instantiated in the activity and in all fragments, together with the regular fragment viewmodel, so have 2 viewmodels in each fragment.
Being able to talk between fragments and activity with a viewmodel will make the finding of active fragment unneeded as the viewmodel will provide the needed mechanism and also would allow to use Navigation component.
Any information is gladly received.
Later edit. Here is some sample code based on the comment bellow. Is this a solution for my question? Can this handle both changes between fragments and parent activity and it's on the recommended side.
private class GlobalViewModel ():ViewModel(){
var eventFromActivity:MutableLiveData<Event>
var eventFromFragment:MutableLiveData<Event>
fun setEventFromActivity(event:Event){
eventFromActivity.value = event
}
fun setEventFromFragment(event:Event){
eventFromFragment.value = event
}
}
Then in my activity
class HomeActivity: AppCompatActivity(){
onCreate{
viewModel = ViewModelProviders.of(this, factory)
.get(GlobalViewModel::class.java)
viewModel.eventsFromFragment.observe(){
//based on the Event values, could update toolbar title, could start
// new fragment, could show a dialog or snackbar
....
}
//when need to update the fragment do
viewModel.setEventFromActivity(event)
}
}
Then in all fragments have something like this
class FragmentA:Fragment(){
onViewCreated(){
viewModel = ViewModelProviders.of(this, factory)
.get(GlobalViewModel::class.java)
viewModel.eventsFromActivity.observe(){
// based on Event value, trigger a fun from the fragment
....
}
viewModelFragment = ViewModelProviders.of(this, factory)
.get(FragmentAViewModel::class.java)
viewModelFragment.some.observe(){
....
}
//when need to update the activity do
viewModel.setEventFromFragment(event)
}
}

How to build UINavigationController-like behaviour in Android?

I'm aware the View structure for Android is completely different (ie. Activities instead of View Controllers) but I need to create a Navigation Bar that persists between views.
Is the best way really to have just one single Activity and then a lot of Fragments?
If so, has this implementation already been done somewhere else that I can use? It seems like something that would come up a lot as I've seen numerous Android apps do this.
I made a Framework (github) to provide a hierarchical navigation pattern, with animations to provide sense of navigation, rather than launching new Activities every time.
Here's how to use it:
Add the framework to your project as a Module
Add a new Java class in your project ("File - New - Java Class").
Note: If you are editing the Activity.java file that provides you the template, delete all its implementations and leave it empty.
Make it extend NavigationActivity
Implement all the NavigationActivity abstract methods
(in Android Studio if you click Alt + insert and select implement - methods all the function definitions are automatically generated).
public class NavigationTest extends NavigationActivity{
#Override
public Fragment firstFragment() {
//return the first fragment that will be shown
}
#Override
public Boolean showBackButtonInFirstFragment() {
//show back button already in the first Fragment
//set to True if this activity is called by another Activity
//the back button will then pop back to the previous Activity
}
#Override
public Boolean showMasterDetailLayoutInTablets() {
//set to false if you don't want a master-detail layout in tablets
}
}
Presenting a new Fragment
You can present a new fragment (with a nice animation) by calling the pushFragment method from NavigationActivity.
public void pushFragment(Fragment newFragment, animationType animation, boolean showAsDetailFragmentIfPossible)
newFragment (Fragment): New Fragment that will be presented
animation (animationType): Animation type enum: RIGHT_TO_LEFT, BOTTOM_TO_TOP, FLIP
showAsDetailFragmentIfPossible (boolean): If set as True, the user is in a Tablet, and you are using a master-detail layout, the Fragment will be shown in the detail Fragment (the panel in the right)!
Since you can access the activity from any Fragment with the getActivity() method, you can show a new Fragment from the currently displaying Fragment.
For example you can put this code within a button click listener:
NextFragment f = new NextFragment();
NavigationActivity nav =((NavigationActivity)getActivity());
nav.pushFragment(f,NavigationActivity.animationType.RIGHT_TO_LEFT,false);
You don't have to worry about implementing the back button behaviour. This is handled automatically by the NavigationActivity class.

MVVM Cross Nesting of fragments gives ViewModel Property of Fragment as NULL

Currently I am using MVVM Cross 4.1.4 in my android Project. I didn't had much time to explore the samples hence I had/am trying to learn
and implement the MVVM Cross framework in my android enterprise application.
I have two huge issues which are making me sleepless and I am sure someone might right there will be facing the same issue.
In my app design I have made a single activity and all the other screens are just fragments which are not exactly controlled by actvity.
The activity just calls ShowViewModel<SomeViewModel> on click of something nothing fancy.
In a particular screen which is itself a fragment I have a view pager which are fragments, so its a nesting of fragments( 3rd fragment in hierarchy) What I noticed is that the ViewModel Property which MvxFragment has is coming null in the 3rd level nesting.
Please see the code
public class MainActivity : MvxCachingFragmentCompatActivity<MainViewModel>
{
}
public class MainViewModel : MvxViewModel
{
ShowViewModel<FirstViewModel>();
}
public class FirstViewModel : MvxViewModel
{
}
[MvxFragment(typeof(MainViewModel), Resource.Id.content_frame)]
public class FirstFragment : MvxFragment<FirstViewModel>
{
}
public class SecondViewModel : MvxViewModel
{
}
[MvxFragment(typeof(MainViewModel), Resource.Id.content_frame)]
[MvxFragment(typeof(FirstViewModel), Resource.Id.content_frame)]
public class SecondFragment: MvxFragment<SecondViewModel>
{
}
The problem is inside the first fragment I have to manage a work flow of three fragments which can then have more fragments inside them i.e. 4 level fragments.
The problem is inside the SecondFragment which is hosted by FirstFragment's viewpager adapter, the ViewModel property is coming null, WHY is that?
Since in FirstFragment ViewModel property doesn't come null and I can easily hit my PCL to get the data.
I know I am doing something wrong any help will be much appreciated. I have many problems to solve in my MVVM android project.

Android TablaLayout and NavigationDrawer

I have a two problems and struggle for a few days. I am not sure what the right way is.
I want to combine a tab layout with a view pager containing three fragments (the tab layout and the view pager are the home screen) and a navigation drawer.
And I think that everything you can navigate to from the drawer should be a fragment accept settings or something similar.
So should I let the tab layout in the layout of the main activities layout or should I put the this layout also in a fragment (so view pager with fragments inside a so calling home fragment)?
When starting the app I check if the user is logged in and if he is download data from Firebase. I do this in another fragment (data fragment) there I setretaininstance to true.
So currently one of the tab fragments needs the data from Firebase but soon all of them will need it.
How do I pass the data (Lists) from the data fragment to the tab fragments or the so called home fragment and then to the tabs.
I already tried interfaces but I don't know how to handle confirmation changes.
Considering the two problems or the combination of both how would you solve this / which design approach is better only use fragments or let the home screen inside the main activity and change the view when the user navigates from the drawer to fragment?
PS. I will add some code as soon as possible.
Thanks.
TabLayout and the ViewPager should be in the Activity layout since it makes more sense, as the Activity manages the Fragments. Also, navigation drawer should navigates through Activities, and Activities between Fragments. Again, that's the logical thing for a decent code organization, and it prevents repetitions in layout (AKA more than one button to navigate to the same fragment).
For the Firebase issue, I suggest you start the call in the Activity, and then call a function in all fragments with the retrieved information. Something like this:
new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for(MyFragment f : mFragmentList)
f.addData(dataSnapshot);
}
}
If you really want the Fragment to make the call to Firebase, then you can create you own Event and Listener set.
public class SnapshotRetrievedEvent {
private static List<SnapshotRetrievedListener> listeners = new ArrayList<>();
public static void addListener(SnapshotRetrievedListener listener){
listeners.add(listener);
}
public static void notify(DataSnapshot snapshot) {
for(SnapshotRetrievedListener listener : listeners)
listener.onSnapshotRetrived( snapshot );
}
}
public interface SnapshotRetrievedListene {
void onSnapshotRetrieved(DataSnapshot snapshot);
}
Then in your Fragment waiting for the snapshot, you add the listener like this:
SnapshotRetrievedEvent.addListener(
new SnapshotRetrievedListener(){
#Override
public void onSnapshotRetrieved(DataSnapshot snapshot){
// Do stuff
}
}
);
onSnapshotRetrieved will be called when you call SnapshotRetrievedEvent.notify(snapshot) in your other Fragment.
Hope this helps

Android UINavigationController-like feature

On the iPhone I use a Navigation Controller to push and pop Views from. Very handy.
Is there an equivalent in Android?
This is an old question, but I believe the answer has changed. It is now possible to imitate the Nav stack in iOS in android using Fragments.
http://developer.android.com/reference/android/app/Fragment.html
Basically instead of jumping from Activity to Activity you instead stay in one Activity that controls the display, organization, and animation of Fragments which each contain their own behavior much like the NavController / UIViewController model in iOS.
It is also backwards compatible as a static library so you can implement it on pre-Honeycomb devices.
Strategies for Honeycomb & backward compatibility
Typically in android, each view is displayed in its own Activity. You can read about activities in the application fundamentals documentation. To move to a new Activity, or view, you use an intent.
If you haven't done so yet, I'd highly recommend reading through those introductary android docs. They aren't too long, and do a good job of explaning the basic program structure.
I made a Framework (github) to provide a hierarchical navigation pattern, with animations to provide sense of navigation, rather than launching new Activities every time.
Here's how to use it:
Add the framework to your project as a Module
Add a new Java class in your project ("File - New - Java Class").
Note: If you are editing the Activity.java file that provides you the template, delete all its implementations and leave it empty.
Make it extend NavigationActivity
Implement all the NavigationActivity abstract methods
(in Android Studio if you click Alt + insert and select implement - methods all the function definitions are automatically generated).
public class NavigationTest extends NavigationActivity{
#Override
public Fragment firstFragment() {
//return the first fragment that will be shown
}
#Override
public Boolean showBackButtonInFirstFragment() {
//show back button already in the first Fragment
//set to True if this activity is called by another Activity
//the back button will then pop back to the previous Activity
}
#Override
public Boolean showMasterDetailLayoutInTablets() {
//set to false if you don't want a master-detail layout in tablets
}
}
Presenting a new Fragment
You can present a new fragment (with a nice animation) by calling the pushFragment method from NavigationActivity.
public void pushFragment(Fragment newFragment, animationType animation, boolean showAsDetailFragmentIfPossible)
newFragment (Fragment): New Fragment that will be presented
animation (animationType): Animation type enum: RIGHT_TO_LEFT, BOTTOM_TO_TOP, FLIP
showAsDetailFragmentIfPossible (boolean): If set as True, the user is in a Tablet, and you are using a master-detail layout, the Fragment will be shown in the detail Fragment (the panel in the right)!
Since you can access the activity from any Fragment with the getActivity() method, you can show a new Fragment from the currently displaying Fragment.
For example you can put this code within a button click listener:
NextFragment f = new NextFragment();
NavigationActivity nav =((NavigationActivity)getActivity());
nav.pushFragment(f,NavigationActivity.animationType.RIGHT_TO_LEFT,false);
You don't have to worry about implementing the back button behaviour. This is handled automatically by the NavigationActivity class.
There are thee basic types in Android to show UI in Android:
View
Fragment
Activity
Google IO 2018 introduced Navigation component which should make life easier. It is a wrapper under a standard mechanisms.
Here you can find NavGraph which looks like storyboard and NavController which help to navigate to destination

Categories

Resources