Android oncreateview called twice - android

I have see this question asked here more than one time but I can't figure it out how to solve for my case.
I have an app that the user does this:
1 - Open the navigationDrawer and selects one option (a fragment is created) (I'm here selecting the second option);
public void selectItem(int position) {
Fragment fragment = null;
switch (position) {
case FRAGMENT_OPTION1:
...
break;
case FRAGMENT_OPTION2:
fragment = ControlPanelFragment.newInstance();
break;
...
case FRAGMENT_OPTIONN:
...
return;
default:
break;
}
if (fragment != null) {
FragmentManager fragmentManager = getSupportFragmentManager();
fragmentManager.beginTransaction().replace(R.id.fragment_container, fragment).commitAllowingStateLoss();
}
}
2 - The selected option (ControlPanelFragment) gets loaded:
2.1 - Control panel has tabs and an iconpager. For each pager page and for each tab a new fragment is created. I have 3 tabs and 3 pages so 9 fragments are created;
#Override
public void onViewCreated(View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
if (savedInstanceState != null) {
currentControlPanelOption = savedInstanceState.getInt("currentControlPanelOption", currentControlPanelOption);
currentControlPanelTab = savedInstanceState.getInt("currentControlPanelTab", currentControlPanelTab);
}
setControlPanelTabs();
setIconPager();
}
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt("currentControlPanelOption", pager.getCurrentItem());
outState.putInt("currentControlPanelTab", mTabHost.getCurrentTab());
}
3 - In the setIconPager(); I have this code:
pager = (ViewPager) view.findViewById(R.id.pager);
cPanelPagerAdapter = new ControlPanelPagerAdapter(getChildFragmentManager());
pager.setOffscreenPageLimit(2);
pager.setAdapter(cPanelPagerAdapter);
where ControlPanelPagerAdapter has this code:
public Fragment getItem(int index) {
Fragment fragment;
switch (index) {
case 1:
fragment = FragmentA.newInstance();
break;
case 2:
fragment = FragmentB.newInstance();
break;
case 3:
fragment = FragmentC.newInstance();
break;
default:
fragment = null;
break;
}
...
return fragment;
}
4 - FragmentA, FragmentB and FragmentC have almost the same code:
public static FragmentA newInstance() {
return new FragmentA();
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_placeholder, container, false);
return view;
}
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
if (savedInstanceState == null) {
fragmentA_Data = new FragmentADetail[3];
createTabInstance(0);
} else {
fragmentA_Data = (FragmentADetail[]) savedInstanceState.getSerializable("Data");
return;
}
}
private void createTabInstance(int tab) {
new FragmentADetail();
fragment = FragmentADetail.newInstance(tab);
Bundle args = new Bundle();
args.putInt("tab", tab);
fragment.setArguments(args);
fragmentA_Data[tab] = fragment;
FragmentTransaction fragmentTransaction = getChildFragmentManager().beginTransaction();
fragmentTransaction.replace(R.id.fragment_placeholder, fragmentA_Data[tab]);
fragmentTransaction.commitAllowingStateLoss();
}
public void getTabData(int tab) {
if (fragmentA_Data[tab] == null) {
createStoreTimePeriodInstance(tab);
} else {
if (fragmentA_Data[tab].getArguments() == null) {
Bundle args = new Bundle();
args.putInt("tab", tab);
fragmentA_Data[tab].setArguments(args);
}
FragmentTransaction fragmentTransaction = getChildFragmentManager().beginTransaction();
fragmentTransaction.replace(R.id.fragment_placeholder, fragmentA_Data[tab]);
fragmentTransaction.commitAllowingStateLoss();
}
}
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putSerializable("data", fragmentA_Data);
}
5 - Finally, FragmentADetail has this code:
public static FragmentADetail newInstance(int tab) {
selectedTab = tab;
return new FragmentADetail();
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
view = inflater.inflate(R.layout.details_fragment, container, false);
...
if (savedInstanceState != null) {
selectedTab = savedInstanceState.getInt("selectedTab");
}
...
}
public void getTabData(int tab) {
//This is where I'm getting the data that populates the layout
}
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt("selectedTab", selectedTab);
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
Bundle args = getArguments();
if (args != null) {
getTabData(args.getInt("tab"));
}
}
Now, imagine I'm on FragmentA with the third tab selected. If I rotate the screen I have this sequence of events:
ControlPanelFragment onSaveInstanceState saves the current tab and current fragment
FragmentA onSaveInstanceState saves the tabs fragments for the pager
navigationDrawer second option gets again called fragment = ControlPanelFragment.newInstance();
ControlPanelFragment onViewCreated is called and I can get the saved data information and a new pager and tabs are created
FragmentA onViewCreated is called and I can extract the saved data
FragmentADetail onActivityCreated gets the saved data and loads the data correctelly (at least I think)
And from now a second set of methods is called the second time and the data that was previously saved is reseted and so it now displays wrong data
ControlPanelFragment onSaveInstanceState but now the savedInstanceState is null
ControlPanelPagerAdapter getItem is called instantiating the 3 fragments
FragmentA onSaveInstanceState its now called but savedInstanceState is null
FragmentADetail onActivityCreated is called but now the tab = 0
Can someone explain-me how can I stop steps 7 to 10 from happening?

