How can activity call a method inside fragment created dynamically - java

I have a main activity which creates fragments using the following code
private void launchFragment(int pos)
{
Fragment f = null;
String title = null;
if (pos == 1)
{
title = "Friends";
f = new FriendList();
}
else if (pos == 2)
{
title = "Notes";
f = new NoteList();
}
else if (pos == 3)
{
title = "Projects";
f = new ProjectList();
}
else if (pos == 5)
{
title = "About";
f = new AboutUs();
}
else if (pos == 6)
{
startActivity(new Intent(this, Login.class));
finish();
}
if (f != null)
{
while (getSupportFragmentManager().getBackStackEntryCount() > 0)
{
getSupportFragmentManager().popBackStackImmediate();
}
getSupportFragmentManager().beginTransaction()
.replace(R.id.content_frame, f).addToBackStack(title)
.commit();
}
}
Here is the code of a fragment.
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState)
{
View v = inflater.inflate(R.layout.group_chat, null);
loadConversationList();
contactName = this.getArguments().getString("contactusername");
contactId = this.getArguments().getString("contactid");
ListView list = (ListView) v.findViewById(R.id.list);
adp = new ChatAdapter();
list.setAdapter(adp);
list.setTranscriptMode(AbsListView.TRANSCRIPT_MODE_ALWAYS_SCROLL);
list.setStackFromBottom(true);
txt = (EditText) v.findViewById(R.id.txt);
txt.setInputType(InputType.TYPE_CLASS_TEXT
| InputType.TYPE_TEXT_FLAG_MULTI_LINE);
setTouchNClick(v.findViewById(R.id.btnCamera));
setTouchNClick(v.findViewById(R.id.btnSend));
return v;
}
I want to call a method in above fragment class. I was not able to do this as I have not given id of the fragment in the XML file. I am not loading the static fragment using XML. Therefore, I don't have Id.
I have already seen this and this questions on the StackOverFlow, but they are not solving my problem.
Kindly help if anyone knows how to tackle this scenario.

First of all make all your fragments implement an interface. This interface will return a String (for example) which will identify your fragment, and then cast your fragment to it after getting the fragment using findFragmentById() as follows:
Create your interface
public interface IFragmentName
{
public String getFragmentName();
}
Implement your interface (for example in NoteList)
public NoteList extends Fragment implements IFragmentName
{
//Do your stuff...
public String getFragmentName()
{
return "NoteList";
}
}
After this get your current fragment from your activity
IFragmentName myFragment = (IFragmentName) getSupportFragmentManager().findFragmentById(R.id.content_frame);
Finally check your getFragmentName() value and cast to the fragment you want:
if(myFragment.getFragmentName().equals("NoteList")
{
NoteList myNoteListFragment = (NoteList) myFragment;
myNoteListFragment.callMyMethod(); //here you call the method of your current Fragment.
}
I have coded these snippets without any IDE so maybe I have missed a semicolon or something like that :)
Hope it helps

I know I am too late for the party. But this will be useful for others.
If at all you are using ViewPager to render the fragment use this code in your parent Activity.
Check Solution here

Related

Android Fragment.getView() returns NULL after BackPressed

My question is quite similar to this one: fragment.getView() return null after backpressed
The problem is next: I have a special Fragment with two states: A and B. If Fragment is in the state B, Backpress should switch the state from B to A. (The difference between two states is in visibility of some elements on a layout) If Fragment is in state A, Backpress should close this Fragment. I overwrote the onBackPressed() method in my activity in next way:
#Override
public void onBackPressed() {
int count = getSupportFragmentManager().getBackStackEntryCount();
if (count > 1) {
if (isStateB) {
isStateB = false;
MessagesFragment messFragment = ((MessagesFragment) getSupportFragmentManager().findFragmentByTag("MessagesFragment"));
if (messFragment != null) {
Log.d(TAG, "Message Fragment Refresh");
messFragment.refreshFragWithoutMessChecked();
return;
} else {
Log.d(TAG, "Fail To Message Fragment Resfresh");
}
}
}
}
super.onBackPressed();
}
This code executes refreshFragWithoutMessChecked() but nothing happens. All elements from the layout which I am trying to close aren't null, but code doesn't affect them. Also I have a button in the MessageFragment which executes the similar method, and in case I press it, the code works well. In additional I found out that if I call getView() inside refreshFragWithoutMessChecked(). In case when it called from onBackPressed(), getView() returns NULL. In case when it called from onClick() it returns something isn't NULL.
So that is why I am asking, why Backpress makes my getView() returns NULL, and how can I solve my problem?
Fragment Code
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
view = inflater.inflate(R.layout.frag_messages, container, false);
/*
Here is a big amount of elements initizalizations and onClick bindings like this:
check_message = view.findViewById(R.id.check_message);
check_message.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {MainActivity.isStateB = true;}});
*/
return view;
}
public void refreshFragWithoutMessChecked() {
MainActivity.isStateB = false;
MainActivity.posMessChecksEnabled.clear();
refreshMessageFragment();
Log.d(TAG,"Message Fragment Refreshed");
}
// Refresh Message Fragment
public void refreshMessageFragment() {
if(getView() != null){
Log.d(TAG,"A");
} else {
Log.d(TAG,"B");
}
// Hide Message Check Show
// THIS CODE DOESN'T WORK ON BACK PRESS BUT IT'S EXECUTED
if (MainActivity.isStateB == true) {
((MainActivity) getActivity()).getSupportActionBar().hide();
write_message_layout.setVisibility(View.GONE);
reply_forward_bottom.setVisibility(View.VISIBLE);
up_message_selected_panel.setVisibility(View.VISIBLE);
toolbar.setVisibility(View.GONE);
appbar_layout.setVisibility(View.GONE);
} else {
if (fromOpen.equalsIgnoreCase(MainActivity.MESSAGES_FILTER_CONTACTS) == false) {
write_message_layout.setVisibility(View.VISIBLE);
}
reply_forward_bottom.setVisibility(View.GONE);
up_message_selected_panel.setVisibility(View.GONE);
toolbar.setVisibility(View.VISIBLE);
appbar_layout.setVisibility(View.VISIBLE);
}
}

