something wrong with my savedInstanceState into my Fragment.
The Save Instance doesn't work. When I switch back to "HomeMenu", all fields disappear.
On my Activity:
private void selectItemFromDrawer(int position) {
FragmentManager fragmentManager = getFragmentManager();
Fragment fragment = null;
String tagFrag = "default";
switch (mNavItems.get(position).mTitle) {
case R.string.menu_home:
fragment = fragmentManager.findFragmentByTag("menu_home");
if(fragment == null) {
fragment = new HomeMenuFragment();
} else {
Toast.makeText(customActivity.this, "already Exists", Toast.LENGTH_SHORT).show();
}
tagFrag = "menu_home";
break;
case R.string.menu_pref:
fragment = fragmentManager.findFragmentByTag("menu_pref");
if(fragment == null)
fragment = new PreferencesFragment();
tagFrag = "menu_pref";
break;
}
if(fragment != null) {
fragmentManager.beginTransaction()
.replace(R.id.mainContent, fragment, tagFrag)
.commit();
}
}
Thats works.. The Fragment change properly..
The TOAST works only if the Fragment is ACTIVE.. but not if I switch back to him.
example:
Home => Home => "toast Appear"
Home => Pref => Home => "No Toast".
Into my HomeMenuFragment I use this code:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.activity_main, container, false);
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if (savedInstanceState != null) {
strUser = savedInstanceState.getString("strUser", "");
strName = savedInstanceState.getString("strName", "");
strDesc = savedInstanceState.getString("strDesc", "");
if(((TextView) getActivity().findViewById(R.id.user))!=null)
((TextView) getActivity().findViewById(R.id.user)).setText(strUser);
if(((TextView) getActivity().findViewById(R.id.name))!=null)
((TextView) getActivity().findViewById(R.id.name)).setText(strName);
if(((TextView) getActivity().findViewById(R.id.desc))!=null)
((TextView) getActivity().findViewById(R.id.desc)).setText(strDesc);
}
}
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString("strUser", ((TextView) getActivity().findViewById(R.id.user)).getText().toString());
outState.putString("strName", ((TextView) getActivity().findViewById(R.id.name)).getText().toString());
outState.putString("strDesc", ((TextView) getActivity().findViewById(R.id.desc)).getText().toString());
}
According to the Bundle documentation, there is no putString() nor getString() methods, you need to use putStringArrayList (String key, ArrayList<String> value). I also suggest you avoid so many calls on getActivity().findViewById(), it is well known that it causes a lot of overhead. Also, why are you obtaining view values from the activity inside a fragment? Looks like duplicated code. Anyway, try this:
TextView tvUser, tvName, tvDesc;
#Override
public void onCreate(Bundle savedInstanceState) {
// Store TextView references calling findViewById only once
tvUser = ((TextView) getActivity().findViewById(R.id.user));
tvName = ((TextView) getActivity().findViewById(R.id.user));
tvDesc = ((TextView) getActivity().findViewById(R.id.user));
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if (savedInstanceState != null) {
ArrayList al = new ArrayList();
al = savedInstanceState.getStringArrayList("args");
tvUser.setText(al.get(0));
tvName.setText(al.get(1));
tvDesc.setText(al.get(2));
}
}
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
ArrayList al = new ArrayList();
al.add(0, tvUser.getText());
al.add(1, tvName.getText());
al.add(2, tvDesc.getText());
outState.putStringArrayList("args", al);
}
OK, I'll try to explain that, is not pretty easy.
In this case, your fragment is not destroyed, only the view inside it does.
So every view you used must implement state save/restore.
I left you a better explanation that mine, hope this helps you!
https://inthecheesefactory.com/blog/fragment-state-saving-best-practices/en
Update
Try to create fragment using newIstance, this is a best practice to avoid the loss of the parameters.
For example:
public static MyFragment newInstance(String user, String name, String desc) {
MyFragment mFragment = new MyFragment();
Bundle args = new Bundle();
args.putString("user", user);
args.putString("name", name);
args.putString("desc", desc);
mFragment.setArguments(args);
return mFragment;
}
And in the fragment onCreate() you can access the data by using:
String strUser = getArguments().getString("user", "");
If the fragment is recreated by Android, the Bundle will be available.
Remember, setArguments can only be called before the Fragment is attached to the Activity.
Let me know!
It might be that you call the super.onSaveInstanceState(outState) before you put the strings which you hope to save. To solve this, you simply need to call super.onSaveInstanceState(outState) AFTER putting the values you hope to save. Here are the changes you need to make:
#Override
public void onSaveInstanceState(Bundle outState) {
outState.putString("strUser", ((TextView) getActivity().findViewById(R.id.user)).getText().toString());
outState.putString("strName", ((TextView) getActivity().findViewById(R.id.name)).getText().toString());
outState.putString("strDesc", ((TextView) getActivity().findViewById(R.id.desc)).getText().toString());
//moved it here
super.onSaveInstanceState(outState);
}
I hope this solves your problem. Give it a try and let me know.
Related
I have a store app
In this application, the fragments each take their own data from the server and are managed by navigation bar.
I want to receive data any fragment one only once , and will only maintain its last state some time later.
I used the replace , hide functions, but I did not get good results
Can anyone suggest a solution?
nav_partition.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View arg0) {
FragmentManager fm = getSupportFragmentManager();
Fragment lastFragment = fm.findFragmentById(R.id.fragment_layout);
if (lastFragment instanceof FragmentPartition)
return;
FragmentTransaction fragmentTransaction = fm.beginTransaction();
fragmentTransaction.hide(lastFragment);
if (fragmentPartition == null)
fragmentPartition = new FragmentPartition();
G.log_toast("is added : " + fragmentPartition);
fragmentTransaction.replace(R.id.fragment_layout, fragmentPartition);
fragmentTransaction.commit();
}
});
You can use savedinstancestate bundle for saving your data. and it can be reused on resuming. Try bellow code.
#Override
public void onSaveInstanceState(Bundle savedInstanceState) {
super.onSaveInstanceState(savedInstanceState);
savedInstanceState.putBoolean("bln", true);
savedInstanceState.putInt("it", 1);
savedInstanceState.putString("str", "Hello");
}
#Override
public void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
boolean bln = savedInstanceState.getBoolean("bln");
int in = savedInstanceState.getInt("it");
String str = savedInstanceState.getString("str");
}
I have tried every post in StackOverflow and have not been successful, i have a FragmentTabHost activity with tabs A B C D E
When i go to tab A and then go to tab B everything is ok, but if i return to tab A is blank, then return to tab B is also blank!!
A -> B -> A = Blank -> B = blank
I followed this post to get it working Dynamically changing the fragments inside a fragment tab host?, but the transition between tabs is not working.
I have tried changing my BaseContainerFragment to use getSupportFragmentManager instead of getChildFragmentManager but was unsuccessful, also removing addToBackStack(null) at this point im out of ideas, any help here will be appreciated, thanks.
This is the mainActivity that contain code for creating tabs using fragment.
public class ActivityMain extends FragmentActivity {
public static final String TAB_1_TAG = "tab_1";
public static final String TAB_2_TAG = "tab_2";
public static final String TAB_3_TAG = "tab_3";
public static final String TAB_4_TAG = "tab_4";
public static final String TAB_5_TAG = "tab_5";
private FragmentTabHost mTabHost;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
}
private void initView() {
mTabHost = (FragmentTabHost)findViewById(android.R.id.tabhost);
mTabHost.setup(this, getSupportFragmentManager(), R.id.realtabcontent);
mTabHost.getTabWidget().setDividerDrawable(null);
mTabHost.getTabWidget().setStripEnabled(false);
mTabHost.addTab(mTabHost.newTabSpec(TAB_1_TAG).setIndicator("", getResources().getDrawable(R.drawable.tab_account)), FragmentAccountContainer.class, null);
mTabHost.addTab(mTabHost.newTabSpec(TAB_2_TAG).setIndicator("", getResources().getDrawable(R.drawable.tab_discounts)), FragmentPromotionsContainer.class, null);
mTabHost.addTab(mTabHost.newTabSpec(TAB_3_TAG).setIndicator("", getResources().getDrawable(R.drawable.tab_payment)), FragmentAccountContainer.class, null);
mTabHost.addTab(mTabHost.newTabSpec(TAB_4_TAG).setIndicator("", getResources().getDrawable(R.drawable.tab_gas)), FragmentAccountContainer.class, null);
mTabHost.addTab(mTabHost.newTabSpec(TAB_5_TAG).setIndicator("", getResources().getDrawable(R.drawable.tab_rest)), FragmentAccountContainer.class, null);
}
#Override
public void onBackPressed() {
boolean isPopFragment = false;
String currentTabTag = mTabHost.getCurrentTabTag();
Log.e("ActivityMain", "currentTabTag: " + currentTabTag);
if (currentTabTag.equals(TAB_1_TAG)) {
isPopFragment = ((BaseContainerFragment) getSupportFragmentManager().findFragmentByTag(TAB_1_TAG)).popFragment();
} else if (currentTabTag.equals(TAB_2_TAG)) {
isPopFragment = ((BaseContainerFragment) getSupportFragmentManager().findFragmentByTag(TAB_2_TAG)).popFragment();
} else if (currentTabTag.equals(TAB_3_TAG)) {
isPopFragment = ((BaseContainerFragment) getSupportFragmentManager().findFragmentByTag(TAB_3_TAG)).popFragment();
} else if (currentTabTag.equals(TAB_4_TAG)) {
isPopFragment = ((BaseContainerFragment) getSupportFragmentManager().findFragmentByTag(TAB_4_TAG)).popFragment();
} else if (currentTabTag.equals(TAB_5_TAG)) {
isPopFragment = ((BaseContainerFragment) getSupportFragmentManager().findFragmentByTag(TAB_5_TAG)).popFragment();
}
Log.e("ActivityMain", "isPopFragment: " + isPopFragment);
if (!isPopFragment) {
finish();
}
}
}
This is my BaseContainerFragment that allows backtracking and replacment of fragments
public class BaseContainerFragment extends Fragment {
public void replaceFragment(Fragment fragment, boolean addToBackStack) {
FragmentTransaction transaction = getChildFragmentManager().beginTransaction();
if (addToBackStack) {
transaction.addToBackStack(null);
}
transaction.replace(R.id.container_framelayout, fragment);
transaction.commit();
getChildFragmentManager().executePendingTransactions();
}
public boolean popFragment() {
Log.e("test", "pop fragment: " + getChildFragmentManager().getBackStackEntryCount());
boolean isPop = false;
if (getChildFragmentManager().getBackStackEntryCount() > 0) {
isPop = true;
getChildFragmentManager().popBackStack();
}
return isPop;
}
}
This is container for the first Tab (this tab holds 2 activities, one is main, and another is called on listview Click)
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
myPrefs = this.getActivity().getSharedPreferences("getLogin", Context.MODE_PRIVATE);
idUser = myPrefs.getInt("idUser", 0);
d(TAG, "idUser: " + idUser);
/*
Map<String,?> keys = myPrefs.getAll();
for(Map.Entry<String,?> entry : keys.entrySet()){
Log.d("map values",entry.getKey() + ": " +
entry.getValue().toString());
}
*/
context = getActivity();
pDialog = new SweetAlertDialog(context, PROGRESS_TYPE);
// Check if Internet present
if (!isOnline(context)) {
// Internet Connection is not present
makeText(context, "Error en la conexion de Internet",
LENGTH_LONG).show();
// stop executing code by return
return;
}
new asyncGetFeedClass(context).execute();
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.activity_cardholder, container, false);
toolbar = (Toolbar) v.findViewById(R.id.toolbar);
TextView mTitle = (TextView) toolbar.findViewById(toolbar_title);
mTitle.setText("TARJETAS");
list = (ListView) v.findViewById(R.id.list);
// Click event for single list row
list.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
FragmentAccount fragment = new FragmentAccount();
// if U need to pass some data
Bundle bundle = new Bundle();
if (listBalance.get(position).get(TAG_ACCOUNT_BANKACCOUNTS_ID) != null) {
bundle.putString("idBankAccount", listBalance.get(position).get(TAG_ACCOUNT_BANKACCOUNTS_ID));
bundle.putString("idGiftCard", "0");
} else if (listBalance.get(position).get(TAG_ACCOUNT_GIFTCARDS_ID) != null) {
bundle.putString("idGiftCard", listBalance.get(position).get(TAG_ACCOUNT_GIFTCARDS_ID));
bundle.putString("idBankAccount", "0");
} else {
bundle.putString("idBankAccount", "0");
bundle.putString("idGiftCard", "0");
}
fragment.setArguments(bundle);
((BaseContainerFragment) getParentFragment()).replaceFragment(fragment, false);
}
});
return v;
}
The main class for Tab #1
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
myPrefs = this.getActivity().getSharedPreferences("getLogin", Context.MODE_PRIVATE);
idUser = myPrefs.getInt("idUser", 0);
d(TAG, "idUser: " + idUser);
/*
Map<String,?> keys = myPrefs.getAll();
for(Map.Entry<String,?> entry : keys.entrySet()){
Log.d("map values",entry.getKey() + ": " +
entry.getValue().toString());
}
*/
context = getActivity();
pDialog = new SweetAlertDialog(context, PROGRESS_TYPE);
// Check if Internet present
if (!isOnline(context)) {
// Internet Connection is not present
makeText(context, "Error en la conexion de Internet",
LENGTH_LONG).show();
// stop executing code by return
return;
}
Bundle bundle = this.getArguments();
idBankAccount = Integer.parseInt(bundle.getString(FragmentCardHolder.TAG_ACCOUNT_BANKACCOUNTS_ID, "0"));
idGiftCard = Integer.parseInt(bundle.getString(FragmentCardHolder.TAG_ACCOUNT_GIFTCARDS_ID, "0"));
if(idBankAccount > 0){
new asyncGetBankTransactions(context).execute();
} else if(idGiftCard > 0) {
new asyncGetGiftCardTransactions(context).execute();
} else {
new asyncGetX111Transactions(context).execute();
}
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.activity_account, container, false);
toolbar = (Toolbar) v.findViewById(id.toolbar);
TextView mTitle = (TextView) toolbar.findViewById(toolbar_title);
mTitle.setText("MI CUENTA");
toolbar.setNavigationIcon(R.drawable.icon_user);
toolbar.setNavigationOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
goToCards();
}
});
layoutAccount = (LinearLayout) v.findViewById(id.layoutAccount);
layoutGetCredit = (LinearLayout) v.findViewById(id.layoutGetCredit);
layoutTransactions = (LinearLayout) v.findViewById(id.layoutTransactions);
btnAccount = (Button) v.findViewById(id.btnMyBalance);
btnGetCredit = (Button) v.findViewById(id.btnGetCredit);
btnSendCredit = (Button) v.findViewById(id.btnSendCredit);
btnTransactions = (Button) v.findViewById(id.btnTransactions);
list = (ListView) v.findViewById(id.list);
btnTransactions.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
layoutAccount.setVisibility(View.GONE);
layoutGetCredit.setVisibility(View.GONE);
layoutTransactions.setVisibility(View.VISIBLE);
}
});
btnGetCredit.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
layoutAccount.setVisibility(View.GONE);
layoutGetCredit.setVisibility(View.VISIBLE);
layoutTransactions.setVisibility(View.GONE);
}
});
btnAccount.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
layoutAccount.setVisibility(View.VISIBLE);
layoutGetCredit.setVisibility(View.GONE);
layoutTransactions.setVisibility(View.GONE);
}
});
return v;
}
private void goToCards() {
FragmentCardHolder fragment = new FragmentCardHolder();
((BaseContainerFragment) getParentFragment()).replaceFragment(fragment, true);
}
I think the problem is in hidden part of code where you add first fragment to container (FragmentAccountContainer and FragmentPromotionsContainer classes). I suggest you to create abstract method in BaseContainerFragment.class with signature by example
protected abstract Fragment getFirstFragment();
So concrete container class will override this method and return new instance of a first fragment to super class and then in parent class add it to fragment container with using add transaction.
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState == null) {
addFragment(getFirstFragment(), false);
}
}
Note you should check if savedInstanceState is null before adding fragment to avoid dublicates in case activity recreation by system.
In nested fragments you could use replace like you did it ((BaseContainerFragment) getParentFragment()).replaceFragment(___, true);
Also i have a few suggestions for you code. You couldn't just avoid overriding onBackPressed in activity like #NecipAllef suggests, because of known bug with default back logic and child fragment manager , but you could simplify call to popFragment like
#Override
public void onBackPressed() {
String currentTabTag = mTabHost.getCurrentTabTag();
boolean isPopFragment = ((BaseContainerFragment) getSupportFragmentManager().findFragmentByTag(currentTabTag)).popFragment();
if (!isPopFragment) {
super.onBackPressed();
}
}
And for setting bundles to fragment i suggest use fabric method pattern, like
public class TestFragment extends Fragment {
public static Fragment newInstance(String text){
Fragment fragment = new TestFragment();
Bundle args = new Bundle();
args.putString("text", text);
fragment.setArguments(args);
return fragment;
}
}
Ps: i created for you a simple project with described logic
Why are you keeping track of Fragments and popping them by yourself? You don't need to do that, and you shouldn't override onBackPressed(). Let FragmentManager handle the fragment transactions.
If you have fragments inside an activity, use
FragmentManager fManager = getFragmentManager();
or if you want to support devices prior to Android 3.0, use
FragmentManager fManager = getSupportFragmentManager();
if fragments are inside another fragment, then use
FragmentManager fManager = getChildFragmentManager();
After you have fManager, to show a fragment, use
fManager.beginTransaction().add(R.id.fragment_parent, new FirstTabFragment()).commit();
where fragment_parent is the parent view which you want to place your fragments.
When you want to switch to next fragment, use
fManager.beginTransaction().replace(R.id.fragment_parent, new SecondTabFragment())
.addToBackStack(null)
.commit();
Since you add it to back stack, you will see your first fragment when you press back. That's it.
Moreover, as you can easily realize this will cause your fragments to be created from scratch every time, you can prevent this by initializing them once and reuse them.
HTH
From all the searches I have found on SO stating that you should save your instance state in the #Override public void onSaveInstanceState(Bundle outState)
However This is tightly coupled with the activities lifestyle.
How can I save the state of my listview in a fragment that gets swapped out with another fragment.
I have one main activity which all the fragments are loaded into.
I have tried this so far:
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
//Save adapter data so that when the fragment is returned to it can be resused.
ArrayList<CategoryMobileDto> categories = new ArrayList<CategoryMobileDto>();
for(int i=0; i < adapter.getCount();i++)
{
categories.add(adapter.getItem(i));
}
String persistData = new Gson().toJson(categories);
outState.putString("Categories", persistData);
}
and then in my OnCreate();
if(savedInstanceState!=null)
{
String data =savedInstanceState.getString("Categories");
Type collectionType = new TypeToken<ArrayList<CategoryMobileDto>>() {
}.getType();
adapter.addAll(gson.<Collection<CategoryMobileDto>>fromJson(data, collectionType));
adapter.notifyDataSetChanged();
}else{
// Make request to server
}
however savedInstanceState is always null. But this makes sense as my activity is not being destroyed and recreated.
This is how I transition from one fragment to another:
fragment.setArguments(args);
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.container, fragment, "ProductListFragment");
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();
Is there a way i can save the state of my listview when the fragment is removed and then restore it again when the fragment is popped from the back-stack?
Move this code from onCreate() to onActivityCreated() of Fragment
if(savedInstanceState!=null)
{
String data =savedInstanceState.getString("Categories");
Type collectionType = new TypeToken<ArrayList<CategoryMobileDto>>() {
}.getType();
adapter.addAll(gson.<Collection<CategoryMobileDto>>fromJson(data, collectionType));
adapter.notifyDataSetChanged();
}else{
// Make request to server
}
If you have any query please let me know.
You can use the Arguments with the Fragment(Only if you have the data to show in fragment before the fragment is loaded means attached). You can setArguments to a fragment which will be persisted when you go to another fragment by fragment transaction and when you come back, load the fragment from the getArguments function.
public void setArguments (Bundle args)
Added in API level 11
Supply the construction arguments for this fragment. This can only be called before the fragment has been attached to its activity; that is, you should call it immediately after constructing the fragment. The arguments supplied here will be retained across fragment destroy and creation.
public final Bundle getArguments ()
Added in API level 11
Return the arguments supplied when the fragment was instantiated, if any.
Please find the sample code below for passing data between fragments :
main.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/flContainer"
android:layout_width="match_parent"
android:layout_height="match_parent" >
</FrameLayout>
MainActivity.java
public class MainActivity extends Activity implements IFragContainer {
private static final String FRAG_TAG = "FragTag";
private FragBase mFrag;
private String dataToBePassedBack;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
changeFragment(FragA.class, "Data to Frag A");
}
#Override
public void changeFragment(Class<? extends FragBase> fragClass, String data) {
try {
FragmentTransaction ft = getFragmentManager().beginTransaction();
mFrag = fragClass.newInstance();
Bundle args = new Bundle();
args.putString("DATA", data);
mFrag.setArguments(args);
ft.replace(R.id.flContainer, mFrag, FRAG_TAG);
ft.addToBackStack(mFrag.toString());
ft.commit();
} catch (Exception e) {
e.printStackTrace();
}
}
#Override
public void onBackPressed() {
dataToBePassedBack = mFrag.getDataToPassBack();
FragmentManager mgr = getFragmentManager();
mgr.executePendingTransactions();
boolean doCheckAndExit = true;
for (int i = mgr.getBackStackEntryCount() - 1; i > 0; i--) {
BackStackEntry entry = mgr.getBackStackEntryAt(i);
if (!TextUtils.isEmpty(entry.getName())) {
mgr.popBackStackImmediate(entry.getId(),
FragmentManager.POP_BACK_STACK_INCLUSIVE);
doCheckAndExit = false;
break;
}
}
if (doCheckAndExit) {
finish();
} else {
mFrag = (FragBase) mgr.findFragmentByTag(FRAG_TAG);
}
}
#Override
public String getDataToBePassedBack() {
return dataToBePassedBack;
}
}
IFragContainer.java
public interface IFragContainer {
void changeFragment(Class<? extends FragBase> fragClass, String data);
String getDataToBePassedBack();
}
FragBase.java
public abstract class FragBase extends Fragment {
public String getDataToPassBack(){
return null;
}
}
FragA.java
public class FragA extends FragBase {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
Button btn = new Button(getActivity());
final IFragContainer fragContainer = (IFragContainer) getActivity();
if (TextUtils.isEmpty(fragContainer.getDataToBePassedBack())) {
btn.setText(getArguments().getString("DATA"));
} else {
btn.setText(fragContainer.getDataToBePassedBack());
}
btn.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT,
LayoutParams.WRAP_CONTENT));
btn.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
fragContainer.changeFragment(FragB.class, "Data to Frag B");
}
});
return btn;
}
}
FragB.java
public class FragB extends FragBase {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
Button btn = new Button(getActivity());
btn.setText(getArguments().getString("DATA"));
btn.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT,
LayoutParams.WRAP_CONTENT));
btn.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
getActivity().onBackPressed();
}
});
return btn;
}
#Override
public String getDataToPassBack() {
return "Data from Frag B to A";
}
}
Hi i have a listview sidebar and i am displaying fragments based on user selection in listview.
This is how i am replacing fragments
public void switchFragment(Fragment fragment, boolean addBackStack) {
try {
FragmentManager manager = getSupportFragmentManager();
FragmentTransaction ft = manager.beginTransaction();
ft.replace(R.id.content, fragment);
currentFragment = fragment;
//if (addBackStack)
ft.addToBackStack(null);
ft.commit();
} catch (Exception e) {
}
}
This is my sample fragment code.Now when i replace fragments i am saving instance state in onpause and restoring it in onresume but it only works when i press back button. When i manually navigate back to fragment from listview ,fragment state is not restored.Why?
public class Fragment1 extends BaseFragment {
int currentFragmentInd = 1;
private Button startButton;
private Button endButton;
private long savedStartTime;
private TextView setStartText;
private TextView setEndText;
private String starttime;
private String endtime;
public int getIndex() {
MyApplication.getApplication().setCurrentChild(0);
MyApplication.getApplication().setCurrentGroup(0);
return currentFragmentInd;
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if (savedInstanceState !=null)
{
}
}
#Override
public void onResume() {
super.onResume();
setStartText= (TextView)getActivity().findViewById(R.id.MAtextView2);
setEndText= (TextView)getActivity().findViewById(R.id.MAtextView3);
setEndText.setText(endtime);
setStartText.setText(starttime);
}
#Override
public void onPause() {
super.onPause();
setStartText= (TextView)getActivity().findViewById(R.id.MAtextView2);
setEndText= (TextView)getActivity().findViewById(R.id.MAtextView3);
starttime=setStartText.getText().toString();
endtime=setEndText.getText().toString();
}
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
}
FrameLayout frameLayout;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View contentView = inflater.inflate(R.layout.layout1, null, false);
((MainActivity) getActivity()).openList(0, 0);
if (savedInstanceState == null) {
}
startButton= (Button) contentView.findViewById(R.id.button);
endButton= (Button) contentView.findViewById(R.id.button2);
endButton.setEnabled(false);
setStartText= (TextView)contentView.findViewById(R.id.MAtextView2);
setEndText= (TextView)contentView.findViewById(R.id.MAtextView3);
startButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
Time now = new Time();
now.setToNow();
}
});
endButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
Time now = new Time();
now.setToNow();
setEndText.setText(now.hour+" : "+now.minute);
}
});
return contentView;
}
}
Late replay but might help somebody else.
This happens because when you click a listview item you create a new inctance of that fragment.
"I assume the fragment you send to switchFragment(Fragment fragment), is created using a 'new' keyword."
Therefore this new instance of a fragment doesnt hold your old data.
This is how I solved this. There are probably better ways, but since nobody replied, I will give my solution.
When you replace the fragment (ft.replace, fragment), give a string reference to that transaction: -ft.replace(R.id.content, fragment, "FRAGMENT_NAME");
When you add the fragment to the backstack with addToBackStack(null); put the name of your fragment where you have null.: -ft.addToBackStack("FRAGMENT_NAME");
Create a method which tells you if that fragment has already been created, and therefore exists in the back stack.:
public boolean isTagInBackStack(String tag){
Log.i(TAG, "isTagInBackStack() Start");
int x;
boolean toReturn = false;
int backStackCount = getSupportFragmentManager().getBackStackEntryCount();
Log.i(TAG, "backStackCount = " + backStackCount);
for (x = 0; x < backStackCount; x++){
Log.i(TAG, "Iter = " + x +" "+ getSupportFragmentManager().getBackStackEntryAt(x).getName());
if (tag == getSupportFragmentManager().getBackStackEntryAt(x).getName()){
toReturn = true;
}
}
Log.i(TAG, "isTagInBackStack() End, toReturn = " + toReturn);
return toReturn;
}
Now before you create a new instance of that fragment check in the backstack if a backstack item named "FRAGMENT_NAME" exists.
if it exists, use that item (fragment) instead of creating a new one.
if (isTagInBackStack("FRAGMENT_NAME")){
Log.i(TAG, "Tag is in BackStack!!!! frag is = " + getSupportFragmentManager().findFragmentByTag("FRAGMENT_NAME"));
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.main_activity_container, getSupportFragmentManager().findFragmentByTag("FRAGMENT_NAME"));
transaction.addToBackStack("FRAGMENT_NAME");
transaction.commit();
}else{
Create the fragment (this happens the first time.
}
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){
}
}