I am attempting to do something that seems common practive (f.i. here, second answer). But while the data is transmitted and can be put f.i. into a viewmodel, android does not seem to care that I have changed the text of a textview. This is my code (I prefer databinding over findviewbyid):
Activity:
#Override
public void onItemSelected(String param) {
MainFragment oFragment = (MainFragment) getSupportFragmentManager().findFragmentByTag(MainFragmentTag);
if(oFragment != null) {
oFragment.SetText(param);
getSupportFragmentManager()
.beginTransaction()
.replace(oBinding.mainContainer.getId(), oFragment)
.addToBackStack(null)
.commit();
}
}
Receiving Fragment:
public void SetText(String param) {
String sInput = oBinding.MyInputField.getText().toString();
oBinding.TextviewIWantToChange.setText(param);
Entry oEntry = Manager.CreateEntry(sInput, param);
viewmodel.Insert(oEntry);
}
The old fragment instance shows up, the right param is transmitted and viewmodel insertion works smoothely. But the textview is not updated. Any ideas?
Pass your data with Fragment arguments and read another fragment getArguments
#Override
public void onItemSelected(String param) {
MainFragment oFragment = (MainFragment) getSupportFragmentManager().findFragmentByTag(MainFragmentTag);
if(oFragment != null) {
Bundle bundle=new Bundle();
bundle.putString("param",param);
oFragment.setArguments(bundle);
getSupportFragmentManager()
.beginTransaction()
.replace(oBinding.mainContainer.getId(), oFragment)
.addToBackStack(null)
.commit();
}
}
// Call this on which fragment you have need
public void SetText(String param) {
String sInput = oBinding.MyInputField.getText().toString();
oBinding.TextviewIWantToChange.setText(param);
Entry oEntry = Manager.CreateEntry(sInput, param);
viewmodel.Insert(oEntry);
}
Call thi methhod on OnCreateView
final Bundle bundle = getArguments();
String param = bundle.getString("param");
Related
I was able to save outState as follows but was unable to restore when I land on this AttendanceFragment.cs second time.
public override void OnSaveInstanceState(Bundle outState)
{
base.OnSaveInstanceState(outState);
dataGotFromServer = JsonConvert.SerializeObject(dataList);
outState.PutString(KEY_OUTSTATE, dataGotFromServer);
}
I tried here to restore but could not get it
public override void OnViewStateRestored(Bundle savedInstanceState)
{
base.OnViewStateRestored(savedInstanceState);
if(savedInstanceState!=null)
{
var result = savedInstanceState.GetString(KEY_OUTSTATE, dataGotFromServer);
}
}
and also I tried on CreateView(), OnActivityCreated() and On Create() but unsuccessfull to restore.
And my code for fragment replacement is as
public void ReplaceFragment(Context context, Fragment newFragment, string TAG)
{
Android.Support.V4.App.FragmentManager fragmentManager = ((FragmentActivity)context).SupportFragmentManager;
Android.Support.V4.App.FragmentTransaction ft = fragmentManager.BeginTransaction();
ft.Replace(Resource.Id.HomeFrameLayout, newFragment);
ft.AddToBackStack(TAG);
ft.Commit();
}
Edited:
This is how I call this fragment
case (Resource.Id.nav_attendance):
var role = session.GetUserDetails().Get(SessionManagement.KEY_ROLE).ToString();
if (role=="Student")
{
Fragment attendanceTabFragment = new AttendanceTabFragment();
customFragment.ReplaceFragment(this, attendanceTabFragment,typeof(AttendanceTabFragment).Name);
}else
{
Fragment attendanceFragment = new AttendanceFragment();
customFragment.ReplaceFragment(this, attendanceFragment, typeof(AttendanceFragment).Name);
}
Any idea or sample code much appreciated.
Thank you.
Unless the Activity that contains the Fragment get disposed, Fragment's OnSaveInstanceState is not going to be called.
In a situation were you are swapping Fragments in and out, using Fragment.Arguments is an option instead of a singleton/static var...
re: getArguments / setArguments
In using arguments:
Create a new Bundle in the Fragment constructor and assign it to
Arguments
In the OnPause override update the Arguments/Bundle
with the items you need to save.
In the OnResume override read the
Arguments/Bundle items that you need to restore.
Example Fragment:
public class Fragment1 : Fragment
{
public Fragment1(System.IntPtr javaReference, Android.Runtime.JniHandleOwnership transfer) : base(javaReference, transfer)
{
CreateArgumentBundle();
}
public Fragment1()
{
CreateArgumentBundle();
}
void CreateArgumentBundle()
{
Arguments = new Bundle();
}
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
return inflater.Inflate(Resource.Layout.Frag1, container, false);
}
public override void OnPause()
{
base.OnPause();
Arguments.PutString("someKey", "StackOverflow");
}
public override void OnResume()
{
base.OnResume();
var someKeyString = Arguments.GetString("someKey", "someDefaultString(new bundle)");
Log.Debug("SO", someKeyString);
}
}
In your ReplaceFragment make sure that you are assigning the TAG in the Replace call:
public void ReplaceFragment(Context context, Fragment newFragment, string TAG)
{
SupportFragmentManager
.BeginTransaction()
.Replace(Resource.Id.fragmentContainer, newFragment, TAG)
.AddToBackStack(TAG)
.Commit();
}
Before calling your ReplaceFragment, check to see if the Fragment exists (FindFragmentByTag) before creating a new one, this example just swaps two fragments in and out and only creates new ones if the manager does not contain one:
button.Click += delegate
{
toggle = !toggle;
var frag = SupportFragmentManager.FindFragmentByTag(toggle ? "frag1" : "frag2");
frag = frag ?? ( toggle ? (Fragment)new Fragment1() : (Fragment)new Fragment2() );
ReplaceFragment(this, frag, toggle ? "frag1" : "frag2");
};
Note: You will still need to handle the other Fragment lifecycle events in the case that the hosting Activity is recycled:
Fragment LifeCycles
I have a ProfileFragment class which contains two setters:
public void setPseudo(String pseudo){
textPseudo.setText(pseudo);
}
public void setEmail(String email){
textEmail.setText(email);
}
And in my Activity I would like to call these functions:
user = new ProfileFragment();
if (intent != null) {
user.setPseudo(intent.getStringExtra(USER_PSEUDO));
user.setEmail(intent.getStringExtra(USER_EMAIL));
}
It says "can't resolve method...".
Does it mean I can't do this?
Are you sure you don't have a Profile class with setters? Not a Fragment?
Fragments generally don't use setters, they use arguments.
Reason being: If you call setEmail, and then you called to some view setText within the new Fragment, you get a NullPointerException because that TextView was never initialized
Fragment profileFragment = new ProfileFragment();
Bundle args = new Bundle();
if (intent != null) {
args.putAll(intent.getExtras());
}
profileFragment.setArguments(args);
// Show new Fragment
getSupportFragmentManager()
.replace(R.id.content, profileFragment)
.commit();
And inside your Fragment's onCreateView, you can now use this, for example
final Bundle args = getArguments();
String pseudo = "";
if (args != null) {
pseudo = args.getString(YourActivity.USER_PSEUDO);
}
textPseudo.setText(pseudo);
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.
I have two fragments that are in the main activity and i want to refresh them when something occurs.
Now the code works for second fragment, but won't work for the first, and i am not sure why.
I have been looking at the code for about an hour, and i can't seem to find a reason.
Here is the code
public class MainActivity extends FragmentActivity {
Fragment frag,frag2;
FragmentManager fm;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
String screen = getString(R.string.screen_type);
/*
* Get a reference to fragment manager
* Wire the container to represent fragment
*/
fm = getSupportFragmentManager();
frag = fm.findFragmentById(R.id.container);
if(screen.equals("large")){
frag2 = fm.findFragmentById(R.id.containerDetails);
loadFragments(frag,frag2,fm);
}
/*Loads the fragment into the activity*/
else
loadFragment(frag,fm);
}
private void loadFragments(Fragment frag, Fragment frag2, FragmentManager fm) {
if(frag == null && frag2 == null){
frag = new DisplayFragment();
frag2 = new DetailsFragment();
fm.beginTransaction().add(R.id.container,frag).add(R.id.containerDetails, frag2).commit();
}
}
private void loadFragment(Fragment frag, FragmentManager fm) {
if(frag == null){
frag = new DisplayFragment();
fm.beginTransaction().add(R.id.container,frag).commit();
}
}
public void updateDetails(int position) {
// Reload current fragment
if(frag2!=null)fm.beginTransaction().remove(frag2).commit();
frag2 = new DetailsFragment();
Bundle b = new Bundle();
b.putInt("Id",position);
frag2.setArguments(b);
fm.beginTransaction().add(R.id.containerDetails, frag2).commit();
}
public void updateDisplay() {
// Reload current fragment
if(frag!=null)fm.beginTransaction().remove(frag).commit(); //THIS IS ALWAYS NULL FOR SOME REASON
frag = new DisplayFragment();
fm.beginTransaction().add(R.id.container, frag).commit();
}
public void refreshDetails() {
// Reload current fragment
if(frag2!=null)fm.beginTransaction().remove(frag2).commit();
frag2 = new DetailsFragment();
fm.beginTransaction().add(R.id.containerDetails, frag2).commit();
}
}
The first fragment is always null, and it doesn't get removed, instead another fragment is pasted over that, and creates a mess.
Try using replace() method rather than add()
An SSCCE for this issue is available on GitHub.
For future readers, the original example is on a branch of the same project, and the fix is available in this diff.
This SSCCE has a ListView and a row of buttons. The buttons are supposed to change the data in the ListView, and the listView rows (when clicked) are supposed to open a new fragment and advance the backstack while staying in the same activity.
If do the following things, it produces the following result:
Open the app.
Tap the ListView. - FragmentTransaction.replace(...) with addToBackStack(true)
Tap any of the buttons. - FragmentTransaction.replace(...) with addToBackStack(false)
Tap the back button.
Result:
Both fragments become visible, but I only want the first loaded fragment (ListTwoFragment in code) to display. Is this how fragments are supposed to work? If so, how can I get the desired effect?
MainActivity.java:
public class MainActivity extends FragmentActivity implements ListTwoFragment.Callbacks,
ListThreeFragment.Callbacks {
public static final String KEY_ARGS = "args";
private String curUri = "";
private String curArgs = "";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
selectContent(false);
}
private void selectContent(boolean addToBackStack) {
Fragment fragment;
if (curUri.isEmpty()) {
// Use default fragment
fragment = new ListTwoFragment();
curUri = ListTwoFragment.class.getName();
}
else {
try {
Class<Fragment> fragmentClass = (Class<Fragment>) Class.forName(curUri);
fragment = fragmentClass.newInstance();
}
catch (Exception e) { // ClassNotFound, IllegalAccess, etc.
return;
}
}
// Configure fragment
Bundle args = new Bundle();
args.putString(KEY_ARGS, curArgs);
fragment.setArguments(args);
attachFragment(fragment, addToBackStack, curUri + ";" + curArgs, R.id.fragment_container);
}
protected void attachFragment(Fragment fragment, boolean addToBackStack, String tag, int replaceId) {
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(replaceId, fragment, tag);
if (addToBackStack) transaction.addToBackStack(tag);
transaction.commit();
}
#Override
public void onTwoButtonClick(String title) {
curUri = ListTwoFragment.class.getName();
curArgs = title;
selectContent(false);
}
#Override
public void onTwoListClick() {
curUri = ListThreeFragment.class.getName();
curArgs = "";
selectContent(true);
}
#Override
public void onThreeButtonClick(String title) {
curUri = ListThreeFragment.class.getName();
curArgs = title;
selectContent(false);
}
}
I'm working with Fragments to, and the way I'm doing it:
to go forward (add to stack), and backwords (remove from stack) are two different functions
to Add to Stack and change Fragment:
public void changeFragmentAddToStack(Fragment myNewFragment) {
FragmentTransaction t = getSupportFragmentManager().beginTransaction();
t.add(R.id.main_fragment, myNewFragment);
t.addToBackStack(null);
t.commit();
}
To go back Stack:
public void goBackStackMain() {
FragmentManager man = getSupportFragmentManager();
if(man.getBackStackEntryCount()>0){
man.popBackStack(man.getBackStackEntryAt(0).getName(), FragmentManager.POP_BACK_STACK_INCLUSIVE);
}
}
And if you want to do both: To go back stack and change fragment:
public void goBackStackAndReplaceFragment(Fragment myNewFragment) {
FragmentManager man = getSupportFragmentManager();
if(man.getBackStackEntryCount()>0){
int n = man.getBackStackEntryCount();
man.popBackStack(man.getBackStackEntryAt(n-1).getName(), FragmentManager.POP_BACK_STACK_INCLUSIVE);
}
FragmentTransaction t = getSupportFragmentManager().beginTransaction();
t.replace(R.id.main_fragment, myNewFragment);
t.commit();
}
I hope to help you !