Reputation: 2289
I've been struggling with this error since the last week. Here it is:
public class MainActivity extends FragmentActivity implements ActionBar.TabListener {
AppSectionsPagerAdapter mAppSectionsPagerAdapter;
ViewPager mViewPager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mAppSectionsPagerAdapter = new AppSectionsPagerAdapter(getSupportFragmentManager(), getApplicationContext());
mViewPager = (ViewPager) findViewById(R.id.pager);
mViewPager.setAdapter(mAppSectionsPagerAdapter);
...
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle item selection
switch (item.getItemId()) {
case R.id.refresh:
mAppSectionsPagerAdapter.onRefresh();
return true;
...
}
public class AppSectionsPagerAdapter extends FragmentPagerAdapter {
private ArrayList<Fragment> fragmentList;
private MainSectionFragment f1, f2;
private SelectedSectionFragment f3;
public AppSectionsPagerAdapter(FragmentManager fm, Context c) {
f3 = new SelectedSectionFragment();
f2 = new MainSectionFragment();
f1 = new MainSectionFragment();
fragmentList = new ArrayList<Fragment>();
fragmentList.add(f1);
fragmentList.add(f2);
fragmentList.add(f3);
...
}
public void onRefresh(){
MainSectionFragment fragment = (MainSectionFragment)fragmentList.get(0);
fragment.fetchList();
}
...
}
public class MainSectionFragment extends Fragment implements CallbackListener {
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
//some initialization
c = getActivity().getApplicationContext(); // tested and it gets initialized
System.out.println(""+hashCode());
...
}
void fetchList() {
System.out.println(""+hashCode());
android.support.v4.app.FragmentManager manager = getActivity().getSupportFragmentManager();
...
}
}
I navigate from ActivityMain
and it dies. When I get back the list is scrollable vertically and horizontally. If I click refresh button NullPointerException
arises. I've debugged the program. OnCreateView()
is called when I return from SettingsActivity, but when I click refresh button, every field of MainSectionFragment
becomes null.
Printed hashcodes before exception:
05-25 01:54:37.190: I/System.out(5982): 1091848056 //onCreateView()
05-25 01:54:42.680: I/System.out(5982): 1091574888 //fetchList()
Can't understand why are these objects different.
Let me know if my question is not clear.
UPDATE 1
I have checked 'Don't keep activities' option in dev tools. I have verified that MainActivitys onCreate
is called after going back from SettingsActivity
UPDATE 2
Going to SettingsActivity detaches the fragments. Returning from SettingsActivity attaches the fragments again.
Upvotes: 0
Views: 1632
Reputation: 39836
edit:
Even though the original answer got pretty close to answering properly, I wanted to edit to bring some knowledge to the case.
The Fragment stack works (under a certain point of view) very similarly to the activity. As fragments are also automatically destroyed and re-created by the framework and have their state saved via the Bundles
that get passed around the callbacks (savedInstanceState).
So what's happening is that even though, the activity is being re-created and a new Adapter being called, when supplying Fragments to the ViewPager the adapter first calls findFragmentByTag
to the FragmentManager
so that the saved states can be re-created, and it only calls public Fragment getItem(int position)
if there's none.
So the fragments that you see on the screen are not the objects that were created during AppSectionsPagerAdapter
constructor. Those fragments were never used.
Remember that, even though it sounds odd at first sight, that is a very desirable feature. For example: imagine there's an EditText
on the fragment and the user filled it with some text. When the fragments gets destroyed, that value will be saved on the Bundle
, and when restored the value is still there.
original answer:
"Can't understand why are these objects different." because that's how the framework operates. Fragments are objects that can be destroyed/re-created by the Framework.
99% chances of what is happening is:
refresh
buttonThe most direct (and cleaner) way to fix your code will be to make the Fragment handle the menu, simply simple put this in the fragment code, and remove the menu stuff from the activity:
onCreate(Bundle savedInstance){
setHasOptionsMenu(true);
}
onOptionsItemSelected(MenuItem item){
if(item.getItemId() == R.id.refresh){
.. do your stuff here
return true;
} else return super.refresh(item);
}
If for some reason that you did not explain you really really must handle the menu in the activity there're other options:
For example: you could send a LocalBroadcast
from the activity and have the fragment register/unregister a BroadcastReceiver
during onResume/onPause
Or another nice trick: the FragmentPagerAdater
uses the following method to create fragment TAG for the FragmentManager
:
private static String makeFragmentName(int viewId, long id) {
return "android:switcher:" + viewId + ":" + id;
}
so then you can add this to your activity and call:
(MainSectionFragment)getSupportFragmentManager().
findFragmentByTag(
makeFragmentName(R.id.pager, mAppSectionsPagerAdapter.getItemId(0)))
.fetchList();
Upvotes: 3
Reputation: 54781
I think if you retain the instance it might solve the problem.
To do this in your oncreateview add this line:
setRetainInstance(true);
To understand this read up here Understanding Fragment's setRetainInstance(boolean)
Upvotes: 1