The Android app I'm working on has a single MainActivity and each screen of the app is implemented as a Fragment. Each fragment is instantiated like this in the MainActivity as a private class variable:
public class MainActivity extends Activity implements MainStateListener {
private FragmentManager fm = getFragmentManager();
private BrowseFragment browseFragment = BrowseFragment.newInstance();
...
There is a single 'fragment frame' that loads each screen fragment. When switching screens in the app this code is called to load a fragment:
FragmentTransaction ft = fm.beginTransaction();
ft.replace(R.id.frag_frame, incoming);
ft.addToBackStack(null);
ft.commit();
fm.executePendingTransactions();
Each screen fragment has a listener that enables the fragment to call various methods in the MainActivity:
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
mainStateListener = (MainStateListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement MainStateListener");
}
}
The issue I am having is updating an aspect of a fragment from a Navigation Drawer that exits in the MainActivity. The navigation drawer has to update the fragment, and it uses this code to do that:
navigationDrawer.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
browseFragment.doSomethingOnBrowserFragment();
}
});
Things work fine when until you change the orientation. Then the current screen fragment loads fine (browseFragment). But then when you click the navigation drawer causing the doSomethingOnBrowserFragment() method to execute I get a null pointer exception due to the mainStateListener object itself (attached to in the browseFragment) being null. From what I know about the Fragment lifecycle this variable shouldn't be null because the onAttach() method executes first before anything and sets mainStateListener variable. Also if I have a button on that browserFragment that uses the mainStateListener object (following an orientation change), clicking the button never has this null pointer issue.
Stack trace:
08-04 16:23:28.937 14770-14770/co.openplanit.totago E/AndroidRuntime﹕ FATAL EXCEPTION: main
Process: co.openplanit.totago, PID: 14770
java.lang.NullPointerException
at co.openplanit.totago.MapFragment.enableOfflineMode(MapFragment.java:489)
at co.openplanit.totago.MainActivity.setMapMode(MainActivity.java:663)
at co.openplanit.totago.MainActivity.itineraryMapDrawerSelectItem(MainActivity.java:610)
at co.openplanit.totago.MainActivity.access$200(MainActivity.java:52)
at co.openplanit.totago.MainActivity$5.onItemClick(MainActivity.java:420)
at android.widget.AdapterView.performItemClick(AdapterView.java:299)
at android.widget.AbsListView.performItemClick(AbsListView.java:1158)
at android.widget.AbsListView$PerformClick.run(AbsListView.java:2957)
at android.widget.AbsListView$3.run(AbsListView.java:3850)
at android.os.Handler.handleCallback(Handler.java:733)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:136)
at android.app.ActivityThread.main(ActivityThread.java:5103)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:790)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:606)
at dalvik.system.NativeStart.main(Native Method)
It seems to me the issue may be that using the Navigation Drawer is actually interacting with the browseFragment lifecycle and causing it to detach or something.
Any suggestions on how to resolve this would be much appreciated.
Setting the navGraph attribute of a FragmentContainerView allows you to navigate between fragments within an activity. The NavGraph editor allows you to add navigation actions and specify arguments between different destinations.
These can be any support fragments you define within your application. Make sure that all the fragments extend from androidx.fragment.app.Fragment. In order to slide our navigation drawer over the ActionBar, we need to use the new Toolbar widget as defined in the AndroidX library.
1. Start a fresh android application project in Android Studio and select the Navigation Drawer Screen as default screen. 2. If you have already created the project then you can also add Navigation drawer activity in your existing project by opening Your Package Name -> New -> Activity -> Navigation Drawer Activity.
With the Navigation component, you can call the NavController 's navigate () method to swap the fragment that's displayed. The NavController also helps you handle common tasks like responding to the system "up" button to navigate back to the previously displayed fragment.
Keeping a reference to a Fragment might leave you out of sync with a reference to an old Fragment that is Detached in cases where the Fragment Manager recreates the Fragment for you.
The solution is to find the fragment currently in frag_frame, in pseudo code:
Fragment fragment = fm.findFragmentById(R.id.frag_frame);
if(fragment instanceof BrowseFragment) {
// do your stuff
}
Just replace onclick listener code with this one in mainactivity.
Error is occuring due to NullPointerException,
cause: Null pointer passing in replace 2nd column.
Solution : initiate fragment class (new fragment())
case R.id.home:
hfragment = new homefragment();
FragmentTransaction hfragmentTransaction= getSupportFragmentManager().beginTransaction();
hfragmentTransaction.replace(R.id.frame, hfragment);
hfragmentTransaction.commit();
//do ur task here or in fragment class
return true;
case R.id.notification:
return true;
default:
Toast.makeText(getApplicationContext(),"Somethings Wrong",Toast.LENGTH_SHORT).show();
return true;
I notice you're basically caching fragments due to code of:
public class MainActivity extends Activity implements MainStateListener {
private FragmentManager fm = getFragmentManager();
private BrowseFragment browseFragment = BrowseFragment.newInstance();
But implementing this may be tricky. Either you create code/class that manages these fragments like using Array of fragments, or use class like FragmentPagerAdapter
.
If I may suggest, don't cache fragments since you have to understand its lifecycle, caching is a good idea only if the fragment's layout is complicated. Simply just create a new instance of it in your code public void onItemClick()
like at Google's suggestion @ Creating a Navigation Drawer, in case you did not read it.
Code snippet in the webpage:
private class DrawerItemClickListener implements ListView.OnItemClickListener {
@Override
public void onItemClick(AdapterView parent, View view, int position, long id) {
selectItem(position);
}
}
private void selectItem(int position) {
// Create a new fragment and specify the planet to show based on position
Fragment fragment = new PlanetFragment();
Bundle args = new Bundle();
args.putInt(PlanetFragment.ARG_PLANET_NUMBER, position);
fragment.setArguments(args);
// Insert the fragment by replacing any existing fragment
FragmentManager fragmentManager = getFragmentManager();
fragmentManager.beginTransaction()
.replace(R.id.content_frame, fragment)
.commit();
Note: A new instance of fragment is done with new PlanetFragment()
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With