My fragments keep recreating whenever I reclick or navigate to the next fragment

I have implemented the new architecture component on my android app, but unfortunately handling the states of these fragments has become a nightmare for me. Whenever I press the icon of the fragment, the fragment is recreated every time I navigate. How can I handle this or rather save these fragments states?
Here is my main activity handling the five fragments:
public class MainActivityCenterofInformation extends AppCompatActivity {
BottomNavigationView bottomNavigationView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate (savedInstanceState);
setContentView (R.layout.activity_maincict);
setUpNavigation ();
}
public void setUpNavigation(){
bottomNavigationView = findViewById (R.id.bottom_nav_cict);
NavHostFragment navHostFragment = (NavHostFragment)getSupportFragmentManager ()
.findFragmentById (R.id.nav_host_fragment_cict);
NavigationUI.setupWithNavController (bottomNavigationView, navHostFragment.getNavController ());
}
//adding animations to the fragment
}
I can't read Kotlin so please direct me to Java, thanks.
TL;DR: skip to the JUST SHOW ME THE STEPS ALREADY !!! section
That is the normal behaviour of the fragments. They are suppose to be recreated every time they are removed or replaced and you are suppose to restore their states using onSaveInstanceState.
Here is a nice article that describes how to do it : Saving Fragment States
Other than that you can use View Model which is the part of the following recommended android architecture. They are a great way to retain and restore UI data.
You can learn how to implement this architecture by following this step by step code lab
EDIT : Solution
Note : The solution assumes one doesn't wants to use ViewModels and simply wants to hide or show fragments using the Navigation UI
It covers the following points
Keeping fragment alive during navigation
Implement custom navigation behavior on back key press (Optional)
Background :
Android Navigation component has a NavController class that you can use to navigate to different destinations. NavController uses a Navigator that actually does the navigation. Navigator is an abstract class and anyone can extend/inherit it to provide custom navigation depending on the type of destination. When using fragments as destinations the NavHostFragment uses a FragmentNavigator whose default implementation replaces the fragments whenever we navigate using FragmentTransaction.replace() which completely destroys the previous fragment and adds a new fragment. So we have to create our own navigator and instead of using FragmentTransaction.replace() we will use a combination of FragmentTransaction.hide() and FragmentTransaction.show() to avoid fragments from being destroyed.
Default behavior of Navigation UI :
By default whenever you navigate to any other fragment other than the home fragment they won't get added to backstack so lets say if you select fragments in the following order
A -> B -> C -> D -> E
your back stack will have only
[A, E]
as you can see the fragments B, C, D weren't added to backstack so pressing back press will always get you to fragment A which is the home fragment
The behavior we want for now :
We want a simple yet effective behavior. We wan't all fragments to get added to backstack but if the fragment is already in backstack we want to pop all fragments upto the selected fragment.
Lets say I select fragment in following order
A -> B -> C -> D -> E
the backstack should also be
[A, B, C, D, E]
upon pressing back only the last fragment should be popped and backstack should be like this
[A, B, C, D]
but if we navigate to lets say fragment B, since B is already in the stack then all the fragments above B should be popped and our backstack should look like this
[A, B]
I hope this behavior makes sense. This behavior is easy to implement using global actions as you will see below and is better than the default one.
OK Hotshot! now what ? :
Now we have two options
extend FragmentNavigator
copy/paste FragmentNavigator
Well I personally wanted to just extend FragmentNavigator and override navigate() method but since all its member variables are private I couldn't implement proper navigation.
So I decided to copy paste the entire FragmentNavigator class and just change the name in entire code from "FragmentNavigator" to whatever I want to call it.
JUST SHOW ME THE STEPS ALREADY !!! :
Create custom navigator
Use custom tag
Add global actions
Use global actions
Add the custom navigator to the NavController
STEP 1: Create custom navigator
Here is my custom navigator called StickyCustomNavigator. All the code is same as FragmentNavigator except the navigate() method. As you can see it uses hide() , show() and add() method instead of replace(). The logic is simple. Hide the previous fragment and show the destination fragment. If this is our first time going to a specific destination fragment then add the fragment instead of showing it.
#Navigator.Name("sticky_fragment")
public class StickyFragmentNavigator extends Navigator<StickyFragmentNavigator.Destination> {
private static final String TAG = "StickyFragmentNavigator";
private static final String KEY_BACK_STACK_IDS = "androidx-nav-fragment:navigator:backStackIds";
private final Context mContext;
#SuppressWarnings("WeakerAccess") /* synthetic access */
final FragmentManager mFragmentManager;
private final int mContainerId;
#SuppressWarnings("WeakerAccess") /* synthetic access */
ArrayDeque<Integer> mBackStack = new ArrayDeque<>();
#SuppressWarnings("WeakerAccess") /* synthetic access */
boolean mIsPendingBackStackOperation = false;
private final FragmentManager.OnBackStackChangedListener mOnBackStackChangedListener =
new FragmentManager.OnBackStackChangedListener() {
#SuppressLint("RestrictedApi")
#Override
public void onBackStackChanged() {
// If we have pending operations made by us then consume this change, otherwise
// detect a pop in the back stack to dispatch callback.
if (mIsPendingBackStackOperation) {
mIsPendingBackStackOperation = !isBackStackEqual();
return;
}
// The initial Fragment won't be on the back stack, so the
// real count of destinations is the back stack entry count + 1
int newCount = mFragmentManager.getBackStackEntryCount() + 1;
if (newCount < mBackStack.size()) {
// Handle cases where the user hit the system back button
while (mBackStack.size() > newCount) {
mBackStack.removeLast();
}
dispatchOnNavigatorBackPress();
}
}
};
public StickyFragmentNavigator(#NonNull Context context, #NonNull FragmentManager manager,
int containerId) {
mContext = context;
mFragmentManager = manager;
mContainerId = containerId;
}
#Override
protected void onBackPressAdded() {
mFragmentManager.addOnBackStackChangedListener(mOnBackStackChangedListener);
}
#Override
protected void onBackPressRemoved() {
mFragmentManager.removeOnBackStackChangedListener(mOnBackStackChangedListener);
}
#Override
public boolean popBackStack() {
if (mBackStack.isEmpty()) {
return false;
}
if (mFragmentManager.isStateSaved()) {
Log.i(TAG, "Ignoring popBackStack() call: FragmentManager has already"
+ " saved its state");
return false;
}
if (mFragmentManager.getBackStackEntryCount() > 0) {
mFragmentManager.popBackStack(
generateBackStackName(mBackStack.size(), mBackStack.peekLast()),
FragmentManager.POP_BACK_STACK_INCLUSIVE);
mIsPendingBackStackOperation = true;
} // else, we're on the first Fragment, so there's nothing to pop from FragmentManager
mBackStack.removeLast();
return true;
}
#NonNull
#Override
public StickyFragmentNavigator.Destination createDestination() {
return new StickyFragmentNavigator.Destination(this);
}
#NonNull
public Fragment instantiateFragment(#NonNull Context context,
#SuppressWarnings("unused") #NonNull FragmentManager fragmentManager,
#NonNull String className, #Nullable Bundle args) {
return Fragment.instantiate(context, className, args);
}
#Nullable
#Override
public NavDestination navigate(#NonNull StickyFragmentNavigator.Destination destination, #Nullable Bundle args,
#Nullable NavOptions navOptions, #Nullable Navigator.Extras navigatorExtras) {
if (mFragmentManager.isStateSaved()) {
Log.i(TAG, "Ignoring navigate() call: FragmentManager has already"
+ " saved its state");
return null;
}
String className = destination.getClassName();
if (className.charAt(0) == '.') {
className = mContext.getPackageName() + className;
}
final FragmentTransaction ft = mFragmentManager.beginTransaction();
int enterAnim = navOptions != null ? navOptions.getEnterAnim() : -1;
int exitAnim = navOptions != null ? navOptions.getExitAnim() : -1;
int popEnterAnim = navOptions != null ? navOptions.getPopEnterAnim() : -1;
int popExitAnim = navOptions != null ? navOptions.getPopExitAnim() : -1;
if (enterAnim != -1 || exitAnim != -1 || popEnterAnim != -1 || popExitAnim != -1) {
enterAnim = enterAnim != -1 ? enterAnim : 0;
exitAnim = exitAnim != -1 ? exitAnim : 0;
popEnterAnim = popEnterAnim != -1 ? popEnterAnim : 0;
popExitAnim = popExitAnim != -1 ? popExitAnim : 0;
ft.setCustomAnimations(enterAnim, exitAnim, popEnterAnim, popExitAnim);
}
String tag = Integer.toString(destination.getId());
Fragment primaryNavigationFragment = mFragmentManager.getPrimaryNavigationFragment();
if(primaryNavigationFragment != null)
ft.hide(primaryNavigationFragment);
Fragment destinationFragment = mFragmentManager.findFragmentByTag(tag);
if(destinationFragment == null) {
destinationFragment = instantiateFragment(mContext, mFragmentManager, className, args);
destinationFragment.setArguments(args);
ft.add(mContainerId, destinationFragment , tag);
}
else
ft.show(destinationFragment);
ft.setPrimaryNavigationFragment(destinationFragment);
final #IdRes int destId = destination.getId();
final boolean initialNavigation = mBackStack.isEmpty();
// TODO Build first class singleTop behavior for fragments
final boolean isSingleTopReplacement = navOptions != null && !initialNavigation
&& navOptions.shouldLaunchSingleTop()
&& mBackStack.peekLast() == destId;
boolean isAdded;
if (initialNavigation) {
isAdded = true;
} else if (isSingleTopReplacement) {
// Single Top means we only want one instance on the back stack
if (mBackStack.size() > 1) {
// If the Fragment to be replaced is on the FragmentManager's
// back stack, a simple replace() isn't enough so we
// remove it from the back stack and put our replacement
// on the back stack in its place
mFragmentManager.popBackStackImmediate(
generateBackStackName(mBackStack.size(), mBackStack.peekLast()), 0);
mIsPendingBackStackOperation = false;
}
isAdded = false;
} else {
ft.addToBackStack(generateBackStackName(mBackStack.size() + 1, destId));
mIsPendingBackStackOperation = true;
isAdded = true;
}
if (navigatorExtras instanceof FragmentNavigator.Extras) {
FragmentNavigator.Extras extras = (FragmentNavigator.Extras) navigatorExtras;
for (Map.Entry<View, String> sharedElement : extras.getSharedElements().entrySet()) {
ft.addSharedElement(sharedElement.getKey(), sharedElement.getValue());
}
}
ft.setReorderingAllowed(true);
ft.commit();
// The commit succeeded, update our view of the world
if (isAdded) {
mBackStack.add(destId);
return destination;
} else {
return null;
}
}
#Override
#Nullable
public Bundle onSaveState() {
Bundle b = new Bundle();
int[] backStack = new int[mBackStack.size()];
int index = 0;
for (Integer id : mBackStack) {
backStack[index++] = id;
}
b.putIntArray(KEY_BACK_STACK_IDS, backStack);
return b;
}
#Override
public void onRestoreState(#Nullable Bundle savedState) {
if (savedState != null) {
int[] backStack = savedState.getIntArray(KEY_BACK_STACK_IDS);
if (backStack != null) {
mBackStack.clear();
for (int destId : backStack) {
mBackStack.add(destId);
}
}
}
}
#NonNull
private String generateBackStackName(int backStackIndex, int destId) {
return backStackIndex + "-" + destId;
}
private int getDestId(#Nullable String backStackName) {
String[] split = backStackName != null ? backStackName.split("-") : new String[0];
if (split.length != 2) {
throw new IllegalStateException("Invalid back stack entry on the "
+ "NavHostFragment's back stack - use getChildFragmentManager() "
+ "if you need to do custom FragmentTransactions from within "
+ "Fragments created via your navigation graph.");
}
try {
// Just make sure the backStackIndex is correctly formatted
Integer.parseInt(split[0]);
return Integer.parseInt(split[1]);
} catch (NumberFormatException e) {
throw new IllegalStateException("Invalid back stack entry on the "
+ "NavHostFragment's back stack - use getChildFragmentManager() "
+ "if you need to do custom FragmentTransactions from within "
+ "Fragments created via your navigation graph.");
}
}
#SuppressWarnings("WeakerAccess") /* synthetic access */
boolean isBackStackEqual() {
int fragmentBackStackCount = mFragmentManager.getBackStackEntryCount();
// Initial fragment won't be on the FragmentManager's back stack so +1 its count.
if (mBackStack.size() != fragmentBackStackCount + 1) {
return false;
}
// From top to bottom verify destination ids match in both back stacks/
Iterator<Integer> backStackIterator = mBackStack.descendingIterator();
int fragmentBackStackIndex = fragmentBackStackCount - 1;
while (backStackIterator.hasNext() && fragmentBackStackIndex >= 0) {
int destId = backStackIterator.next();
try {
int fragmentDestId = getDestId(mFragmentManager
.getBackStackEntryAt(fragmentBackStackIndex--)
.getName());
if (destId != fragmentDestId) {
return false;
}
} catch (NumberFormatException e) {
throw new IllegalStateException("Invalid back stack entry on the "
+ "NavHostFragment's back stack - use getChildFragmentManager() "
+ "if you need to do custom FragmentTransactions from within "
+ "Fragments created via your navigation graph.");
}
}
return true;
}
#NavDestination.ClassType(Fragment.class)
public static class Destination extends NavDestination {
private String mClassName;
public Destination(#NonNull NavigatorProvider navigatorProvider) {
this(navigatorProvider.getNavigator(StickyFragmentNavigator.class));
}
public Destination(#NonNull Navigator<? extends StickyFragmentNavigator.Destination> fragmentNavigator) {
super(fragmentNavigator);
}
#CallSuper
#Override
public void onInflate(#NonNull Context context, #NonNull AttributeSet attrs) {
super.onInflate(context, attrs);
TypedArray a = context.getResources().obtainAttributes(attrs,
R.styleable.FragmentNavigator);
String className = a.getString(R.styleable.FragmentNavigator_android_name);
if (className != null) {
setClassName(className);
}
a.recycle();
}
#NonNull
public final StickyFragmentNavigator.Destination setClassName(#NonNull String className) {
mClassName = className;
return this;
}
#NonNull
public final String getClassName() {
if (mClassName == null) {
throw new IllegalStateException("Fragment class was not set");
}
return mClassName;
}
}
public static final class Extras implements Navigator.Extras {
private final LinkedHashMap<View, String> mSharedElements = new LinkedHashMap<>();
Extras(Map<View, String> sharedElements) {
mSharedElements.putAll(sharedElements);
}
#NonNull
public Map<View, String> getSharedElements() {
return Collections.unmodifiableMap(mSharedElements);
}
public static final class Builder {
private final LinkedHashMap<View, String> mSharedElements = new LinkedHashMap<>();
#NonNull
public StickyFragmentNavigator.Extras.Builder addSharedElements(#NonNull Map<View, String> sharedElements) {
for (Map.Entry<View, String> sharedElement : sharedElements.entrySet()) {
View view = sharedElement.getKey();
String name = sharedElement.getValue();
if (view != null && name != null) {
addSharedElement(view, name);
}
}
return this;
}
#NonNull
public StickyFragmentNavigator.Extras.Builder addSharedElement(#NonNull View sharedElement, #NonNull String name) {
mSharedElements.put(sharedElement, name);
return this;
}
#NonNull
public StickyFragmentNavigator.Extras build() {
return new StickyFragmentNavigator.Extras(mSharedElements);
}
}
}
}
STEP 2: Use custom tag
Now open up your navigation.xml file and rename the fragment tags related to your bottom navigation with whatever name you gave in #Navigator.Name() earlier.
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/mobile_navigation"
app:startDestination="#+id/navigation_home">
<sticky_fragment
android:id="#+id/navigation_home"
android:name="com.example.bottomnavigationlogic.ui.home.HomeFragment"
android:label="#string/title_home"
tools:layout="#layout/fragment_home" />
</navigation>
STEP 3: Add global action
Global actions are a way to navigate to destination from anywhere in your app. You can use the visual editor or directly use xml to add global actions. Set global action on each fragment with the following settings
destination : self
popUpTo : self
singleTop : true/checked
This your how your navigation.xml should look like after adding global actions
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/mobile_navigation"
app:startDestination="#+id/navigation_home">
<sticky_fragment
android:id="#+id/navigation_home"
android:name="com.example.bottomnavigationlogic.ui.home.HomeFragment"
android:label="#string/title_home"
tools:layout="#layout/fragment_home" />
<sticky_fragment
android:id="#+id/navigation_images"
android:name="com.example.bottomnavigationlogic.ui.images.ImagesFragment"
android:label="#string/title_images"
tools:layout="#layout/fragment_images" />
<sticky_fragment
android:id="#+id/navigation_videos"
android:name="com.example.bottomnavigationlogic.ui.videos.VideosFragment"
android:label="#string/title_videos"
tools:layout="#layout/fragment_videos" />
<sticky_fragment
android:id="#+id/navigation_songs"
android:name="com.example.bottomnavigationlogic.ui.songs.SongsFragment"
android:label="#string/title_songs"
tools:layout="#layout/fragment_songs" />
<sticky_fragment
android:id="#+id/navigation_notifications"
android:name="com.example.bottomnavigationlogic.ui.notifications.NotificationsFragment"
android:label="#string/title_notifications"
tools:layout="#layout/fragment_notifications" />
<action
android:id="#+id/action_global_navigation_home"
app:destination="#id/navigation_home"
app:launchSingleTop="true"
app:popUpTo="#id/navigation_home" />
<action
android:id="#+id/action_global_navigation_notifications"
app:destination="#id/navigation_notifications"
app:launchSingleTop="true"
app:popUpTo="#id/navigation_notifications" />
<action
android:id="#+id/action_global_navigation_songs"
app:destination="#id/navigation_songs"
app:launchSingleTop="true"
app:popUpTo="#id/navigation_songs" />
<action
android:id="#+id/action_global_navigation_videos"
app:destination="#id/navigation_videos"
app:launchSingleTop="true"
app:popUpTo="#id/navigation_videos" />
</navigation>
STEP 4: Use global actions
When you wrote
NavigationUI.setupWithNavController (bottomNavigationView, navHostFragment.getNavController ());
then inside setupWithNavController() NavigationUI uses bottomNavigationView.setOnNavigationItemSelectedListener() to navigate to proper fragments depending on id of the menu item that was clicked. It's default behavior is as I mentioned before. We will add our own implementation to it and use global actions to achieve our desired back press behavior.
Here is how you do it simply in MainActivity
bottomNavigationView.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem menuItem) {
int id = menuItem.getItemId();
if (menuItem.isChecked()) return false;
switch (id)
{
case R.id.navigation_home :
navController.navigate(R.id.action_global_navigation_home);
break;
case R.id.navigation_images :
navController.navigate(R.id.action_global_navigation_images);
break;
case R.id.navigation_videos :
navController.navigate(R.id.action_global_navigation_videos);
break;
case R.id.navigation_songs :
navController.navigate(R.id.action_global_navigation_songs);
break;
case R.id.navigation_notifications :
navController.navigate(R.id.action_global_navigation_notifications);
break;
}
return true;
}
});
FINAL STEP 5: Add your custom navigator to NavController
Add your navigator as follow in your MainActivity. Make sure you are passing childFragmentManager of the NavHostFragment.
navController.getNavigatorProvider().addNavigator(new StickyFragmentNavigator(this, navHostFragment.getChildFragmentManager(),R.id.nav_host_fragment));
Also add the navigation graph to NavController here as well using setGraph() method as shown below.
This is how my MainActivity looks like after step 4 and step 5
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
BottomNavigationView navView = findViewById(R.id.nav_view);
AppBarConfiguration appBarConfiguration = new AppBarConfiguration.Builder(
R.id.navigation_home, R.id.navigation_images, R.id.navigation_videos,R.id.navigation_songs,R.id.navigation_notifications)
.build();
NavHostFragment navHostFragment = (NavHostFragment)getSupportFragmentManager().findFragmentById(R.id.nav_host_fragment);
final NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment);
navController.getNavigatorProvider().addNavigator(new StickyFragmentNavigator(this, navHostFragment.getChildFragmentManager(),R.id.nav_host_fragment));
navController.setGraph(R.navigation.mobile_navigation);
NavigationUI.setupActionBarWithNavController(this, navController, appBarConfiguration);
NavigationUI.setupWithNavController(navView,navController);
navView.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem menuItem) {
int id = menuItem.getItemId();
if (menuItem.isChecked()) return false;
switch (id)
{
case R.id.navigation_home :
navController.navigate(R.id.action_global_navigation_home);
break;
case R.id.navigation_images :
navController.navigate(R.id.action_global_navigation_images);
break;
case R.id.navigation_videos :
navController.navigate(R.id.action_global_navigation_videos);
break;
case R.id.navigation_songs :
navController.navigate(R.id.action_global_navigation_songs);
break;
case R.id.navigation_notifications :
navController.navigate(R.id.action_global_navigation_notifications);
break;
}
return true;
}
});
}
}
Hope this helps.
I am not entirely sure if this is the answer you are looking for but if you are concerned about managing state the modern way of managing state is by using something called a view model. View models are a component of the MVVM architecture. Their purpose is to hold and expose data to your fragment/activity to display. With the navigation architecture, if you store the data related to each fragment in this view model appropriately your state will be retained in the view model.
That being said I would personally suggest looking into MVVM architecture as well as view models specifically. Otherwise, a brute force for retaining state is by using the savedInstance state throughout fragments and manually saving and recovering important data.
Links:
- Android View Model Component
- Android architecture guide
I think you might need to prevent recreating the fragment upon clicking bottom nav view item twice. bottomNavigationView.setOnNavigationItemReselectedListener { /*Nothing to ignore reselection*/} after NavigationUI.setupWithNavController (bottomNavigationView, navHostFragment.getNavController ());

