I have a problem with one thing - when I change orientation my second fragment, which is active at the moment, replaces by first fragment. I have never so such a behaviour, how can If fix it?
MainActivity:
public class MainActivity extends SherlockFragmentActivity implements onDialogClickListener, ITaskLoaderListener {
FragmentManager fm;
public ActionBar actionBar;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.pager_layout);
fm = getSupportFragmentManager();
actionBar = getSupportActionBar();
actionBar.setHomeButtonEnabled(false);
actionBar.setDisplayUseLogoEnabled(true);
actionBar.setDisplayShowTitleEnabled(false);
FragmentTransaction ft = fm.beginTransaction();
ft.add(android.R.id.content, new FirstActivity.FirstFragment(), "loan").commit();
}
}
FirstFragment:
public class FirstActivity extends SherlockFragmentActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG, "here");
final ActionBar actionBar = getSupportActionBar();
actionBar.setDisplayUseLogoEnabled(true);
actionBar.setDisplayShowTitleEnabled(false);
FragmentManager fm = getSupportFragmentManager();
if (fm.findFragmentById(android.R.id.content) == null) {
FirstFragment first = new FirstFragment();
fm.beginTransaction().add(android.R.id.content, first).commit();
}
}
public static class FirstFragment extends SherlockFragment implements OnClickListener, OnItemClickListener {
private static final String TAG = "LoanFragment";
private View rootView;
private Button bExtend;
private FragmentManager fm;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
if (session.hasLoan()) {
rootView = inflater.inflate(R.layout.fragment_loan, container, false);
bExtend = (Button) rootView.findViewById(R.id.b1);
return rootView;
}
}
#Override
public void onClick(View v) {
FragmentTransaction ft = fm.beginTransaction();
switch (v.getId()) {
case R.id.b1:
ft.add(android.R.id.content, new SecondActivity.SecondFragment(), "second").addToBackStack(null).commit();
break;
}
Second Fragment:
public class SecondActivity extends SherlockFragmentActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final ActionBar actionBar = getSupportActionBar();
actionBar.setDisplayUseLogoEnabled(true);
actionBar.setDisplayShowTitleEnabled(false);
FragmentManager fm = getSupportFragmentManager();
if (fm.findFragmentById(android.R.id.content) == null) {
SecondFragment second = new secondFragment();
fm.beginTransaction().add(android.R.id.content, second).commit();
}
}
public static class SecondFragment extends SherlockFragment {
private View rootView;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
rootView = inflater.inflate(R.layout.fragment_extend, container, false);
return rootView;
}
That`s to say when I am in second fragment and try to change orientation then my second fragment will replaced on first fragment. Why? How to fix it?
onCreate will be called on orientation change and you add the first fragment there. You can save which fragment you want to show in onSaveInstanceState and then use the instance state in onCreate to add the correct fragment.
EDIT:
You need to maintain a variable currentFragmentIndex and save it in onSaveInstanceState like so:
protected void onSaveInstanceState(Bundle bundle) {
super.onSaveInstanceState(bundle);
bundle.putInt("currentFragment", currentFragment);
}
Then retrieve it in onCreate and initialize the fragment accordingly:
public void onCreate(Bundle bundle) {
if (bundle!= null){
currentFragmentIndex= bundle.getInt("currentFragment");
} else {
currentFragmentIndex = 0;
}
switch(currentFragmentIndex) {
case 0:
// TODO: Add first fragment
break;
case 1:
// TODO: Add second fragment
break;
}
}
Don't forget to change currentFragmentIndex to 1 when you switch to the second fragment.
I ran into the same issue, though as a note, I'm using the v4 support library. After reading Sapan Diwakar's answer, I wondered if this was necessary; instead, I tried this...
if (null == mFragmentManager.findFragmentByTag(TAG_HERE)) {
mFragmentManager.beginTransaction()./*blah blah blah*/.commit();
}
...so the original fragment is only instantiated/attached if there's nothing already attached where it's supposed to go.
I've tested this a bit and it seems to be fine... of course, the fact that it works doesn't mean it's a good idea, but I'm too new at this to know why it wouldn't be. If anyone can chime in on that, it'd be useful.
Thanks for posting this question, and also to everyone with input!
Related
Hi I have an activity A with a button that sends to Activity B with ActionBar and up button enabled, this activity has tabs with fragments and a button that send to a new fragment C not related with the tabs. By clicking the back button, it is supposed to return to the tabs. The back button works and shows the tabs but not the contain. What I'm doing wrong? Here's some code.
Thanks in advance.
Activity B
public class BackActivity extends AppCompatActivity {
...
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_back);
// get fragment manager
myChildToolbar =(Toolbar)findViewById(R.id.my_child_toolbar2);
setSupportActionBar(myChildToolbar);
// Get a support ActionBar corresponding to this toolbar
ActionBar ab = getSupportActionBar();
// Enable the Up button
ab.setDisplayHomeAsUpEnabled(true);
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
myChildToolbar.setTitle(R.string.menu_ver_lista);
Bundle bundle=new Bundle();
bundle.putInt(NuevoinformeFragment.INFORMESEL,datosRecuperados.getInt(NuevoinformeFragment.INFORMESEL));
bundle.putString(TabsFragment.ARG_MUESTRA,"true");
TabsFragment detailFragment = new TabsFragment();
detailFragment.setArguments(bundle);
ft.add(R.id.back_fragment, detailFragment);
ft.commit();
}
#Override
public boolean onSupportNavigateUp() {
onBackPressed();
return false;
}
}
Tabs fragment
TabLayout tabs;
ViewPager viewPager;
private ListaDetalleViewModel mViewModel;
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container,
#Nullable Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.fragment_lista_tabs, container, false);
mViewModel = new ViewModelProvider(requireActivity()).get(ListaDetalleViewModel.class);
return root;
}
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
Bundle bundle = getArguments();
viewPager = view.findViewById(R.id.view_pager);
tabs = view.findViewById(R.id.tabs);
tabs.setupWithViewPager(viewPager);
mViewModel.cargarPestañas(ciudadNombre).observe(getViewLifecycleOwner(), words -> {
configureTabLayout();
});
}
private void configureTabLayout() {
SectionsPagerAdapter sectionsPagerAdapter = new SectionsPagerAdapter( getFragmentManager(),clientes, mViewModel,clientesplan);
viewPager.setAdapter(sectionsPagerAdapter);
viewPager.addOnPageChangeListener(new
TabLayout.TabLayoutOnPageChangeListener(tabs));
tabs.addOnTabSelectedListener(new
TabLayout.OnTabSelectedListener() {
#Override
public void onTabSelected(TabLayout.Tab tab) {
viewPager.setCurrentItem(tab.getPosition());
}
#Override
public void onTabUnselected(TabLayout.Tab tab) {
}
#Override
public void onTabReselected(TabLayout.Tab tab) {
}
});
}
}
You're using the wrong FragmentManager when creating your SectionsPagerAdapter, you should be using getChildFragmentManager() here:
SectionsPagerAdapter sectionsPagerAdapter = new SectionsPagerAdapter(
getFragmentManager(), // This is the problem
clientes, mViewModel,clientesplan
);
getFragmentManager() actually refers to the FragmentManager that your TabsFragment has been added to - that's why that method was deprecated and replaced with getParentFragmentManager(), which is more explicit on what that FragmentManager means.
Whenever the fragment you create is fully contained in the layout of another fragment, it needs to be a child fragment by being added to the getChildFragmentManager(). This ensures that it moves through lifecycle states properly (such as getting its Views recreated when its parent has its Views recreated) and to ensure that the state of the fragments are saved and restored properly.
how to Saving and Restoring Fragment state in Android ?
my code for save and restore state :
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt("CurrentState",CurrentState);
}
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
if (savedInstanceState != null) {
// Restore last state for checked position.
CurrentState = savedInstanceState.getInt("CurrentState", 0);
}
.
.
.
switch (CurrentState){
case 1 :button_DisplayMemoris.performClick();break;
case 2 :
linearLayout_AddMemoris.setVisibility(View.VISIBLE);
linearLayout_Memoris.setVisibility(View.GONE);
linearLayout_DisplayMemoris.setVisibility(View.GONE);
break;
default:break;
}
return inflate;
}
when CurrentState=2 , linearLayout_AddMemoris Not displayed
How do I fix it?
update :
this is my activity :
public class ToolsActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_tools);
displayView();
}
public void displayView() {
// update the main content by replacing fragments
Fragment fragment = = new MemoirsFragment();
FragmentManager fm = getFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
ft.replace(R.id.frame_container, fragment);
ft.commit();
}
}
Is there a way to solve the problem?
I have no idea
Add in onCreate method null checking, now you are replace restored fragment by new fragment
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_tools);
if (savedInstanceState == null)
displayView();
}
I have an Activity in which I go through several fragments. In every fragment I have several views (EditText, ListView, Map, etc).
How can I save the instance of the fragment that is shown at that moment? I need it to work when the activity is onPause() --> onResume(). Also I need it to work when I return from another fragment (pop from backstack).
From the main Activity I call the first fragment, then from the the fragment I call the next one.
Code for my Activity:
public class Activity_Main extends FragmentActivity{
public static Fragment_1 fragment_1;
public static Fragment_2 fragment_2;
public static Fragment_3 fragment_3;
public static FragmentManager fragmentManager;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
fragment_1 = new Fragment_1();
fragment_2 = new Fragment_2();
fragment_3 = new Fragment_3();
fragmentManager = getSupportFragmentManager();
FragmentTransaction transaction_1 = fragmentManager.beginTransaction();
transaction_1.replace(R.id.content_frame, fragment_1);
transaction_1.commit();
}}
Then here is the code for one of my fragments:
public class Fragment_1 extends Fragment {
private EditText title;
private Button go_next;
#Override
public View onCreateView(final LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_1,
container, false);
title = (EditText) rootView.findViewById(R.id.title);
go_next = (Button) rootView.findViewById(R.id.go_next);
image.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
FragmentTransaction transaction_2 = Activity_Main.fragmentManager
.beginTransaction();
transaction_2.replace(R.id.content_frame,
Activity_Main.fragment_2);
transaction_2.addToBackStack(null);
transaction_2.commit();
});
}}
I have searched a lot of information but nothing clear. Can somebody give a clear solution and an example, please ?
When a fragment is moved to the backstack, it isn't destroyed. All the instance variables remain there. So this is the place to save your data. In onActivityCreated you check the following conditions:
Is the bundle != null? If yes, that's where the data is saved (probably orientation change).
Is there data saved in instance variables? If yes, restore your state from them (or maybe do nothing, because everything is as it should be).
Otherwise your fragment is shown for the first time, create everything anew.
Edit: Here's an example
public class ExampleFragment extends Fragment {
private List<String> myData;
#Override
public void onSaveInstanceState(final Bundle outState) {
super.onSaveInstanceState(outState);
outState.putSerializable("list", (Serializable) myData);
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if (savedInstanceState != null) {
//probably orientation change
myData = (List<String>) savedInstanceState.getSerializable("list");
} else {
if (myData != null) {
//returning from backstack, data is fine, do nothing
} else {
//newly created, compute data
myData = computeData();
}
}
}
}
Android fragment has some advantages and some disadvantages.
The most disadvantage of the fragment is that when you want to use a fragment you create it ones.
When you use it, onCreateView of the fragment is called for each time. If you want to keep state of the components in the fragment you must save fragment state and yout must load its state in the next shown.
This make fragment view a bit slow and weird.
I have found a solution and I have used this solution: "Everything is great. Every body can try".
When first time onCreateView is being run, create view as a global variable. When second time you call this fragment onCreateView is called again you can return this global view. The fragment component state will be kept.
View view;
#Override
public View onCreateView(LayoutInflater inflater,
#Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
setActionBar(null);
if (view != null) {
if ((ViewGroup)view.getParent() != null)
((ViewGroup)view.getParent()).removeView(view);
return view;
}
view = inflater.inflate(R.layout.mylayout, container, false);
}
Try this :
#Override
protected void onPause() {
super.onPause();
if (getSupportFragmentManager().findFragmentByTag("MyFragment") != null)
getSupportFragmentManager().findFragmentByTag("MyFragment").setRetainInstance(true);
}
#Override
protected void onResume() {
super.onResume();
if (getSupportFragmentManager().findFragmentByTag("MyFragment") != null)
getSupportFragmentManager().findFragmentByTag("MyFragment").getRetainInstance();
}
Hope this will help.
Also you can write this to activity tag in menifest file :
android:configChanges="orientation|screenSize"
Good luck !!!
In order to save the Fragment state you need to implement onSaveInstanceState():
"Also like an activity, you can retain the state of a fragment using a Bundle, in case the activity's process is killed and you need to restore the fragment state when the activity is recreated. You can save the state during the fragment's onSaveInstanceState() callback and restore it during either onCreate(), onCreateView(), or onActivityCreated(). For more information about saving state, see the Activities document."
http://developer.android.com/guide/components/fragments.html#Lifecycle
As stated here: Why use Fragment#setRetainInstance(boolean)?
you can also use fragments method setRetainInstance(true) like this:
public class MyFragment extends Fragment {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// keep the fragment and all its data across screen rotation
setRetainInstance(true);
}
}
You can get current Fragment from fragmentManager. And if there are non of them in fragment manager you can create Fragment_1
public class MainActivity extends FragmentActivity {
public static Fragment_1 fragment_1;
public static Fragment_2 fragment_2;
public static Fragment_3 fragment_3;
public static FragmentManager fragmentManager;
#Override
protected void onCreate(Bundle arg0) {
super.onCreate(arg0);
setContentView(R.layout.main);
fragment_1 = (Fragment_1) fragmentManager.findFragmentByTag("fragment1");
fragment_2 =(Fragment_2) fragmentManager.findFragmentByTag("fragment2");
fragment_3 = (Fragment_3) fragmentManager.findFragmentByTag("fragment3");
if(fragment_1==null && fragment_2==null && fragment_3==null){
fragment_1 = new Fragment_1();
fragmentManager.beginTransaction().replace(R.id.content_frame, fragment_1, "fragment1").commit();
}
}
}
also you can use setRetainInstance to true what it will do it ignore onDestroy() method in fragment and your application going to back ground and os kill your application to allocate more memory you will need to save all data you need in onSaveInstanceState bundle
public class Fragment_1 extends Fragment {
private EditText title;
private Button go_next;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true); //Will ignore onDestroy Method (Nested Fragments no need this if parent have it)
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
onRestoreInstanceStae(savedInstanceState);
return super.onCreateView(inflater, container, savedInstanceState);
}
//Here you can restore saved data in onSaveInstanceState Bundle
private void onRestoreInstanceState(Bundle savedInstanceState){
if(savedInstanceState!=null){
String SomeText = savedInstanceState.getString("title");
}
}
//Here you Save your data
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString("title", "Some Text");
}
}
I'm not quite sure if this question is still bothering you, since it has been several months. But I would like to share how I dealt with this.
Here is the source code:
int FLAG = 0;
private View rootView;
private LinearLayout parentView;
/**
* The fragment argument representing the section number for this fragment.
*/
private static final String ARG_SECTION_NUMBER = "section_number";
/**
* Returns a new instance of this fragment for the given section number.
*/
public static Fragment2 newInstance(Bundle bundle) {
Fragment2 fragment = new Fragment2();
Bundle args = bundle;
fragment.setArguments(args);
return fragment;
}
public Fragment2() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
Log.e("onCreateView","onCreateView");
if(FLAG!=12321){
rootView = inflater.inflate(R.layout.fragment_create_new_album, container, false);
changeFLAG(12321);
}
parentView=new LinearLayout(getActivity());
parentView.addView(rootView);
return parentView;
}
/* (non-Javadoc)
* #see android.support.v4.app.Fragment#onDestroy()
*/
#Override
public void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();
Log.e("onDestroy","onDestroy");
}
/* (non-Javadoc)
* #see android.support.v4.app.Fragment#onStart()
*/
#Override
public void onStart() {
// TODO Auto-generated method stub
super.onStart();
Log.e("onstart","onstart");
}
/* (non-Javadoc)
* #see android.support.v4.app.Fragment#onStop()
*/
#Override
public void onStop() {
// TODO Auto-generated method stub
super.onStop();
if(false){
Bundle savedInstance=getArguments();
LinearLayout viewParent;
viewParent= (LinearLayout) rootView.getParent();
viewParent.removeView(rootView);
}
parentView.removeView(rootView);
Log.e("onStop","onstop");
}
#Override
public void onPause() {
super.onPause();
Log.e("onpause","onpause");
}
#Override
public void onResume() {
super.onResume();
Log.e("onResume","onResume");
}
And here is the MainActivity:
/**
* Fragment managing the behaviors, interactions and presentation of the
* navigation drawer.
*/
private NavigationDrawerFragment mNavigationDrawerFragment;
/**
* Used to store the last screen title. For use in
* {#link #restoreActionBar()}.
*/
public static boolean fragment2InstanceExists=false;
public static Fragment2 fragment2=null;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
setContentView(R.layout.activity_main);
mNavigationDrawerFragment = (NavigationDrawerFragment) getSupportFragmentManager()
.findFragmentById(R.id.navigation_drawer);
mTitle = getTitle();
// Set up the drawer.
mNavigationDrawerFragment.setUp(R.id.navigation_drawer,
(DrawerLayout) findViewById(R.id.drawer_layout));
}
#Override
public void onNavigationDrawerItemSelected(int position) {
// update the main content by replacing fragments
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction=fragmentManager.beginTransaction();
switch(position){
case 0:
fragmentTransaction.addToBackStack(null);
fragmentTransaction.replace(R.id.container, Fragment1.newInstance(position+1)).commit();
break;
case 1:
Bundle bundle=new Bundle();
bundle.putInt("source_of_create",CommonMethods.CREATE_FROM_ACTIVITY);
if(!fragment2InstanceExists){
fragment2=Fragment2.newInstance(bundle);
fragment2InstanceExists=true;
}
fragmentTransaction.addToBackStack(null);
fragmentTransaction.replace(R.id.container, fragment2).commit();
break;
case 2:
fragmentTransaction.addToBackStack(null);
fragmentTransaction.replace(R.id.container, FolderExplorerFragment.newInstance(position+1)).commit();
break;
default:
break;
}
}
The parentView is the keypoint.
Normally, when onCreateView, we just use return rootView. But now, I add rootView to parentView, and then return parentView. To prevent "The specified child already has a parent. You must call removeView() on the ..." error, we need to call parentView.removeView(rootView), or the method I supplied is useless.
I also would like to share how I found it. Firstly, I set up a boolean to indicate if the instance exists. When the instance exists, the rootView will not be inflated again. But then, logcat gave the child already has a parent thing, so I decided to use another parent as a intermediate Parent View. That's how it works.
Hope it's helpful to you.
If you using bottombar and insted of viewpager you want to set custom fragment replacement logic with retrieve previously save state you can do using below code
String current_frag_tag = null;
String prev_frag_tag = null;
#Override
public void onTabSelected(TabLayout.Tab tab) {
switch (tab.getPosition()) {
case 0:
replaceFragment(new Fragment1(), "Fragment1");
break;
case 1:
replaceFragment(new Fragment2(), "Fragment2");
break;
case 2:
replaceFragment(new Fragment3(), "Fragment3");
break;
case 3:
replaceFragment(new Fragment4(), "Fragment4");
break;
default:
replaceFragment(new Fragment1(), "Fragment1");
break;
}
public void replaceFragment(Fragment fragment, String tag) {
if (current_frag_tag != null) {
prev_frag_tag = current_frag_tag;
}
current_frag_tag = tag;
FragmentManager manager = null;
try {
manager = requireActivity().getSupportFragmentManager();
FragmentTransaction ft = manager.beginTransaction();
if (manager.findFragmentByTag(current_frag_tag) == null) { // No fragment in backStack with same tag..
ft.add(R.id.viewpagerLayout, fragment, current_frag_tag);
if (prev_frag_tag != null) {
try {
ft.hide(Objects.requireNonNull(manager.findFragmentByTag(prev_frag_tag)));
} catch (NullPointerException e) {
e.printStackTrace();
}
}
// ft.show(manager.findFragmentByTag(current_frag_tag));
ft.addToBackStack(current_frag_tag);
ft.commit();
} else {
try {
ft.hide(Objects.requireNonNull(manager.findFragmentByTag(prev_frag_tag)))
.show(Objects.requireNonNull(manager.findFragmentByTag(current_frag_tag))).commit();
} catch (NullPointerException e) {
e.printStackTrace();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
Inside Child Fragments you can access fragment is visible or not using below method
note: you have to implement below method in child fragment
#Override
public void onHiddenChanged(boolean hidden) {
super.onHiddenChanged(hidden);
try {
if(hidden){
adapter.getFragment(mainVideoBinding.viewPagerVideoMain.getCurrentItem()).onPause();
}else{
adapter.getFragment(mainVideoBinding.viewPagerVideoMain.getCurrentItem()).onResume();
}
}catch (Exception e){
}
}
I am using the following code to populate my UI with 2 fragments, the containers are FrameLayout's defined in XML. This first time this code is called i.e. when the app starts, it works fine, and both my fragments are displayed as expected. However after a configuration change(specifically, orientation), only the first fragment in the transaction is shown.
I don't think it's an issue with the fragments themselves, because if I reverse the code so one replace is called before the other or vice versa, that fragment will be displayed. So for example with the snippet from below as a guide, if I swap the mSummary and mDetails replace calls, then mDetails will be displayed and mSummary won't.
It's always the second one in the block that is missing.
// Using tablet layout
} else {
FragmentManager fm = super.getFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
ft.replace(R.id.summary_container, mSummaryFragment);
ft.replace(R.id.details_container, mDetailsFragment);
ft.commit();
}
I'm saving the fragments in onSaveInstanceState and restoring them from the Bundle savedInstanceState when the activity is recreated. I also tried breaking the transaction into two pieces by calling commit() and then getting another FragmentTransaction object but no joy there.
So for anyone coming across this at a later stage...
I finally manage to fix this by creating a new instance of the fragment and restoring it's state using a Fragment.SavedState object. So:
if (mSummaryFragment.isAdded() && mDetailsFragment.isAdded()) {
Fragment.SavedState sumState = getSupportFragmentManager().saveFragmentInstanceState(mSummaryFragment);
Fragment.SavedState detState = getSupportFragmentManager().saveFragmentInstanceState(mDetailsFragment);
mSummaryFragment = new SummaryFragment();
mSummaryFragment.setInitialSavedState(sumState);
mDetailsFragment = new DetailsFragment();
mDetailsFragment.setInitialSavedState(detState);
}
FragmentTransaction ft = mFragmentManager.beginTransaction();
ft.add(R.id.summary_container, mSummaryFragment);
ft.add(R.id.details_container, mDetailsFragment);
ft.commit();
I do not understand why this works and the old method doesn't, however this may be helpful for someone else.
this should work and orientation change will not affect the fragment., if you face any problem just let me know.
public class MainActivity extends FragmentActivity {
Fragment fragment = new Fragment1();
Fragment fragment2=new Fragment2();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
FragmentManager fm = super.getSupportFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
ft.replace(R.id.frame1, fragment);
ft.replace(R.id.frame2, fragment2);
ft.commit();
}
public void onSaveInstanceState(Bundle outState){
getSupportFragmentManager().putFragment(outState,"fragment1",fragment);
getSupportFragmentManager().putFragment(outState,"fragment2",fragment2);
}
public void onRetoreInstanceState(Bundle inState){
fragment = getSupportFragmentManager().getFragment(inState,"fragment1");
fragment2 = getSupportFragmentManager().getFragment(inState,"fragment2");
}
class Fragment1 extends Fragment{
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
}
ListView listView;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view=inflater.inflate(R.layout.summary_view,container,false);
return view;
}
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
}
}
class Fragment2 extends Fragment{
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
}
ListView listView;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view=inflater.inflate(R.layout.detail_view,container,false);
return view;
}
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
}
}
}
I'm having a problem with dynamic fragment . If I'm not change orientation , it work fine . When I change orientation , I click on ListView item . It's not change textview .
This is DynamicActivity class
public class DynamicActivity extends Activity implements FragmentCoordinator{
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_dynamic);
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction transaction = fragmentManager.beginTransaction();
ListContentFragment listContentFragment = new ListContentFragment();
DetailsContentFragment detailsContentFragment = new DetailsContentFragment();
transaction.add(R.id.listContainer, listContentFragment,"listContent");
transaction.add(R.id.detailsContainer, detailsContentFragment,"detailsContent");
transaction.commit();
}
#Override
public void onSetContentDetails(int index) {
FragmentManager fragmentManager = getFragmentManager();
DetailsContentFragment detailsContentFragment = (DetailsContentFragment)fragmentManager.findFragmentByTag("detailsContent");
detailsContentFragment.setContentDetails(index);
}
}
And DetailsContentFragment class
public class DetailsContentFragment extends Fragment {
TextView lbMess;
String[] array;
int saveIndex;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.details_content_fragment, container,false);
array = getResources().getStringArray(R.array.list_details);
lbMess = (TextView)v.findViewById(R.id.lbDetails);
int currentIndex = savedInstanceState == null ? 0 : savedInstanceState.getInt("indexContent",0);
setContentDetails(currentIndex);
return v;
}
#Override
public void onSaveInstanceState(Bundle outState) {
outState.putInt("indexContent", saveIndex);
}
public void setContentDetails(int index) {
saveIndex = index;
lbMess.setText(array[saveIndex]);
}
}
I have debug but it doesn't have any error . Please give me some advice
I found the problems are :
When the system destroys and re-creates an activity because of a run-time configuration change, the activity automatically
re-instantiates existing fragments.
This isn’t a problem for “static” fragments declared in the activity’s layout.
But for “dynamic” fragments, i need to test for this situation to prevent creating a second instance of my fragment.
I check the Bundle argument passed to my activity’s onCreate() is null.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_dynamic);
if(savedInstanceState == null)
{
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction transaction = fragmentManager.beginTransaction();
ListContentFragment listContentFragment = new ListContentFragment();
DetailsContentFragment detailsContentFragment = new DetailsContentFragment();
transaction.add(R.id.listContainer, listContentFragment,"listContent");
transaction.add(R.id.detailsContainer, detailsContentFragment,"detailsContent");
transaction.commit();
}
}
And it work fine . I think is helpful for someone have same problems .