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

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.

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)
}
}

Reuse single ViewModel with multiple fragments. MvvmCross 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.

Using onMenuItemCLickListener from activity to call methods from multiple fragments

i wish you all a good day.
First of all, sorry if i mispell frecuently because i din't know how to write on english, at least properly. But that's not a problem for all the response's that u can give me because i can read english pretty well (yeah, im a lazzy a.s.s. to learn it as should have to).
Second, im learning android right now and had been developing an app for like a month. The problem is that my app have an actionbar Menu on the Activity that contains 3 fragments THAT ARE ADDED BY a VIEWPAGER, so i can't cast them in activity to run their methods.... Thats a big problem!. Each one of those fragments have several EditText, and what i want is to USE MY ACTIVITY ACTIONBAR MENU ITEM CLICK to store all data from the EdtiText's and store it in an SQLite Database; one table per fragment.
I have been done everything and the only thing that i need isis to know how to call methods from the three fragments when pressing an item, on the onOptionClickListener OF THE MENU ITEM OF MY ACTIVITY. (The most important thing is that is the menu of my activity, not the fragment's, and i cant instantate the fragments on my activity because im using viewpager to create them).
Sorry again of my mispell's, and sorry for not posting my code, but, is really large so instead of make it clear my problem im gonna confuse everbody, so its better that you can help me withow the need of my really large code, so thanks for understanding and helping me.
There is a class for this.
Check Observer class. actually is nothing too complicated. You can easily remake this behave.
*just make a interface with a method
public interface MyObserverInterface {
//the code that will run when a save button is clicked in your menu
//fragment
public void starAction();
}
*make your fragments implament this interface.
*create a second class in wich you will get a reference to your fragments
public class MyObserver {
List<Fragment> listFragments;
//make sure your fragments implement the MyObserverInterface interface
public MyObserver(Fragment fragment1, Fragment fragment2) {
listFragments = new ArrayList<Fragment>();
listFragments.add(fragment1);
listFragments.add(fragment2);
}
public void startActionInAllFragments(){
for(int n=0;n<listFragments.size();n++){
listFragments.get(n).starAction();
}
}
}
Now just create an instance of MyObserver class in your fragment and call its method startActionInAllFragments()
let me know if it worked for u.

Illegalstateexception activity has been destroyed fragmenttransaction.commit()

I have seen a few versions of this question before, but the reasons for this exception were different than my own it seems.
What I am trying to do:
-Main Activity class has a toolbar at the bottom, clicking the buttons will display a series of fragments, one after another.
- A class EditItemFragmentManager, which is instatiated on a button click, and has methods that display specific fragments based on the toolbar button clicked.
I would like to use this manager class I created because it cleans my code up significantly and will make adding more features later helpful.
Here is my EditItemFragmentManager class, I am not sure if extending Activity is a good idea or not, I think that it will put my MainActivity on pause
public class EditItemFragmentManager extends Activity{
//instance variables
public EditItemFragmentManager(){
// initialization of some variables
}
public void editItem(){
editItemSequence();
}
private void editItemSequence(){
EditNameFragment enf = new EditNameFragment();
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
fragmentTransaction.add(editNameFragment, EDIT_FRAG_TAG);
fragmentTransaction.addToBackStack(EDIT_FRAG_TAG);
fragmentTransaction.commit();
}
}
So it blows up when commit(); is called, giving me
java.lang.IllegalStateException: Activity has been destroyed
This is how I am trying to get this fragment from my MainActivity,
#Override
public void onClick(View view) {
EditIteFragmetManager manager = new EditIteFragmetManager();
manager.editItem();
}
I am still learning about the Acvtivity lifecycle in Android. I think my problem is something due to this class extending Activity, which puts my Main on pause, and the FragmentTransaction has nothing to commit to? If so, I need to get the existing instance of my main activity and call it on that? This is where I'm a bit lost, if anyone who understands the lifecycle of Activities/Fragments explain how I could go about implementing this while still having a helper class such as this?
If you're using the SupportFragmentManager, then you need to extend from FragmentActivity, and not just Activity. Also make sure that you imported the Fragment from the v4 support library, and not android.app.
Other than that, you seem to be instantiating a subclass of Activity with "new", which is terrible. Create activities only using Intents.
I solved this issue by moving my manager class to become a private inner class of my main, since they are so tightly coupled. No fragment issues now.

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