Save state when navigating between fragments

I'm working on an app and I have a menu with a NavigationDrawer to navigate between fragments. In one of the fragments I make a call to the backend and then save the results in a list. When I navigate to another fragment and back, the results are gone, but I'd like to save the contents of the list temporarily. I wanted to use onSaveInstanceState(), but that method doesn't seem to get called ever. I also looked if the data is still in the fields when I return to the fragment, but that also wasn't the case. I think I'm doing something wrong with the FragmentManager, but I'm not sure about it.
This is the method used for the transactions for the Fragments:
private void openFragment(Class fragmentClass) {
Fragment fragment;
try {
fragment = (Fragment) fragmentClass.newInstance();
} catch (InstantiationException | IllegalAccessException e) {
e.printStackTrace();
return;
}
contentFrame.removeAllViews();
FragmentManager fragmentManager = getSupportFragmentManager();
fragmentManager.beginTransaction().replace(R.id.contentFrame,fragment).commit();
}
I use a switch case to determine the Fragment's class and send that to this method.
I could probably figure out a hacky-snappy way to fix this, but I'd like to fix this without too much hacky-snappy code.
I hope someone has an idea on how to fix this. Thanks in advance.
EDIT:
Here is my fragment class:
public class LGSFragment extends Fragment {
#BindView(R.id.rvLGS)
RecyclerView rvLGS;
private List<LGS> lgsList;
private LGSAdapter adapter;
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
//I debugged here and all fields were null at this point
View view = inflater.inflate(R.layout.fragment_lgs,container,false);
ButterKnife.bind(this, view);
lgsList = new ArrayList<>();
LinearLayoutManager manager = new LinearLayoutManager(getContext());
rvLGS.setLayoutManager(manager);
adapter = new LGSAdapter(lgsList);
rvLGS.setAdapter(adapter);
getDatabaseLGSs();
return view;
}
/**
* Method to load in the LGSs from the database
*/
private void getDatabaseLGSs() {
String collection = getString(R.string.db_lgs);
FireStoreUtils.getAllDocumentsConverted(collection, LGS.class, new OperationCompletedListener() {
#Override
public void onOperationComplete(Result result, Object... data) {
if (result == Result.SUCCESS) {
lgsList.clear();
List<LGS> newLGSs = (List<LGS>) data[0];
List<String> ids = (List<String>) data[1];
int i = 0;
for (LGS lgs : newLGSs) {
lgs.setId(ids.get(i));
lgsList.add(lgs);
i++;
}
adapter.notifyDataSetChanged();
}
}
});
}
#Override
public void onSaveInstanceState(#NonNull Bundle outState) {
super.onSaveInstanceState(outState);
}
}
onSaveInstanceState is not called because there is no reason to, when you navigate between fragments, the older fragment doesn't get destroyed till the OS need the space they use (low Memory).
First of all create a back stack to keep fragments or just call addtoBackStack at the end of fragmentTransaction and then move the list initiation and data request to onCreate so it only called when the fragment is created:
lgsList = new ArrayList<>();
getDatabaseLGSs();
and after that every time you get back to fragment the view is recreated with available data.
Update:
Instead of keeping an reference on your own, you can add the fragment to the backstack and then retrieve it using corresponding tag. This let's fragmentManager manages the caching by itself. And the second time you access a fragment, it doesn't gets recreated:
#Override
public void onNavigationDrawerItemSelected(#NonNull MenuItem item) {
if (item.isChecked())
return;
item.setChecked(true);
setTitle(item.getTitle());
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction transaction = fragmentManager.beginTransaction();
Fragment currentlyShown = fragmentManager.findFragmentByTag(currentlyShownTag);
Fragment dest;
switch (item.getItemId()){
case R.id.nav_lgs:
dest = fragmentManager.findFragmentByTag(LGSFragment.class.getName());
if (dest == null) {
Log.d("TRANSACTION", "instanciating new fragment");
dest = new LGSFragment();
currentlyShownTag = LGSFragment.class.getName();
transaction.add(R.id.contentFrame, dest, LGSFragment.class.getName());
}
break;
...
}
if(currentlyShown != null)
transaction.hide(currentlyShown);
transaction.show(dest);
transaction.commit();
drawerLayout.closeDrawers();
return true;
}
EDIT:
Although this solution works fine, this solution uses some bad practices, I recommend using the accepted solution instead.
I've solved the problem with the help of Keivan Esbati and denvercoder9 (Thanks for that!)
Since I only have 4 fragments I keep an instance of each of them in the MainActivity, I also have a variable to track the current Fragment. Everytime I open a fragment, I hide the current fragment using the FragmentManager and calling .hide() in the transaction. Then, if the Fragment is a new Fragment I call .add() in the transaction, otherwise I call .show in the transaction.
The code for the onNavigationItemSelected() method (which triggers when a user selects an item in the menu):
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
if (!item.isChecked()) {
item.setChecked(true);
setTitle(item.getTitle());
switch (item.getItemId()) {
case R.id.nav_lgs: {
if (lgsFragment == null) {
lgsFragment = new LGSFragment();
openFragment(lgsFragment, FragmentTag.LGS.toString());
} else {
openFragment(lgsFragment, "");
}
currentFragmentTag = FragmentTag.LGS;
break;
}
case R.id.nav_users: {
if (userFragment == null) {
userFragment = new UserFragment();
openFragment(userFragment, FragmentTag.USERS.toString());
} else {
openFragment(userFragment, "");
}
currentFragmentTag = FragmentTag.USERS;
break;
}
case R.id.nav_profile: {
if (profileFragment == null) {
profileFragment = new ProfileFragment();
openFragment(profileFragment, FragmentTag.PROFILE.toString());
} else {
openFragment(profileFragment, "");
}
currentFragmentTag = FragmentTag.PROFILE;
break;
}
case R.id.nav_my_lgs: {
if (myLGSFragment == null) {
myLGSFragment = new MyLGSFragment();
openFragment(myLGSFragment, FragmentTag.MY_LGS.toString());
} else {
openFragment(myLGSFragment, "");
}
currentFragmentTag = FragmentTag.MY_LGS;
break;
}
default: {
if (lgsFragment == null) {
lgsFragment = new LGSFragment();
openFragment(lgsFragment, FragmentTag.LGS.toString());
} else {
openFragment(lgsFragment, "");
}
currentFragmentTag = FragmentTag.LGS;
break;
}
}
}
drawerLayout.closeDrawers();
return true;
}
The openFragment() method used above:
private void openFragment(Fragment fragment, String tag) {
FragmentManager fragmentManager = getSupportFragmentManager();
if (currentFragmentTag != FragmentTag.NO_FRAGMENT) {
fragmentManager.beginTransaction().hide(fragmentManager.findFragmentByTag(currentFragmentTag.toString())).commit();
}
if (!tag.equals("")) {
fragmentManager.beginTransaction().add(R.id.contentFrame,fragment,tag).commit();
} else {
fragmentManager.beginTransaction().show(fragment).commit();
}
}
Set up in onCreate():
currentFragmentTag = FragmentTag.NO_FRAGMENT;
if (lgsFragment == null) {
lgsFragment = new LGSFragment();
openFragment(lgsFragment, FragmentTag.LGS.toString());
} else {
openFragment(lgsFragment, "");
}
currentFragmentTag = FragmentTag.LGS;

