this is my issue, when I create a fragment in android for example let's Say:
GastosSectionFragment gastosFragment = new GastosSectionFragment();
Fragment.gastosFragment.darUserID(userKey);
I send a simple Integer to that activity, no problem. For example lets say I send the number 1. It will be stored in attribute inside that class, because I create the fragment.
Everything will work fine, I receive the number. But when I rotate the phone, the variable "userKEY will be lost, it will converted to cero" I can't save that number when I rotate horizontally my phone, how can I save the data of that variable.
Here I put the code of how I recieve the "userKey" inside the Fragment,
public long darUserID(long userKey) {
// TODO Auto-generated method stub
return user_id = userKey;
}
And the attribute with the onCreate method.
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_section_gastos,
container, false);
idStatico = darUserID(user_id);
And I can't save that little number. this is not a FragmentActivity, this class is just a FRAGMENT. First time fine, rotate screen lost the number that I previously recieve. Fragment doesn't have OnSaveBundleInstances.
Fragment can save user_id in bundle in onSaveInstanceState method and restore after orientation change in onCreateView method. Check this fragment guide, especially in example is shown saving fragment state.
Activity code stays the same (I just changed method name) and your fragment should look like this:
class GastosSectionFragment extends Fragment {
private static final String BUNDLE_USER_ID = "BUNDLE_USER_ID";
private long user_id;
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_section_gastos, container, false);
if (savedInstanceState != null)
user_id = savedInstanceState.getLong(BUNDLE_USER_ID);
// now is your user_id restored after orientation change,
// so you can do some stuff with it
return rootView;
}
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putLong(BUNDLE_USER_ID, user_id);
}
public long setUserID(long userKey) {
this.user_id = userKey;
}
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
}
add method override, and you variable saving state
After KOSO answer, this is how I solve the problem, hope it helps someone else.
When I create the Fragment for first time everything work well, send the userKey, everything is fine.
GastosSectionFragment gastosFragment = new GastosSectionFragment();
Fragment.gastosFragment.darUserID(userKey);
Now in order to fix that, a fragment when the phone is rotated, for example to horizontal it will call the override method OnCreateView(). Everytime when you rotate the phone this method will be called.
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState)
{
View rootView = inflater.inflate(R.layout.fragment_section_gastos,
container, false);
//This is not null when we rotate the screen, first we save the Bundle SavedInstance
//After do that we put a integer, in this case the userKey
//then we recover the user key, and everything will work fine.
if (savedInstanceState != null)
{
this.savedInstanceState = savedInstanceState;
savedInstanceState.putInt("userKey", (int) idStatico);
idStatico = savedInstanceState.getInt("userKey");
}
//this will be null for the first time, only the first time this will bse used.
if (savedInstanceState == null)
{
idStatico = darUserID(user_id);
}
//Do the rest of the work
}
Related
This question already has answers here:
How to retain EditText data on orientation change?
(12 answers)
Closed 5 years ago.
I have already posted this question once and even tried all possible solutions. So please, if are going to downvote, at least provide the correct answer because I have tried out many solutions and nothing is working out.
In my app, I have an EditText inside the fragment which on orientation change, looses its value.One more thing, I am using different layouts for different orientations. Is there any way to retain the fragment data? I am again mentioning "Fragment data".Please find my Fragment code below for further reference.
#Override
public void onSaveInstanceState(Bundle outState) {
outState.putString("fname",etxt_firstname.getText().toString());
outState.putString("lname",etxt_lastname.getText().toString());
super.onSaveInstanceState(outState);
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// retain this fragment
setRetainInstance(true);
}
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if(savedInstanceState!= null)
{
etxt_firstname.setText(savedInstanceState.getString("fname"));
etxt_lastname.setText(savedInstanceState.getString("lname"));
}
}
Inside OnCreateView():
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
shrp_sharedpreferences = myContext.getSharedPreferences("shrp", Context.MODE_PRIVATE);
View view = null;
if(!isTablet(myContext))
{
if(getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE)
{
view = inflater.inflate(R.layout.fragment_home_landscape, container, false);
}
else
{
view = inflater.inflate(R.layout.fragment_home, container, false);
}
}
else
{
view = inflater.inflate(R.layout.fragment_home, container, false);
}
Even though the fragment itself is retained, its view is destroyed when device is rotated in onDestroyView() (You should also clear any references to views there to prevent leaking old orientation).
Trying to restore text in onActivityCreated() does nothing because you're referencing views from old orientation, and they aren't used anymore.
When fragment is reattached (in Activity created for new orientation), new View is created in onCreateView() - this is where you can use savedInstanceState or any fragment fields to restore views to previous state:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// (...) your layout inflation and view binding logic...
if(savedInstanceState != null){
etxt_firstname.setText(savedInstanceState.getString("fname"));
etxt_lastname.setText(savedInstanceState.getString("lname"));
}
// return view you inflated
}
Users have a crash. I know why but I do not know how to fix it. I am newbie in android dev.
Situation:
Android: Fragment inside Activity. Fragment has an EditText. Activity has a button. User tap the button. Inside Button.OnClick() I want to get text Fragment.EditText.getText();
Some users have a crash here EditText.getText(). i.e. EditText is null.
How I do:
In Activity:
public class MyAcrivity extends AppCompatActivity implements OnFragmentInteractionListener {
final MyFrag myFrag= MyFrag.newInstance();
public void run(final View view) {
//some users have crash here because getEt() return null
final String str = myFrag.getEt().getText().toString();
}
}
In Fragment:
public class MyFrag extends Fragment {
private EditText et;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_myfrag, container, false);
et = (EditText) view.findViewById(R.id.et);
return view;
}
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
et = (EditText) view.findViewById(R.id.et);
}
public EditText getEt() {
// return (EditText) getView().findViewById(R.id.et); here getView() could be nul too
return et;
}
}
I know getView() could be null (already googled it).
Init "View" inside onCreateView useless. Crash still happend.
Init "View" inside onViewCreated useless. Crash still happend.
I can NOT reproduce this crash in emulator or my smartphone. I have stable work of my app. BUT some users have the crash and Fabric(crashlytics) is sending messages about it.
People! Help! How to obtain some View from Fragment correctly? I can not to find answers from lifecycle of Fragment. Please explain to me what is wrong.
From your code it seems you have only created instance of fragment and trying to access the view .But to get the view of the fragment you have to add fragment to your activity.
Do this in your activity
FragmentManager fm = getFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
MyFrag myFrag= MyFrag();
ft.replace("YOUR_FRAGMENT_CONTAINER's id", myFrag);
ft.commit();
Just a hunge, try to do the newInstance() call in the activity lifecycle. Otherwise, there might not be a trustable context to attach fragments. Try to do so in the onCreate().
And something that might be unrelated, but did you properly attach that fragment to the activity? Chances are you are already doing, since some users are correctly accessing it. Anyway, here's the fragment documentation, and a code block to attach it.
FragmentManager fragmentManager = getFragmentManager()
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
Fragment myFrag = MyFrag.newInstance();
fragmentTransaction.add(R.id.fragment_container, myFrag);
fragmentTransaction.commit();
Use butterknife like this:
class ExampleActivity extends Activity {
#Bind(R.id.user) EditText username;
#Bind(R.id.pass) EditText password;
#BindString(R.string.login_error)
String loginErrorMessage;
#OnClick(R.id.submit) void submit() {
// TODO call server...
}
#Override public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.simple_activity);
ButterKnife.bind(this);
// TODO Use fields...
}
}
Add the fragment to the Activity in Activity.onCreate() method.
Get the reference to the Activity during Fragment.onAttach (Context) in the Fragment.
Create a method in Activity like enableButton(), which will enable the button like button.setEnabled (true).
Call this method from Fragment onCreateView after initializing the EditText.
This way you can ensure that EditText is initialized before the button is clicked.
Also remember to make the Activity reference null in Fragment.onDetach() to prevent any leak.
If you use getActivity.findViewById in Fragment.
public class MyFrag extends Fragment {
private EditText et;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_myfrag, container, false);
return view;
}
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
et = (EditText) getActivity.findViewById(R.id.et);
}
public EditText getEt() {
// return (EditText) getView().findViewById(R.id.et); here getView() could be nul too
return et;
}
}
I sort of figured out how to send the data from a fragment -> activity -> a second fragment. However, when the second fragment tries to pull the data, it runs into a null error. The code that first sends the data is shown below:
Intent i = new Intent(getActivity(), scoring_two.class);
i.putExtra("Cv",Cv);
dicstr_twotank_frag.this.startActivity(i);
The code for the scoring_two activity is below:
package edu.UDayton.www;
import android.app.Fragment;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
//This is simply the placeholder activity for allowing the fragment to attach to something.
//Fragements deal with screen rotations more easily than activities, which is why all the
//code is the fragment scoring. But fragments can't exist by themselves so there needs to
//be a dummy activity to hold them.
public class scoring_two extends FragmentActivity{
#Override
protected void onSaveInstanceState(Bundle saveInstanceState) {
super.onSaveInstanceState(saveInstanceState);
}
#Override
protected void onCreate(Bundle saveInstanceState) {
super.onCreate(saveInstanceState);
setContentView(R.layout.rel5);
Intent intent = getIntent();
Double Cv = intent.getDoubleExtra("Cv", 1.0);
Fragment scoring = new Fragment();
Bundle bundle = new Bundle();
bundle.putDouble("Cv", Cv);
scoring.setArguments(bundle);
}
}
And the scoring fragment:
package edu.UDayton.www;
public class scoring extends Fragment {
public TextView textOverallScore;
Bundle bundle;
Double Cv;
//Button declarations
Button main;
//This creates a view that the fragment will use to obtain the actual layout for the activity
public View rootView;
//This is the beginning of activity initialization. Since this is an Android fragment, the first
//step is to attach it to an actual activity. The activity in this case is essentially a placeholder.
//All of the real work of this system comes from this Android fragment
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
}
//This code preserves certain values if you exit the app and come back to it
#Override
public void onCreate(Bundle saveInstanceState) {
super.onCreate(saveInstanceState);
//This retains all of the data in the app upon screen rotation. Normally the activity is destroyed and
//re-created upon rotation. This prevents this from happening.
setRetainInstance(true);
}
//These lines obtain the layout view "scoring" to use for the fragment.
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle saveInstanceState) {
View rootView = getActivity().getLayoutInflater().inflate(R.layout.scoring, null);
//Identify the text that needs to change
textOverallScore=(TextView)rootView.findViewById(R.id.textOverallScore);
//Receives data
bundle = getArguments();
Cv = bundle.getDouble("Cv", 1.0);
textOverallScore.setText(Cv+"");
//Identifies the button
main = (Button) rootView.findViewById(R.id.buttonMain);
//Sends user back to main screen
main.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
Intent i = new Intent(getActivity(), systems.class);
scoring.this.startActivity(i);
}
});
return rootView;
}
}
I believe the error occurs at the following line.
Cv = bundle.getDouble("Cv", 1.0);
If I comment out the line, and just have Cv = 0.0 or something else, it makes the proper change to the output file. Any help would be appreciated, thanks!
After the help from #dhun, I have updated the code for the scoring fragment to the following:
public class scoring extends Fragment {
public TextView textOverallScore;
//Button declarations
Button main;
//This creates a view that the fragment will use to obtain the actual layout for the activity
public View rootView;
//This is the beginning of activity initialization. Since this is an Android fragment, the first
//step is to attach it to an actual activity. The activity in this case is essentially a placeholder.
//All of the real work of this system comes from this Android fragment
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);}
public Bundle getBundle() {
Bundle bundle = new Bundle();
return bundle;
}
//This code preserves certain values if you exit the app and come back to it
#Override
public void onCreate(Bundle saveInstanceState) {
super.onCreate(saveInstanceState);
//This retains all of the data in the app upon screen rotation. Normally the activity is destroyed and
//re-created upon rotation. This prevents this from happening.
setRetainInstance(true);
}
//These lines obtain the layout view "scoring" to use for the fragment.
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle saveInstanceState) {
View rootView = getActivity().getLayoutInflater().inflate(R.layout.scoring, null);
//Identify the text that needs to change
textOverallScore=(TextView)rootView.findViewById(R.id.textOverallScore);
//Receives data
Double Cv = getBundle().getDouble("Cv",1.36);
textOverallScore.setText(Cv + "");
//Identifies the button
main = (Button) rootView.findViewById(R.id.buttonMain);
//Sends user back to main screen
main.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
Intent i = new Intent(getActivity(), systems.class);
scoring.this.startActivity(i);
}
});
return rootView;
}
}
And now it no longer crashes, but it only passes the final default value through, it does not receive the Cv value (or it cannot find it).
I think the bundle you are getting in scoring fragment's onCreatView() by
bundle = getArguments();
returns null bundle and that is why you are getting null pointer exception at the line you have mentioned. Try implementing callback interface to send data back and forth between the activity and fragment. Let me give you a good example. You should refer to this answer posted here and make your code like that and try.
https://stackoverflow.com/a/16578326/4150528
So here is my code. 'currentFragment' is simply a field that tracks what is currently being displayed. This is in a class that itself is a Fragment (so I have a fragment showing a fragment).
private void selectNavBarItem(NavbarItem v)
{
Fragment fragmentToUse = null;
if (v == setpointsNavItem)
{
fragmentToUse = setpointsFragment;
}
else if (v == rapidSetupNavItem)
{
fragmentToUse = rapidSetupFragment;
}
else if (v == outdoorResetNavItem)
{
fragmentToUse = outdoorResetFragment;
}
else if (v == rampDelayNavItem)
{
fragmentToUse = rampDelayFragment;
}
if (fragmentToUse != null)
{
FragmentManager fm = getFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
if (currentFragment != null)
{
ft.detach(currentFragment);
}
currentFragment = fragmentToUse;
if (currentFragment.isDetached())
{
ft.attach(currentFragment);
}
else
{
ft.add(R.id.setup_content_holder, currentFragment);
}
ft.addToBackStack(null);
ft.commit();
}
Everything looks great, but the views are getting recreated for all the fragments (onCreateView() and onViewCreated()). I was hoping that attaching and detaching would work, but it doesn't. The reason I want to maintain the view is so the user's selections are still there when they navigate back.
Another option is showing and hiding, but I don't know how to make that work because the fragment that owns this code has a FrameLayout (R.id.setup_content_holder) that holds the fragment I want to add, and I can't just add four fragments to it but hide three of them. There is an option to add a fragment with no container, but I have no idea how that is supposed to work.
So, any ideas?
Try this, this will solve your frgment view r-creating issue;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
if (mFragmentView != null) {
((ViewGroup) mFragmentView.getParent()).removeView(mFragmentView);
return mFragmentView;
}
mFragmentView = inflater.inflate(R.layout.home_fragment, container, false);
..... // your remaining code
}
The OnCreateView methods are always called within a Fragment.
To solve the problem you're describing what you really need to do is save the state of the fragment, then when it returns the application will restore what you saved.
e.g. (within the fragment class in question):
#Override
public void onSaveInstanceState(Bundle savedInstanceState) {
super.onSaveInstanceState(savedInstanceState);
savedInstanceStatePutString("userString", someTextView.getText().toString());
savedInstanceStatePutInt("userInt", userInt);
// etc...
}
#Override
public void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
someTextView.setText(savedInstanceState.getString("userString"));
userInt = savedInstanceState.getInt("userInt");
}
That should solve your problem while hopping between fragments; the application will call onSaveInstanceState and onRestoreInstanceState when a fragment gets pushed into or pulled out of the stack.
It will not save them forever, however! If you need more persistent storage, look into other options such as saving to a sqlite database.
OP here.
So I hacked something together here, and I'm wondering if it's a good idea. I have a Fragment holding a View. The View contains everything I want to save (in the short term, of course - this isn't supposed to be any more persistent than RAM). When the Fragment calls onCreateView() I simply return the already-created View.
Now, I ran into an issue where the View was not being removed by the fragment manager. I added a call in onPause() to make sure it's removed from the parent.
Everything seems to work fine, but I want to make sure I'm not doing something really bad. I know Android really really wants to manage its view lifecycles itself, but I do not want it recreating them every damn time. They are complicated and I don't want to deal with re-initializing all the subview text/image/state. Will I run into issues in my attempt to do a run-around Android's normal operating procedure?
EDIT: forgot the code:
public class OutdoorResetFragment extends Fragment
{
private OutdoorResetView view;
public OutdoorResetFragment()
{
}
public void onAttach(Activity activity)
{
if (view == null || view.getContext() != activity)
{
view = new OutdoorResetView(activity);
}
super.onAttach(activity);
}
public void onPause()
{
super.onPause();
ViewGroup container = (ViewGroup) view.getParent();
if (container != null)
{
container.removeAllViews();
}
}
#Override
public View onCreateView(final LayoutInflater inflater, final ViewGroup container,
final Bundle savedInstanceState)
{
return view;
}
public OutdoorResetView getView()
{
return view;
}
}
I have a ViewPager. My FragmentPageAdapter returns the position of the Viewpager in the getItem() methode. But after rotating the Screen the methode returns no value. Why? If I understood it right, everytime you rotate the screen OnCreateView() is called, but why doesn't it return the value any more? Could someone point out how to solve this? Thank you
Edit: My FragmentPageAdapter:
public Fragment getItem(int position) {
return Fragment_results.newInstance(position);
}
My Fragment:
public static Fragment_results newInstance(int i) {
Fragment_results fragment = new Fragment_results();
fragment.mContent = i +"";
return fragment;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.layout_result, null);
((TextView) view.findViewById(R.id.text)).setText(mContent);
The position is set to 0 upon creating of the ViewPager instance and whenever you set a new adapter. When onCreateView() is called, you're rebuilding the entire app essentially. In order to revert back to the position it was, you must first use onSavedInstanceState(Bundle savedInstanceState) and store the item position via the Bundle.
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt("pageItem", myViewPager.getCurrentItem());
}
Then in onCreate, restore the viewPager's state like so:
if (savedInstanceState != null) {
myViewPager.setCurrentItem(savedInstanceState.getInt("pageItem", 0));
}