The first time my android app is launched I bring up a preferencefragment because the user needs to initially make a selection from a list of items and their selection needs to be saved. I extend ListPreference to display the list of items.
The way it should work is the user selects an item from the presented list. This selection
gets saved in SharedPreferences and the app transitions to another fragment. All of this is
working, but for some reason the ListPrefence gets display a second time.
I've put in some logging and discovered that for some reason two ListPreference object are
being constructed...but I know of only one call to create it. I'm somehow missing where/why
the ListPreference constructor is called a second time.
My PreferenceFragment code is simple. It looks like
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.i("INIT", "ONCREATE");
addPreferencesFromResource(R.xml.inititems); // load from XML
There's only the addPreferencesFromResource() call. No other place in my fragment code
should (as far as I understand) cause the ListPreference to be instancitated, but
the logging tells me the ListPreference constructor is called twice and the fragment's
onCreate is called just once.
I'm going to try to do a stack trace from the ListPreference constructor, but I wanted to
see if anyone here has thoughts or suggestions beyond the stack trace. Why would the
constructor be called twice ?
Thanks!
-Mar
You possibly have referenced the fragment in your layout resource file using android:name="yourfragment" as well as at run-time in onCreate(). That would cause it to be loaded twice.
Related
I am just trying to change the value of a Preference when the PreferenceActivity has been opened. As there is no "setValue" or similar on a Preference, I try
My code:
long value = System.currentTimeMillis()/1000;
PreferenceManager.getDefaultSharedPreferences(getActivity()).edit().putString("test",""+value).apply();
getPreferenceScreen().findPreference("test").setSummary(""+value);
My XML:
<EditTextPreference
android:key="test" />
What I expect:
When clicking on my Preference, it should display the value of time (same than summary) and let me edit it.
What happen:
The value is only changed after I closed the Activity. Next time I open the screen, the value is correct (but as in fact already changed to the next one)
First attempt:
Let s say value is 1521143527. Correctly written in the summary, but when I click on the Preference, the popup display an empty value.
Second attemp:
Summary has changed to 1521143540. When I click on Preference, I can edit the previous value (1521143527)
Third attempt:
New Summary, but Preference value is not changed and is still: 1521143540
etc...
Any idea what is wrong?
DIRTY WORKAROUND:
setPreferenceScreen(null);
addPreferencesFromResource(R.xml.preferences);
Will now force the preference to update, but that's really dirty, and I still don't understand...
If you look into the PreferenceFragment source code ,you can see that there is a method called bindPreferences() which binds the preference values to Views . Only in 2 scenarios this method is called,
When Activity created onActivityCreated(#Nullable Bundle savedInstanceState)
When addPreferencesFromResource() called. there is a handler which triggers the bindPreferences()
Other than this there is no way the views are updated. bindPreferences() is a private method , so you can't call this method outside of the class. So you should update your preferences before either those events.
You mentioned, As a workaround solution, you should update your preference first then call addPreferencesFromResource() . Like below
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
long value = System.currentTimeMillis()/1000;
getPreferenceManager().getSharedPreferences().edit().putString("test",""+value).commit();
addPreferencesFromResource(R.xml.pref_general);
getPreferenceScreen().findPreference("test").setSummary(""+value);
}
In my activity I have several fullscreen fragments, each of them downloads some data from web (using an async task) and shows them to the user. The fragments are showed one at a time.
To be more specific, each of the fragment readings some urls from a sqlite database, and fetch the content before showing them in a list, if that matters. The data loading tasks can be done in the OnCreate() function.
I would like to preload all the fragment (at least starting the downloading), when I show a splash screen. Pretty much like a viewpager preload its fragments.
I am wondering how to achieve this? I tried initialize/create all the fragments in the OnCreate() function of my activity, hoping the OnCreate() of fragments could be called earlier, but the OnCreate() and OnCreateView() function of the fragments are not called until a fragment is about to show to the user.
It sounds like you need to separate your model (the data which is downloaded) from your view (the fragments). One way to do this is to start the downloading AsyncTasks in your activity, rather than starting them in each fragment. Then when the fragments are eventually displayed they can show the data which has been downloaded (or a spinner or some other indication that the download process is still executing).
Fragment's onActivityCreated(Bundle) tells the fragment that its activity has completed its own Activity.onCreate().
So your solution to this problem is initialize or create or do your stuffs which you want to preload before fragments are created, inside your Fragment's onActivityCreated(Bundle)
see documents for fragment's lifecyle
The earliest pace you can start loading is either in a static singleton or in the Application Class
What I end up doing is the following, (1) add all the fragments into the container. So they (and their view) will be created and initialized. (2) hide those not in use and only show the one I would like the user to see. (3) use FragmentTrasaction.show()/FragmentTrasaction.hide() to manipulate the visibility instead of FragmentTrasaction.add() or FragmentTrasaction.replace().
If you following this approach, be warn that all the fragments will be cached in memory. But the benefit is the switch between fragment will be fast and efficient.
I was facing the same problem and then I used this method, suppose we are having an EditText in the fragment, then we can use codes like this
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
//this method allows you to input or instantiate fragments before showing this to an activity conidering id is "editTextEditProfileFirstName"
EditText firstName = (EditText) getActivity().findViewById(R.id.editTextEditProfileFirstName);
firstName.setText("This is my first name", TextView.BufferType.EDITABLE);
super.onViewCreated(view, savedInstanceState);
}
I have a detected a bug in my app today : When a user clic on an item in my listview, it launch a new fragment with details about this item. But when the user is in this fragment, if he change orientation of the device, the onCreate method of my first class (which fetch my listview) is called, and the app crashed.
I would like to know if it's possible to get the name of the previous fragment, in order to add a test like the following in my onCreate method :
if (!fragmentName.equals("common")){
Log.d("INFO", "Do nothing in this case, because user was in detail fragment before !"),
} else {
super.onCreate(savedInstanceState);
refreshList(true);
}
I don't think it is possible to get the name of the previous fragment as its creating a new instance of your Fragment object.
If I am right in what you are trying to do you need to somehow store the object for which you are displaying data so that when the Fragment is recreated you can then retrieve the required object. One way is you keep the ID or a unique property of the object in SharedPreferences and then reacquire its data when it reloads from a singleton somewhere.
Basically you may need to change the way you retrieve your data or pass it from the original opening Fragment so that you can retrieve it again if the second Fragment is destroyed.
I am inflating a view on button click and the user can add as many views as he likes, all is fine I made it work, but now the problem is when I go back one activity and come again to my dynamically generated activity every single view that was generated is gone. Similar is the case if I go to next activity and come back to the inflated activity. I know about onSaveInstance and onRestoreSaveInstance. But how do I put view information in a bundle in onSaveInstanceState? Please note that my view was generated Dynamically i.e. on button Click and I want to know as of how to preserve the state of my activity.
How do you go about it?
I am thinking that you should implement some kind of logic that helps you restore the state of your Views. So you should be designing a class, let say ViewDetail that somehow keeps details about the Views that you are adding.... type, dimension, etc. This class should implement Parcelable so you are able to add it to the bundle.
So you will keep an ArrayList<ViewDetail>, myViews where everytime the user adds a new View you create a new ViewDetail object that you add to your myViews array.
And then save your Views and restore them using those objects:
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
//save your view states
outState.putParcelableArrayList("MY_VIEWS",myViews);
}
#Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
//get the views back...
myViews=savedInstanceState.getParcelableArrayList("MY_VIEWS");
//TODO: add the views back to your Activity
}
As your application may be killed completely at any moment without noticem you have to provide long term storage off heap memory
You only have to restore all the views, if your activity was terminated (and it can be at any time). When it is activated again after termination, it goes through onCreate() method
- this would be proper place to restore activity state.
Only callback which is guaranted to be called before your application / activity is destroyed is onPause() - this is a proper place to save views states into long term off-heap storage.
I have set a value in spinner , from activity-1.
now i am traversing to activity-2 and again coming back to activity-1, i am not getting the updated spinner value which i have selected previously but i am getting default value(value at index 1) of spinner .
An Activity that becomes inactive (invisible to a user) can be destroyed by the system in case of lacking resources. To keep values between Activity runs you need to save your state using Bundles. If you look closely to Activity::onCreate method you can see it has a parameter:
protected void onCreate (Bundle savedInstanceState)
So, eg in method onPause() you save your desired values and when Activity is recreated - load them.
Android documentation contains a chapter how to save states between Activity runs.