Fragment Crashing when receiving data from an Activity

Good day all,
I have an issue where my activity is making a network call and when the network call is completed, it makes some changes in the activity using the data from the JSON object received from the call, it then passes the object down to the fragments in the same activity. These fragments are in a TabLayout.
I had this same issue which I asked here at this SO Question That sorted it out but I seem to be having the same issue, even after it worked for a little bit after not changing anything significant. I was just adding more fields I wanted to change?
The issue I have is that if I put a System.out.println() it prints out the correct data. The minute I want to set say a TextView with the data I receive in the Fragment the app Crashes with Nullpointer. When I debug it with the Debug in Android studio, the TextView I'm setting is always null for some reason.
Activity Code that does the initial Network call:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_listings);
ButterKnife.bind(this);
setSupportActionBar(toolbar);
handleIntent(getIntent());
}
private void handleIntent(Intent aIntent) {
if (aIntent != null) {
String tradeType = aIntent.getStringExtra("itemType");
String tradeId = aIntent.getStringExtra("itemId");
presenter = new ItemPresenterImpl(this, ItemBuyNowActivity.this);
presenter.doListingServiceCall(tradeId); // <------- This is the where I send the Trade Id so I can do the network call.
} else {
System.out.println("Intent is null in " + ItemBuyNowActivity.class.getSimpleName());
}
}
Interface between Activity and Presenter:
public interface ItemPresenter {
void doListingServiceCall(String itemId); //<------- Comes to this Interface
void doToolbarBackgroundImageCall(TradeItem aTradeItem);
}
Class the implements the Presenter:
#Override
public void doListingServiceCall(String aItemId) { // <------- This is where the network call starts
String homeURL = BobeApplication.getInstance().getWsURL() + mContext.getString(R.string.ws_url_item) + aItemId;
BobeJSONRequest jsObjRequest = new BobeJSONRequest(Request.Method.GET, homeURL, null, this, this);
VolleySingleton.getInstance().addToRequestQueue(jsObjRequest, "ListingRequest");
}
#Override
public void doToolbarBackgroundImageCall(TradeItem aTradeItem) {
ImageRequest request = new ImageRequest(aTradeItem.getItem().getImageUrl(),
new Response.Listener<Bitmap>() {
#Override
public void onResponse(Bitmap bitmap) {
Drawable drawable = new BitmapDrawable(mContext.getResources(), bitmap);
mItemView.loadBackgroundImage(drawable);
}
}, 0, 0, null,
new Response.ErrorListener() {
public void onErrorResponse(VolleyError error) {
mItemView.displayErrorMessage(VolleyErrorHelper.getErrorType(error, mContext) + " occurred downloading background image");
}
});
VolleySingleton.getInstance().addToRequestQueue(request, "ListItemToolbarBackgroundImageRequest");
}
#Override
public void onResponse(Object response) {
Gson gson = new Gson();
TradeItem tradeItem = gson.fromJson(response.toString(), TradeItem.class);
mItemView.populateListViews(tradeItem); // <------- This is the where I send the Object so the views in the activity can be manipulated
doToolbarBackgroundImageCall(tradeItem);
}
Method in the Activity that handles
#Override
public void populateListViews(TradeItem aTradeItem) {
mOverviewPresenter = new OverviewPresenterImpl(new OverviewListItemFragment(), aTradeItem);
OverviewListItemFragment.setData(aTradeItem); //<------- This is the where I send the Object to the fragment so i can manipulate the views in the fragment
}
class TabAdapter extends FragmentPagerAdapter {
public TabAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
Fragment fragment = null;
if (position == 0) {
fragment = new OverviewListItemFragment();
}
if (position == 1) {
fragment = new DescriptionListItemFragment();
}
if (position == 2) {
fragment = new ShippingListItemFragment();
}
if (position == 3) {
fragment = new PaymentListItemFragment();
}
return fragment;
}
#Override
public int getCount() {
return 4;
}
#Override
public CharSequence getPageTitle(int position) {
if (position == 0) {
return "Overview";
}
if (position == 1) {
return "Description";
}
if (position == 2) {
return "Shipping";
}
if (position == 3) {
return "Payment";
}
return null;
}
}
The Fragment that receives the data:
public class OverviewListItemFragment extends Fragment implements OverviewView {
private static TextView mOverViewHeading;
public OverviewListItemFragment() {
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.overview_list_item_fragment, container, false);
mOverViewHeading = (TextView) view.findViewById(R.id.frag_overview_heading_textview);
return view;
}
#Override
public void populateOverviewViews(final TradeItem aTradeItem) {
System.out.println("Overview Trade Object title is:" + aTradeItem.getItem().getTradeTitle()); // <------- This is print statement works 100% but when I try setting mOverViewHeading to the text in aTradeItem.getItem().getTradeTitle() I get a Null pointer Exception.
}
public static void setData(TradeItem aTradeItem) {
System.out.println("Overview Trade Object title is:" + aTradeItem.getItem().getTradeTitle()); // <------- This is print statement works 100% but when I try setting mOverViewHeading to the text in aTradeItem.getItem().getTradeTitle() I get a Null pointer Exception.
mOverViewHeading.setText(aTradeItem.getItem().getTradeTitle());// <------- This is where it crashes and mOverViewHeading is still null at this point.
}
}
EDIT: Sorry I forgot the LogCat:
02-05 17:08:21.554 30512-30512/com.example.app E/AndroidRuntime: FATAL EXCEPTION: main
java.lang.NullPointerException
at com.example.app.ui.fragments.OverviewListItemFragment.setData(OverviewListItemFragment.java:46)
at com.example.app.ui.activities.ItemBuyNowActivity.populateListViews(ItemBuyNowActivity.java:95)
at com.example.app.listing.ItemPresenterImpl.onResponse(ItemPresenterImpl.java:62)
at com.android.volley.toolbox.JsonRequest.deliverResponse(JsonRequest.java:65)
at com.android.volley.ExecutorDelivery$ResponseDeliveryRunnable.run(ExecutorDelivery.java:99)
at android.os.Handler.handleCallback(Handler.java:725)
at android.os.Handler.dispatchMessage(Handler.java:92)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:5041)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:793)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:560)
at dalvik.system.NativeStart.main(Native Method)
My thinking is that the view I'm trying to set isn't "Active" (if thats the right word) at the time it receives the data, because when I run the debugger with a break point at the method that receives the data in the Fragment, the mOverViewHeading TextView id is null, even though I have the findViewById in the onCreate, also tried placing it in the onCreateView() but both times failed. I also tried placing the findViewById in the same method that gets called when the response is successful but before I try setting the setText() on the TextView.
Thank you
OverviewListItemFragment I assume this is not your added fragment instance, but the class.
I suggest the following changes: remove static from setData and your TextView, leave it, if you really know how it works. I don't think it is necessary or recommendable.
private OverviewListItemFragment mFrag; //declare globally
mFrag = new OverviewListItemFragment();
//if you do not want to add it now, ignore the following line
getSupportFragmentManager().beginTransaction().add(R.id.yourContainer, mFrag, "mFrag").commit();
now call mFrag.setData everytime you want to set your data. Check if your mFrag is null, then reinitialize, and maybe re-add, or whatever you want to do.
Edit: Now that I know that you use a ViewPager, I suggest the following:
Do the above. I don't think it is recommendable to have static methods in this Context. You get an error because you are trying to reach a TextView in your Fragment. This was initialized in a ViewPager/PagerAdapter, and the PagerAdapter holds the reference to the used instance of your fragment.
You can access your used fragment through
Fragment mFragment = pagerAdapter.getFragment(0); //frag at position 0
with some casting, you will be able to find your (now NOT static) method:
((OverviewListItemFragment)pagerAdapter.getFragment(0)).setData(YOUR_DATA);
Please add some try/catch. check if your fragment is null, because it is possible that your fragment is recycled in the FragmentPagerAdapter, because it reached the offset. Another way to achieve this, would be to store your required data, and update it everytime your fragment gets visible as described here.
Edit 2: Obviously, You'll need some changed in your Adapter:
I would recommend creating an array containing your fragment in the constructor:
//global in your adapter:
private Fragment[] fragments;
public CustomPagerAdapter(FragmentManager fm) {
super(fm);
fragments = new GameFragment[4];
fragments[0] = new MyFragment();
fragments[1] = new SecondFragment();
....
}
public Fragment getItem(int position) {
return fragments[position];
}
public Fragment getFragment(int position) {
return fragments[position];
}