I figure out what was my problem.
When I was doing:
case FRAGMENT_OPTION2:
fragment = ControlPanelFragment.newInstance();
break;
I was creating a fragment and when I rotated the screen selectItem(int position) was again called so a new instance of the same object was created thus the steps 7 and following. The solution was to check if the fragment was already created and use him instead of creating a new one.
I've saved the initial fragment with a tag and them looked for that tag. If the tag existed, use that fragment otherwise create a new one.
public void selectItem(int position) {
Fragment fragment = null;
switch (position) {
case FRAGMENT_OPTION1:
...
break;
case FRAGMENT_OPTION2:
fragment = getSupportFragmentManager().findFragmentByTag(String.valueOf(position));
if (fragment == null) {
fragment = ControlPanelFragment.newInstance();
}
break;
...
case FRAGMENT_OPTIONN:
...
return;
default:
break;
}
if (fragment != null) {
FragmentManager fragmentManager = getSupportFragmentManager();
fragmentManager.beginTransaction().replace(R.id.fragment_container, fragment,
String.valueOf(position)).commitAllowingStateLoss();
}
}

Related

Saving and Restoring Fragment state in Android

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

Fragment is re-created after back from Activity

I have a problem related to fragment life cycle.
Before doing this, I will set DONT KEEP ACTIVITY mode(Setting -> Developer options).
In my project have 2 activity:
Activity 1: keep and control Fragment A and Fragment B
Activity 2: do not have fragment.
Activity 1 will be called first, then Fragment A and Fragment B will be called to visible.
Start Activity 2 from Activity 1, this cause Activity 1 will be destroyed and Fragment A & Fragment B will be destroyed too (Because of dont keep activity mode).
Press back key from Activity 2 to back Activity 1
Problem occurs here: Fragment A and Fragment B will be automatically called onCreateView() after back from activity 2 -> I want to avoid this. Can you give me some tips to resolve it?
Update Code
Activity 1
public class MainActivity extends Activity implements OnClickListener {
private Button mBtnShowFragmentA;
private Button mBtnShowFragmentB;
private Button mBtnGoAcitivity2;
protected String mCurrentFragmentTag;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.layout_activity_1);
mBtnShowFragmentA = (Button) findViewById(R.id.btn_show_fragmentA);
mBtnShowFragmentB = (Button) findViewById(R.id.btn_show_fragmentB);
mBtnGoAcitivity2 = (Button) findViewById(R.id.btn_go_activity2);
mBtnShowFragmentA.setOnClickListener(this);
mBtnShowFragmentB.setOnClickListener(this);
mBtnGoAcitivity2.setOnClickListener(this);
}
#Override
protected void onDestroy() {
super.onDestroy();
}
protected void addFragment(int contentId, Fragment fragment, boolean isAddStack,
String stackName) {
FragmentManager fm = getFragmentManager();
String newFragment = fragment.getClass().getName();
FragmentTransaction ft = fm.beginTransaction();
Fragment currentFragment = fm.findFragmentByTag(mCurrentFragmentTag);
if (currentFragment != null && !TextUtils.equals(currentFragment.getTag(), newFragment)) {
ft.hide(currentFragment);
}
if (fm.findFragmentByTag(newFragment) != null) {
fragment = (Fragment) fm.findFragmentByTag(newFragment);
}
if (!fragment.isAdded()) {
ft.add(contentId, fragment, newFragment);
} else {
ft.show(fragment);
}
if (isAddStack) {
ft.addToBackStack(stackName);
}
try {
ft.commitAllowingStateLoss();
} catch (Exception e) {
}
mCurrentFragmentTag = newFragment;
}
#Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_show_fragmentA:
FragmentA fragmentA = new FragmentA();
addFragment(R.id.activity_main_content, fragmentA, false, null);
break;
case R.id.btn_show_fragmentB:
FragmentB fragmentB = new FragmentB();
addFragment(R.id.activity_main_content, fragmentB, false, null);
break;
case R.id.btn_go_activity2:
Intent intent = new Intent(getApplicationContext(), SettingActivity.class);
startActivity(intent);
break;
default:
break;
}
}
}
Fragment A
public class FragmentA extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.layout_fragment_a, container, false);
return view;
}
}
Fragment B
public class FragmentB extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.layout_fragment_b, container, false);
return view;
}
}
Activity 2
public class SettingActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.layout_activity_2);
}
#Override
protected void onDestroy() {
super.onDestroy();
}
#Override
public void onBackPressed() {
super.onBackPressed();
}
}
...................
Use below code to remove activity from stack.
FragmentManager fragmentManager = getSupportFragmentManager();
if (fragmentManager .getBackStackEntryCount() > 0) {
fragmentManager .popBackStack();
}

