I use ListFragment
setRetainInstance (true); does not work, I do not know why
savedInstanceState == null
my sources
public class Fragment_Left extends ListFragment {
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
Log.d(LOG_TAG, "Fragment1 onAttach");
}
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(LOG_TAG, "Fragment1 onCreate");
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
setRetainInstance(true);
}
#Override
public void onListItemClick(ListView l, View v, int position, long id) {
getListAdapter().getItem(position);
}
public void onSaveInstanceState(Bundle state) {
super.onSaveInstanceState(state);
Log.i(LOG_TAG, "onSaveInstanceState()");
}
}
public class Fragment_Left extends ListFragment {
private ArrayList<Menu_item> menu_list; .....
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
setRetainInstance(true);
menu_list = new ArrayList<Menu_item>();......
but After I turned Screen menu_list is null, or I did something wrong?
how do I store an array in a fragment?
I am assuming that by "store an array in a fragment" that you mean "retain an array used by a fragment across configuration changes". If so:
Option #1: Put it in a data member of the fragment, and call setRetainInstance(true) on that fragment.
Option #2: If the array is of a data type that is supported by Bundle, override onSaveInstanceState(), put the data in the Bundle, and retrieve that data in other methods in the new fragment instance created after the configuration change (e.g., onCreateView()).
Option #3: If the array really represents the data model of your app, hold it in a central persistent spot that the old and new fragment instances can access, such as a database or a file.
Related
https://medium.com/#cervonefrancesco/model-view-presenter-android-guidelines-94970b430ddf says to restore state in the model instead of the presenter. What if I have a very simple "model", say a binary toggle that updates a textview to be on or off? Creating a model Toggle class that has a single string value seems like overkill.
Another option is to pass the bundle from my Activity into a corresponding method in my presenter inside onSaveInstanceState and restore it similarly with onCreate. But the article also says that we should avoid having android dependencies in the presenter.
Finally I tried using Icepick but this did not work:
MainActivity.java
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Icepick.restoreInstanceState(this, savedInstanceState);
(Button) findViewById(R.id.btn).setOnClickListener(this);
presenter.onCreate();
}
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
Icepick.saveInstanceState(this, outState);
}
#Override
public void onClick(View view) {
presenter.onButtonClicked();
}
#Override
public void updateState(String state) {
tv.setText(state);
}
MainPresenter.java
public class MainPresenter {
private MainView mainView;
#State String toggle;
#Inject
public MainPresenter(MainView mainView) {
this.mainView = mainView;
}
void onCreate() {
mainView.updateState(toggle);
}
void onButtonClicked() {
mainView.updateState(toggle.equals("on") ? "off" : "on");
}
}
What are my options? If I have to use the model approach can I see an example of this for my case?
If you're using annotation processing to maintain the state, it won't automatically populate data into your presenter without Icepick.saveInstanceState(this, outState) which you can't call in the presenter.
#State String toggle;
This line should be present in the activity. Have a method in your presenter to request data by toggle. Something like this:
#State String toggle = "off"; //default value
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Icepick.restoreInstanceState(this, savedInstanceState);
(Button) findViewById(R.id.btn).setOnClickListener(this);
presenter.onCreate();
presenter.setState(toggle)
}
You can store this value as the global variable in the presenter and decide the app flow accordingly.
I have a little project, one activity and two fragment. I have started from the "Bottom Navigation Activity" sample on a new projet.
The I use the Android Annotations (https://github.com/androidannotations/androidannotations/wiki) for injecting my fragment, It display correctly but when I rotate, I loose all my information and the app display the first fragment, even if I displayed the second one. I tried to save my data, from an edit text with a Bundle object, in the "onSaveInstanceState" method, I get back it in the "onActivityCreated" or in the "onCreate" method but after their method were re-call and my data desappear.
I don't know how to save which fragment was displayed and how re-displayed it with the android annotations.
In my Activity class I have those methods :
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
}
#Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
}
And in my main fragments :
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
if(dpdEditText != null){
rttNumberTemp = rttEditText.getText().toString();
outState.putString("rttNumberTemp", rttNumberTemp);
}
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
Log.d(Tag, "onActivityCreated");
if(savedInstanceState != null){
Log.d(Tag, "onActivityCreated if");
updateEditText(rttEditText,savedInstanceState.getString("rttNumberTemp"));
}
}
So my question is how can I save my fragment, and its data and which fragment is displayed ?
I finally find my mistake. In my main activity I have the following method :
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
So the program call the view by the injection and call it again with the setContentView. So I update the method like that :
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
I have a Main Activity which contains two Fragments (Frag A and Frag B).
Only one Fragment is to be shown at a time.
Both Fragments are contain a ListView (Common for both Frag A and Frag B).
1.When Activity is created Frag A loads all its values through an AsyncTask
2.When I push a button I switch to Frag B which does the same process
3.Again on push of a button I Switch to Frag A and the AsyncTask is called again
I don't want to carry out the third step .Instead I just want to save the instance of Frag A. So the contents are not loaded again.How can I do it?
I know i will have to stop calling AsyncTask again which will be taken care of. Any help with saving of Instance State is much appreciated.
public class DepositFragement extends Fragment {
ProgressDialog pDialog;
private String TAG="DEPOSITS FRAGMENT";
PrefManager pref;
ListView depList;
ListView depoList;
Boolean isFirstTime=true;
Parcelable depoListInstance;
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
setRetainInstance(true);
return inflater.inflate(R.layout.deposits_fragment,container,false);
}
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putParcelable("Depo Instance", depoList.onSaveInstanceState());
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if(savedInstanceState!=null)
{
depoListInstance = savedInstanceState.getParcelable("Depo Instance");
depoList.onRestoreInstanceState(depoListInstance);
}
depoList=(ListView)getActivity().findViewById(R.id.lvDepositList);
if(isNetworkAvailable())
{
if(isFirstTime) {
new LoadDeposits().execute();
isFirstTime = false;
}
}
else {
AlertDialog alertDialog = new AlertDialog.Builder(getActivity()).create();
alertDialog.setTitle("Info");
alertDialog.setMessage("Internet not available, Cross check your internet connectivity and try again");
alertDialog.setIcon(android.R.drawable.ic_dialog_alert);
alertDialog.setButton("OK", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
});
alertDialog.show();
}
}
}
You can create a flag on the fragment - like isLoaded that is set to true when the AsyncTask returns with the data.
Then, in the onCreate method of the fragment, you can use:
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
}
So that the data is preserved when the fragment is paused.
In the post execute callback of your async task, set a field to the data returned.
#Override
protected void onPostExecute(List<Object> result) {
DepositFragement.this.dataResult = result;
refreshAdapter();
}
In the onResume:
#Override
public void onResume() {
super.onResume();
if(this.dataResult != null) {
refreshAdapter();
}
}
void refreshAdapter() {
adapter.data = dataResult;
adapter.notifyDataSetChanged();
}
For each fragment you can override the onSaveInstanceState method and save whatever you want in there.
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
//Save the fragment's state here
}
also you should retrieve it by overriding onActivityCreated.
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
...
if (savedInstanceState != null) {
//Restore the fragment's state here
}
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.savedInstanceState = savedInstance;
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
try {
Log.d("Equals?",savedInstanceState.equals(this.savedInstanceState));
} catch (Exception e) {
Log.d("Equals?",savedInstanceState==this.savedInstanceState);
}
}
Will this always\never or only on some cases log true?
EDIT:
after seeing Henry's comment, lets address my question as if Intent has overriden its equals and it does compare those objects content, not by references...
onCreate(Bundle) called to do initial creation of the fragment.
onActivityCreated(Bundle) tells the fragment that its activity has completed its own Activity.onCreate().
so we can say that Bundle savedInstanceState are same at both places.
for more visit
http://developer.android.com/reference/android/app/Fragment.html
http://developer.android.com/guide/components/fragments.html
you wrote
#Override
public void onCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
this.savedInstanceState = savedInstance;
}
i do not understand which lifecycle mehod is onCreated i think it should be onCreate and the lines should be
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
....}
I have a ListView in my activity.On clicking on the list item it invokes another activity.In that activity I have implemented ViewPager and fragments.
When it loads first time onResume() ,onCreate() and onCreateView() method called twice, if I clicks on first list item. (i.e. it loads first and second fragment view)
when I click on the any other List fragment except first then it calls onResume() ,onCreate() and onCreateView() methods three times (i.e. It loads previous and after and click view )
It is absoutely fine but I have google analytics code with which I have to track only current page so where I can put this code to load for only current page
My question is my googleAnalytics code tracs three or two pages at first time even user doesnot gone through those pages how to avoid this ?
My code is as below for fragment
public class MainListActivity extends Activity{
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.v(TAG, "onCreate()");
CustomFragmentPagerAdapter adapter = new CustomFragmentPagerAdapter();
viewPager.setAdapter(adapter);
}
}
//code for fragment adapter
public class CustomFragmentPagerAdapter extends FragmentPagerAdapter {
public CustomFragmentPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int pos) {
CustomFragment customFragment = new CustomFragment();
arrayList.add(customFragment);
return customFragment;
}
#Override
public int getCount() {
// TODO Auto-generated method stub
return arrayList.size();
}
}
//code for fragment
public class CustomFragment extends Fragment{
public CustomFragment() {
super();
}
#Override
public void onResume() {
super.onResume();
Log.v(TAG, "onCreate -Resume");
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.v(TAG, "onCreate");
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
Log.v(TAG, "onCreateView");
return myAnyView;
}
}
The problem is that the onResume() method is called for all your fragments, that is including the invisible ones.
Check gorn's answer here:
How to determine when Fragment becomes visible in ViewPager
You have to override
#Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (isVisibleToUser) {
// Your logic
}
and do your logic in there.