Passing data between two Fragments in a VIewPager (Android) (NullPointerException)

So basically I have 2 Fragments - FragmentConverter and FragmentFavourites, and I have one MainActivity. I'm trying to pass 4 arrays from the first fragment to the second one using an Interface called Communicator. The specific snippets are show below:
public interface Communicator {
public void respond(String[] names, String[] codes, String[] symbols, int[] images);
}
This is a method inside FragmentFavourites:
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// TODO Auto-generated method stub
String[] checkedNames = new String[counter];
String[] checkedCodes = new String[counter];
String[] checkedSymbols = new String[counter];
int[] checkedImages = new int[counter];
comm = (Communicator) getActivity();
int index = 0;
if (item.getItemId() == R.id.action_save){
for (int i=0;i<checked.size();i++){
if (checked.get(i) == true){
checkedNames[index] = names[i];
checkedCodes[index] = codes[i];
checkedSymbols[index] = symbols[i];
checkedImages[index] = images[i];
index++;
}
}
comm.respond(checkedNames, checkedCodes, checkedSymbols, checkedImages);
}
return super.onOptionsItemSelected(item);
}
This is the implemented interface method inside MainActivity:
#Override
public void respond(String[] names, String[] codes, String[] symbols,
int[] images) {
// TODO Auto-generated method stub
FragmentConverter frag = (FragmentConverter) fragmentPagerAdapter.getItem(1);
frag.changeData(names, codes, symbols, images);
}
And this is a method that collects the data in FragmentConverter:
public void changeData(String[] names, String[] codes, String[] symbols, int[] images){
this.names = names;
this.codes = codes;
this.symbols = symbols;
this.images = images;
Log.d("TEST", symbols.length + names.length + codes.length + images.length + "");
tvOneRate.setText(names[1]);
}
Now the problem is that whenever I try to change a ui component inside FragmentConverter, I get a NullPointerException, though the Log.d statement returns the correct results.
EDIT1: getItem() method of FragmentPagerAdapter:
#Override
public Fragment getItem(int i) {
// TODO Auto-generated method stub
Fragment frag = null;
if (i == 0){
frag = new FragmentFavourites();
}
if (i == 1){
frag = new FragmentConverter();
}
return frag;
}
EDITED:
When you call fragmentPagerAdapter.getItem(1) you are getting a new instance of the fragment so you are referring to a different object. this is why the view is null and you get the NullPointerException. If you need an adapter for only 2 fragments, you can try with something like that:
public class YourPagerAdapter extends android.support.v4.app.FragmentPagerAdapter {
private FragmentFavourites mFragFavourites;
private FragmentConverter mFragConverter;
public YourPagerAdapter() {
// ... your code above
this.mFragFavourites = new FragmentFavourites();
this.mFragConverter = new FragmentConverter();
}
#Override
public Fragment getItem(int position) {
switch (position) {
case 0:
return mFragFavourites;
case 1:
return mFragConverter;
default:
return null;
}
}
}
As above carlo.marinangeli has suggested when you call fragmentPagerAdapter.getItem(1) you are getting a new instance of the fragment so you are referring to a different object
So to get same object from you adapter you need to store your object. you can try following method in your adapter -
public Fragment getFragmentAtPosition(int position) {
return registeredFragments.get(position);
}
where registeredFragments is -
private SparseArray<Fragment> registeredFragments = new SparseArray<Fragment>();
and fill this sparseArray in getItem method like below -
#Override
public Fragment getItem(int position) {
switch (position) {
case 0:
fragment = FragmentPost.newInstance(position);
registeredFragments.put(position, fragment);
return fragment;
}
return null;
}
By using fragmentPagerAdapter.getItem(pos) method I was referring to a new object every time the respond() method was called. I fixed it by using findFragmentByTag() method instead:
#Override
public void respond(String[] names, String[] codes, String[] symbols,
int[] images) {
FragmentManager manager = getSupportFragmentManager();
FragmentConverter frag = (FragmentConverter) manager.findFragmentByTag("android:switcher:" + pager.getId() + ":" + 1);
frag.changeData(names, codes, symbols, images);
}
you can get that error because you are assuming that you have got the FragmentConverter and the views associated to it.
Without a logcat it becomes a little bit difficult to help you but basically what I would like to do in a situation like this is to pass everything through the activity without letting know the existence of the other fragment to the fragments.
F1 modifies a state object into the activity
F2 has to register as a
listener to the activity (be aware that the fragment can be attached
and detached in the view pager)
The Activity as soon it receives an updated, looks for all the registered listeners and if there is someone it delivers the updated

Categories