Trying to make a flexible UI

I'am trying to make a app with a flexible UI.
I already implemented for handset devices (I have one activity and multiple fragments), and what I done was: The main fragment is a dashboard, and when I click in one button of it, he dashboard is replaced by a new fragment ( the clicked feature). Here is the code:
Dashboard fragment:
public class DashboardFragment extends Fragment {
GridView gridView;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// TODO Auto-generated method stub
return inflater.inflate(R.layout.activity_dashboard, container, false);
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onActivityCreated(savedInstanceState);
gridView=(GridView)getView().findViewById(R.id.dashboard_grid);
gridView.setAdapter(new ImageAdapter(getActivity()));
gridView.setOnItemClickListener(new android.widget.AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
FragmentTransaction transaction = getFragmentManager().beginTransaction();
Fragment fragment = null ;
switch (position) {
case 0:
fragment = new TestFragment();
break;
case 1 :
fragment = new TestFragment();
break;
case 2 :
fragment = new TestFragment();
break;
case 3 :
fragment = new TestFragment();
break;
case 4 :
fragment = new TestFragment();
break;
}
transaction.replace(R.id.container, fragment);
transaction.addToBackStack(null);
transaction.commit();
}
});
}
}
and my Main Activity:
public class MainActivity extends FragmentActivity{
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (findViewById(R.id.container) != null) {
if (savedInstanceState != null) {
return;
}
// Create an instance of ExampleFragment
DashboardFragment firstFragment = new DashboardFragment();
firstFragment.setArguments(getIntent().getExtras());
getSupportFragmentManager().beginTransaction().add(R.id.container, firstFragment).commit();
}
}
}
Now, what I want is to adapt this code and use a layout for tablets, with the dashboard on the left and the choosen fragment on the right, like this:
What could I do? I already tried to adapt this example, but I can't because they only update the fragment, they don't replace it.
Check this great article about multi-pane development.
It also includes an example (Sections 10 and 11)
Basically you can check whether there is a fragment element for your "Fragment B" in the current layout. If yes, you just update its content, if no, then start an activity which has it in its layout, or replace one in the current layout.
DetailFragment fragment = (DetailFragment) getFragmentManager().findFragmentById(R.id.detail_frag);
if (fragment==null || ! fragment.isInLayout()) {
// start new Activity or replace
}
else {
fragment.update(...);
}

Fragment onCreateView and onActivityCreated not being called on rotation

