I'm making an app with a lot of user input from edittexts stored in a Room database with an attached TextWatcher. When a fragment is opened, I want to repopulate the EditTexts with the values that have previously been stored. In order to do this, I am currently observing LiveData from the Database once, and removing the observer once the EditText is re-populated.
This works well the first time I open each fragment. The problem that I'm having is that if I make a quick change in one fragment -- deleting the text, for instance -- and navigate away, then back again, the EditText re-populates with old data instead of the newest change.
I know from the Database Inspector that the new value is being stored, but when the EditTexts are re-populated, the TextWatcher causes the old value to be stored in the database again, overwriting the new value. So I'm guessing that the issue is a delay in the Database updating the LiveData, and the EditTexts being re-populated before the data is actually returned from the Database.
Am I going about this in the wrong way? Should I be using SavedInstanceState instead, and somehow only using LiveData on the first load? I'm not currently using a repository -- I'm teaching myself Kotlin and haven't learned about them yet. Would that solve this issue? I have a lot of fragments and a lot of EditTexts, so I'm trying to find the cleanest way to deal with this.
I hope this is clear. Thanks for taking the time to answer!
Related
I'm practice in using Navigation component. Now i have a trouble. I'm using Room database and i'm update it in my Fragment multiple times. Somehow it's anyway taking my last result no matter that i'm updating it so i decided that i need to update my fragment but i don't know how. I'm already found solutions like to detach and then attach fragment but i don't know how to do it because in my MainActivity i'm attaching to my fragment through "setupActionBarWithNavController".
I think you are overcomplicating matters. If you are using Room to manage your data, and the values in the database are getting updated by your code, then you can simply use LiveData. You don't need to reload your fragment at all.
Have your Room DAO query return a LiveData<MyType> instead of just a MyType object (for example). You can then "observe" the LiveData in your Fragment, and write a callback function to update your UI every time the value returned by the query changes. See this page for more info.
This basically means that when you tell your model to updateInsulin, once the database value is changed, the LiveData watching your insulin level will change and trigger your Observer. The Observer can then do something like change the text of a TextView or naviagte to a different Fragment.
I have 3 fragments and a bottom navigation menu to switch between them.
In every fragment i have a recyclerview to display data gathered from FirebaseDatabase.
My question is: Should i use Replace() fragment? As it would result in restarting the fragment and requesting again Firebase SingleEventValue every time the user navigate between fragments
Or i must use Add Show Hide? Or that would lead in memory leaks?
The methods you use to deal with the fragments are mostly not relevant. The Firebase SDK will cache data previously fetched, so if you make a second request for some data, from any point in your app, you will be seeing cached data, and it will even work offline. There are no "leaks" involved here when using single value events.
Adding a listener to a database location is a different matter. Ideally, listeners should be attached when a fragment becomes visible, then detached when not visible.
I have a fragment, which contains many EditTexts.
And when I rotate the device the EditText goes blank. The fragment is not saving its state/value. But at the same time if I use that fragment layout for activity it stores the EditText's state.
And yes I've given the IDs to each EditText, even to each view if that matters.
I know I can use saveInstanceState to save those values but is there any other way to do it? Cause there are almost 20 EdiText in that fragment, so should I use saveInstanceState, will it be okay to save these many variables/values in saveInstanceState?
Update:
I was recreating the fragment in activity on orientation change, so that was the reason, EditText was unable to save its state.
Such a silly mistake!
So now I just used saveInstanceState like following:
if (savedInstanceState == null) {
initialiseNewTaskFragment();
}
And that's it. EditText is saving its state now.
Thank you JorgeGil for saving my time!
Well this is a fairly loaded question lol.
So let's start with a few points.
First, you can of course handle your own lifecycle change if you choose to NOT allow Android to reset your lifecycle on device rotate.
android:configChanges="orientation"
Use that flag in your manifest if you want to retain everything and handle your own rotation changes. However, if you have a layout-land folder with different XML files, you will not want to do this.
Yes of course you can do fragment.retainInstance when nested in Activities to get it to retain values. However, retaining populated Elements with values is not something you can just natively expect it to do as the UI elements were completely redrawn, so something has to tell it to redraw it again.
So if you are going old school and you are actually still doing findViewById and myText.setText('some Stuff'). Then you may find some time savings in using a library like icepick.
https://github.com/frankiesardo/icepick
However, the BEST option by a mile is to modernize your coding practice to use DataBinding. This allows you to not care about the UI interactions anymore as the values are bound to your Fragment or Activity or Model values by default and can be done with 2-way binding. This ensures databinding always populates with the value that was last updated.
Imagine you have an object of
public class Student implements BaseObservable{
String firstName;
}
Then in your xml you have
editText
android:text="#={student.firstName}"
Obviously there is a little more, like you need to set your student object in the onCreate to ensure it is in the XML for using. But when the user modifies the student firstname it is retained in the model, and redrawn automatically into the Edit Text.
This is your best solution, but depends on how invested you are in the future binding techniques of Android development or if you just prefer to go status quo to get across a finish line.
Hope that helps.
Add this in your Activity tag on your AndroidManifest.xml to avoid the recreation of the Activity and you won't lose the data.
android:configChanges="orientation|screenSize"
This is a practice I've been using for a while now, but it seem to be deprecated, the literature seems to say that the correct way to save and restore data when the screen goes to the background or rotates is fragments.
I would like your opinion on this
What I've been doing in my apps is create a class I call ApplicationDataHolder()
This has all the variables that define the state of each activity and fragment stored in it.
For example I have an activity that shows a list of tickets and two widgets one for the way to sort the tickets and one to select if it will be ascending or descending.
For this I have created the variables List _tickets, SortOrder _order and boolean _ascending in my DataHolder() and given them default values
Whenever the activity is recreated/created for the first time, I access those variables to set default values (what the default sort order will be, what the initial list will be)
Is this not the optimal way? could this cause problems (for example after the screen has rotated too many times) what is the benefit of using fragments or saveinstancestate/restoreinstancestate over this?
Thanks in advance for any help you can provide
the correct way to save and restore data when the screen goes to the
background or rotates is fragments
Here they are talking about data that is obtained dynamically, either as input data from the user or data coming from a sensor or web-service. This data needs to be restored using onSaveInstanceState() and onConfigurationChanged() when a state change occurs, such as rotation or a tab swipe.
Initial values can of course be saved in a central global constants file, nothing wrong with that.
I am currently working on an app which takes a number of user entries. I want to have each EditText field on its own page, and instead of having a seperate activity for each entry, I wanted instead to call the same activity again. Is this possible, and if so, is it a feasible solution? Thanks.
It is possible but I don't think it is the way to go. Basically if the next input is a separate action then it deserves its own activity.
That is the way you are supposed to do it.
You could store the gathered values either in the Application class as a temporary storage or you can save it using SharedPreference. However if it is only temporary data I advice you to use the Application class rather than writing it to a file.
I would think that if your UI doesn't change (significantly) between views, then reusing your activity and displaying different data seems fine to me (I do this myself).
I keep an object on the Application class that contains a list of the sub-objects (Inputs in your case).
On the top level object, I keep the index of the current index.
This works very well, does not leak memory and is very fast to render as I swipe through my pages.