how to save current state of fragment - android-fragments

in my MainActivity, I have a bottom menu and a FrameLayout.
each bottom loads specific fragment using code bellow:
bottomNavigation.setOnClickMenuListener {
when (it.id) {
1 -> supportFragmentManager.beginTransaction()
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
.replace(R.id.frame, ProfileFragment())
.commit()
2 -> supportFragmentManager.beginTransaction()
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE)
.replace(R.id.frame, MyCalendarFragment())
.commit()
3 -> supportFragmentManager.beginTransaction()
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE)
.replace(R.id.frame, ScoreFragment())
.commit()
}
}
the problem is after traveling to other fragments, old fragment state will be eliminated,
how can I save the state of the current fragment and load it when I come back to this fragment.

I am not too sure if this works out, but cannot comment as my rep isn't high enough. You could create the fragments lazyily and hold a ref:
private val profileFragment: ProfileFragment by lazy {
ProfileFragment()
}
Because at the moment you create a new instance every time you replace the content.
That means your replace would look like this:
supportFragmentManager.beginTransaction()
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
.replace(R.id.frame, profileFragment)
.commit()

Related

RecyclerView does not maintain scroll position on Room database update

I have two fragments, the first with a recyclerView to show all notes from a Room database and a second fragment where notes can be added, edited or deleted in the database. Navigation between the fragments is by JetPack Navigation. My problem is that after editing and saving a note in Room, when I navigate back to the List fragment, the recyclerView does not stay on the edited note. It defaults to the first item in the list. I am not sure how to inform the List fragment if a note was edited or added, and where it would be in the updated list.
In the Update Fragment, I add or edit note, and navigate back to List Fragment.
binding.btnSave.setOnClickListener {
val note = binding.etNote.text.trim().toString()
if (!args.isEditing){
//save a new note
val newNote = Note(0,note,date)
mNoteViewModel.addNote(newNote)
}else{
//save edited note
val updatedNote = Note(noteId,note,date)
mNoteViewModel.updateNote(updatedNote)
}
val action = UpdateDataDirections.actionUpdateDataToNoteList()
findNavController().navigate(action)
In the recycler adapter I have a function to update the note list.
var allNotes = listOf<Note>()
fun setData(notes:List<Note>){
this.allNotes = notes
notifyDataSetChanged()
}
In the List Fragment I have an observer to monitor changes to the database and update the recyclerView. The notes are sorted by date in my Room query. For now, I only need the recycler to scroll to the proper position when a note is edited, but it would also be useful if it could scroll to the position of a newly added note.
val adapter = NoteAdapter(viewClickListener)
val layoutManager = LinearLayoutManager(context)
binding.recycler.layoutManager = layoutManager
binding.recycler.setHasFixedSize(true)
binding.recycler.addItemDecoration(DividerItemDecoration(context,LinearLayoutManager.VERTICAL))
binding.recycler.adapter = adapter
mNoteViewModel.getAllNotes().observe(viewLifecycleOwner, {
it.let{
adapter.setData(it)
}
})

TornadoFX/JavaFX treeview doesnt repopulate correctly after mutating multiple ObservableList

Problem description
I have a treeview (TornadoFX) which gets populated with 3 Observable Lists. If I mutate the 3 lists (first clear them than add new content) the treeview only contains the content of one of the Observable Lists.
The interpretation of my debugging
The treewiev actualises itself in the moment the first List haves its new content and the other 2 are still empty. It does not actualise itself as soon as the other 2 Lists gain their new content.
What I have tried so far
calling treeview.root.children.clear() before I mutate the List to empty the treewiev (same outcome)
calling treeview.refresh() after all Lists have been set (same outcome)
My (more or less pseudo) Code
{//Classes
R extends X
A(val name :String) extends X
B(val name :String, val A :String) extends X
C(val B :String) extends X
}
{//this is how the Lists get their content each time
private val aList = observableListOf<A>()
private val bList = observableListOf<B>()
private val cList = observableListOf<C>()
fun setLists(){
a.clear()
b.clear()
c.clear()
//...
a.add(...)
b.add(...)
c.add(...)
}
}
{
treeview<X> {
root = TreeItem(R)
populate{ parent ->
if (parent == root){
aList
} else if (parent.value is A){
bList.filter { it.A == parent.value.name } //works always
}else if (parent.value is B){
cList.filter { it.B == parent.value.name } //works at first start but not after set setLists() get recalled
}else {
emptyList<X>() //not sure if this is really needed but the compiler needs something here
}
}
}
}
Side Question
Ignore this Question if you want: Intelij is able to run my TornadoFX application (from main or from App with the TornadoFX Plugin), but if I try to export it it doesnt run (neither the jar nor the jnlp). Even if I select TornadoFX and all the other Libraries in the Artifact menu. BUT: if I select Native Bundle -> image in the JavaFX tab, the resulting exe (and only the exe) is working. I also tried to create a shadowJar with com.github.johnrengelman.shadow 5.2.0 Plugin but that also didnt work. So how can I export the Program as a jar (a fatjar including TornadoFX in best case)?
Well I don't know if it's the perfect solution but I simply clear the children and populate the treeview again. It works, but if that is the intended way it's not as "reactive" as I thought it is.

Xamarin Form passing parameters between page keep a hard link to it

I try the new Shell of Xamarin Form 4 for a small project.
I have a list of order, then someone chooses an order and start picking some inventory for this order with barcode. To be simple, I use 2 views and 2 viewmodel.
The process is:
1. User select an order the click a button "pickup"
2. I use ViewModelLocator (TinyIoc) to resolve the correct ViewModel of the pickup view
3. I call Initialize on the new ViewModel and pass some parameters needed. Like a list of items needed to be pickup.
4. Open the view in modal state.
I don't understand that if I change some qty in the list I pass on the second viewmodel, then hit the back button (modal view close). The quantity changed is now in the original viewmodel. How this is possible? I was thinking that passing parameter to a function do not share the same variable but just copy it??
Viewmodel of the first view (look at the Initialize function the List passed and the JobEnCours object)
private async Task GoPickup()
{
Device.BeginInvokeOnMainThread(async () =>
{
if (this.CodeJob != null && this.CodeJob != "")
{
this.IsBusy = true;
PrepChariotSP3ViewModel vm = ViewModelLocator.Resolve<PrepChariotSP3ViewModel>();
await vm.InitializeAsync(this._jobEnCours, this.ComposantesPick.ToList()) ;
await Shell.Current.Navigation.PushAsync(new PrepChariotSP3Page(vm));
this.IsBusy = false;
}
});
}
the the Initialize code on the second Viewmodel (look I set the JobEnCours.Description=Test)
public async Task InitialiseAsync(Job jobEnCours, List<ComposantePick> composantePick)
{
Title = "Ceuillette: " + jobEnCours.CodeProd;
this._jobEnCours = jobEnCours;
this.ComposantesPick = new ItemsChangeObservableCollection<ComposantePick>();
foreach(ComposantePick c in composantePick)
{
this.ComposantesPick.Add(c);
}
jobEnCours.Description = "test";
So, If I do the back button, then in the first VM the JobEnCours.Description is now set to "test" how this is possible?!
Any idea?

unwind segue Xcode 10 swift with navigation controller not working

I have pursued the internet for an answer and following steps in apple documentation but to no avail.
I have two view controllers. One which contains a list of favourite items (FavouriteTableViewController), another which contains the details of the item being added to the list of favourites (ExplorationDetailViewController).
I have a save button on the navigation bar (from a navigation controller scene) in the ExplorationDetailViewController that I want to click to kick off the unwind segue. I have carried out the following steps, but when I click on the save button, it is completely unresponsive. I am including the most relevant code for the unwind.
I have created prepareforsegue function in ExplorationResultViewController from where I will be passing data for an image and a label to the other ViewController as part of the unwind segue
class ExplorationResultViewController: UIViewController {
var exploration_result: Favourites?
#IBOutlet weak var ExplorationLabel: UILabel!
#IBOutlet weak var ExplorationPhoto: UIImageView!
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Pass the selected object to the new view controller.
super.prepare(for: segue, sender: sender)
let photo = ExplorationPhoto.image
let name = ExplorationLabel.text ?? ""
exploration_result = Favourites(name: name, photo: photo)
}
Then in the FavouriteTableViewController, I have added an unwindtoHere method which adds image and label to the list.
class FavouritesTableViewController: UITableViewController {
// MARK: Properties
var favourites = [Favourites]()
// MARK: Actions
#IBAction func unwindToFavouriteList(sender: UIStoryboardSegue) {
if let sourceViewController = sender.source as? ExplorationResultViewController, let favourite = sourceViewController.exploration_result{
// Add a new meal.
let newIndexPath = IndexPath(row: favourites.count, section: 0)
favourites.append(favourite)
tableView.insertRows(at: [newIndexPath], with: .automatic)
}
Then going into my storyboard, I control+drag the Save button in the ExplorationDetailViewController and connect it to the exit item at the top of the scene. My unwind method appears in the pop up as: 'unwindToFavouriteListWithSender'. I select this.
I should be good to go now, but when I run the simulator and click the Save button, it is absolutely unresponsive. See StoryBoard Design
Please help
From your comments, I'm guessing that the problem is that there is no FavouritesTableViewController in the view controller hierarchy at the time that the Save button is pressed. You cannot unwind to something that isn't already present: "unwind" means go back to something that was instantiated earlier and remains present, above the current view controller in the view controller hierarchy.
For example, let's say we have
UINavigationController -> (root view controller) -> FavouritesTableViewController
-> (push) ExplorationResultViewController
Now we can unwind from the Exploration controller to the Favourites controller, because the Favorites controller is already in the hierarchy before the Exploration controller. It sounds like maybe you are not appreciating that fact.

Setting SharedPreference within Fragments as Tabs

I'm having problems with what I thought was a simple use of SharedPreferences!
Aim: I've a ActionBar/Tab application with a MainActivity and 4 Tabs as swipe Fragments. I want the App to remember the last user selected Tab, so that on the next start of the App, it defaults to that Tab.
Code in each Fragment (in the onCreateView method):
SharedPreferences prefs = getActivity().getPreferences(Context.MODE_PRIVATE);
SharedPreferences.Editor editor = prefs.edit().putInt("tabPref", 0);
editor.apply();
where the "tabPref", 0 is the name of the pref and number of the Tab (0-3).
In the MainActivity, I'm using the following to read the preference and set the default tab on starting the App (in onCreate):
SharedPreferences prefs = getPreferences(Context.MODE_PRIVATE);
Integer tabPref = prefs.getInt("tabPref", 99);
mViewPager.setCurrentItem(tabPref, false);
However, I'm getting some really weird preferences being set, that don't conform to the selection of the tab (e.g. Tab 4 doesn't even set the preference, and Tabs 1-3 randomly set Integers 0, 1, 2 or 3 - I'm using System.Outs to see what's happening).
Am I missing something fundamental regarding the life cycle of the fragments that isn't properly setting the SharedPreference?
Thanks....
Solved: I needed to set the SharedPreferences in the OnPageChangeListener class within MainActivity, not in the Fragments (doh!).
mViewPager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
#Override
public void onPageSelected(int position) {
actionBar.setSelectedNavigationItem(position);
SharedPreferences.Editor editor = prefs.edit().putInt("tabPref", position);
editor.apply();
}
});

Resources