I am working with fragments and pushing new fragments on the backstack but when I rotate the device twice the fragment's onCreateView, onActivityCreated, and so on in the fragment life cycle methods are never called leaving a blank screen. This only occurs when a fragment has been added to the backstack or returning to the first fragment in the backstack.
Here is my activity's fragment handling methods:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Injection
MormonChannel.injectActivity(this);
setDrawerIndicatorEnabled(true);
// Do not set currentNavigationItem here.
NavigationItemSelectedEvent.NavigationItem navItem = null;
Intent intent = getIntent();
if (intent != null) {
navItem = (NavigationItemSelectedEvent.NavigationItem)
intent.getSerializableExtra(EXTRA_NAV_ITEM);
}
if (savedInstanceState == null) {
FragmentManager fm = getSupportFragmentManager();
fm.beginTransaction().replace(R.id.container, new FeatureListFragment()).commit();
if (navItem != null) {
onNavigationItemSelected(new NavigationItemSelectedEvent(navItem));
} else {
currentNavigationItem = NavigationItemSelectedEvent.NavigationItem.FEATURES;
}
}
}
#Subscribe
public void onNavigationItemSelected(NavigationItemSelectedEvent event) {
if (currentNavigationItem == event.getNavigationItem()) {
return;
}
FragmentManager fragmentManager = getSupportFragmentManager();
fragmentManager.popBackStack();
NavigationItemSelectedEvent.NavigationItem navigationItem = event.getNavigationItem();
String name = navigationItem.getName();
switch (navigationItem) {
default:
case FEATURES:
// Nothing needs to be done it is already there.
break;
case AUDIO:
fragmentManager.beginTransaction().replace(R.id.container,
CollectionListFragment.newInstance(prefs.getLanguageId(), prefs.getAudioCollectionId()))
.addToBackStack
(name).commit();
break;
case VIDEO:
fragmentManager.beginTransaction().replace(R.id.container,
CollectionListFragment.newInstance(prefs.getLanguageId(), prefs.getVideoCollectionId()))
.addToBackStack(name).commit();
break;
case RADIO:
fragmentManager.beginTransaction().replace(R.id.container,
CollectionListFragment.newInstance(prefs.getLanguageId(), prefs.getRadioCollectionId()))
.addToBackStack(name).commit();
break;
case HISTORY:
fragmentManager.beginTransaction().replace(R.id.container, new HistoryFragment()).addToBackStack(name).commit();
break;
case DOWNLOADS:
fragmentManager.beginTransaction().replace(R.id.container, new DownloadsFragment()).addToBackStack(name).commit();
break;
case PLAYLISTS:
fragmentManager.beginTransaction().replace(R.id.container, new PlaylistFragment()).addToBackStack(name).commit();
break;
}
currentNavigationItem = navigationItem;
}
Here is my CollectionListFragment Code:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
MormonChannel.injectFragment(this);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.collection_list_fragment, container, false);
ButterKnife.inject(this, v);
return v;
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
listView.setDividerHeight(0);
listView.setFastScrollEnabled(true);
Bundle args = getArguments();
if (args != null) {
languageId = args.getLong(ARG_LANGUAGE_ID, -1L);
collectionId = args.getLong(ARG_COLLECTION_ID, -1L);
if (args.containsKey(ARG_SORT)) {
sort = (Sort) args.getSerializable(ARG_SORT);
}
}
if (collectionId == -1L || languageId == -1L) {
// TODO Implement Empty Collection Text
} else {
collection = collectionManager.findByCollectionId(languageId, collectionId);
getLoaderManager().initLoader(LOADER_ID, null, this);
}
titleEvent = new TitleChangeEvent(collection != null ? collection.getTitle() : getString(R.string.app_name));
bus.post(titleEvent);
}
Manifest for activity:
<activity
android:name=".activity.MainActivity"
android:launchMode="singleTask">
<meta-data
android:name="android.app.default_searchable"
android:value=".activity.SearchActivity"/>
</activity>
In your CollectionListFragment Code, add call to setRetainInstance() method in the onCreate() method, with true as its argument:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
MormonChannel.injectFragment(this);
}
Using setRetainInstance(true) ensures that when configuration changes occur activity will be killed but android will not destroy the fragment it is hosting.
Instead android will save the fragment state and detach the fragment from the activity. Also it wont destroy the fragment and so it wont create it later when hosting activity is created. So fragment will not receive calls to its onDestroy() and onCreate() methods.

Android - save/restore fragment state

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

Categories

Resources