I am stuck with this problem from few hours now.
I have a Android App, There is Activity A. This activity has bottom navigation drawer, that uses view pager, I have 3 views, account, contact and notes.
On each view you can see list of items, as in contacts you can see list of contacts added, on notes you can see list of notes added and so on.
There is fab icon on every fragment that is basically add/edit button, when clicked it opens a dialogfragment which is used for editing or adding contacts or notes.
My problem is that I can only open my Edit dialog for Contact and notes one time. Once you rotate the screen 2-3 times, I get the error .. Fragment ContactDialogXXXX has not been attached yet.
fun editContactDialog(contact: Contact?) {
val transaction = childFragmentManager.beginTransaction()
val previous = childFragmentManager.findFragmentByTag(ContactEditDialog.name)
if (previous != null) {
transaction.remove(previous)
}
val dialog = ContactEditDialog.getInstance(contact, accountId)
dialog.show(transaction, ContactEditDialog.name)
}
I am using this above method to open dialog everytime. and only gives me error that Fragment XX has not been attached yet. after rotation. I have even done below thing from given links with no luck.
set retainInstance to true in onCreate
also added this snippet
override fun onDetach() {
super.onDetach()
callbacks = null
}
I did add
if (!isAdded)
return
but adding above line in that method only stops the crash but my dialog does not open then.
I have searched everywhere online, looked into all possible combinations on stackoverflow but my problem persists.
tried solutions from below places:
Passing an Object to Fragment or DialogFragment upon Instantiation
https://www.androiddesignpatterns.com/2013/04/retaining-objects-across-config-changes.html
https://guides.codepath.com/android/using-dialogfragment#styling-dialogs
What am I missing? Is there another way of doing it? A better way of creating error prone Custom Dialog Fragments?
Related
I'm handling Login and Logout Logic in a Fragment. I want the fragment to refresh itself when the user logs in or logs out. I'm using Navigation Component for my Fragment Navigation.
I've tried several ways but none of them are working. I'm trying the following code:
val fragManager = this#WelcomeFragment.parentFragmentManager
fragManager.beginTransaction().detach(this).attach(this).commit()
However, this code does nothing.
I've also tried doing the following:
val act = this.requireActivity() as MainActivity
val fragmentTransaction =
act.supportFragmentManager.beginTransaction()
fragmentTransaction.setReorderingAllowed(true)
fragmentTransaction.detach(this).attach(this)
Though this crashes my App. How can I refresh/reload a currently displayed fragment? Thanks.
Hi folks I need to throw up a series of DialogFragments one after another using the navigation component. I have encountered some pretty unusual system behavior which looks like a race condition. I show the dialogs with a global action after an item is clicked, and use the fragment result api to determine if another one should be shown.
I am using a custom layout so there's no positive / negative listeners etc., and my own continue / cancel buttons send a result back to the host fragment.
ItemsFragment.kt:
navController.navigate(RegisterItemsDirections.openModsDialog(item.id, 0))
fragment.setFragmentResultListener(ItemsFragment.MODIFIERS_REQUEST) { key, bundle ->
//kill the current dialog
navController.navigateUp()
//some logic to determine if we need another dialog...
if(needAnotherDialog){
//navigate to the next one
navController.navigate(RegisterItemsDirections.openModsDialog(item.id, lastModGroupSelectionIndex + 1))
}
}
ModsDialogFragment.kt, when the user clicks "continue"
setFragmentResult(MODIFIERS_REQUEST, bundleOf(MODIFIERS_RESULT to selectedMods))
So the issue only appears on 3rd or more dialogs on my devices, I can see that the 1st and 2nd dialogs are completely destroyed and detached. When it displays the 3rd one, the first one attaches itself again, and appears beneath the 3rd one which I can't explain.
I've tried:
Popping the back stack in the global action's nav xml
Navigating up or dismissing the dialog fragment in the dialog itself (before calling setFragmentResult), which is the most logical place to put it
Popping the backstack instead of navigating up, basically the same thing in this case
When I don't dismiss / nav up any of the dialogs and allow them all to just stack, there's no issue. When I delay the navigation by 500ms there is no issue. So navving up and then immediately navigating to another instance of the dialog are fighting with eachother producing very strange results. What could be the solution here that doesn't involve a random delay?
Navigation version is 2.3.3 and I'm having a lot of trouble trying to update it due to AGP upgrades being incompatible with a jar I need so I'm not sure if this has been fixed.
I figured out the issue, it's down to dumb copy pasting.
I took the donut tracker sample code and made it stack dialogs in the same way and there was no issue.
The difference between the two was I am subclassing DialogFragment and for some unknown reason doing this:
override fun show(manager: FragmentManager, tag: String?) {
val transaction = manager.beginTransaction()
val prevFragment = manager.findFragmentByTag(tag)
if (prevFragment != null) {
transaction.remove(prevFragment)
}
transaction.addToBackStack(null)
show(transaction, tag)
}
This code predates the Nav component library I believe, and it completely f***s with the fragment manager used by the navigation component. So the moral of the story is to not do bizarre things in super classes, or better yet not super class at all.
I have multiple fragment, one bottom navigation view on bottom of Main Activity. The problem appears when I want to set selected item on Bottom Navigation View when fragment pop back from stack, setSelectedItemId always trigger OnBackStackChangedListener, thus make loop event. Here are the code
fragmentManager.addOnBackStackChangedListener {
var f : Fragment = fragmentManager.findFragmentById(R.id.frame)
if(f is HomeFragment){
bottomNavigation.selectedItemID = R.id.navigation_home
}
}
I've check from the documentation at developer.android.com and various post on StackOverflow or even forum and I don't find any proper solution for my case.
Any solution? thanks
I have an issue with showing DialogFragment inside Fragment. It does not matter what I try, I cannot get it to work everytime.
My layout consist of activity with navigation drawer and inside activity I am showing fragments. On those fragments I must show DialogFragment but for that, I must get context/application context/activity/whatever (depends on current year and weather) to show it.
In most cases it works ok but sometimes, even if fragment is constantly showed to user (no configuration changes or anything) it sometimes happen that fragment is not attached. I am getting that error in production as well so it is not just my device.
Good practice for that would be to check with method isAdded() and if activity is not attached then do nothing. I am not sure why this is good practice because users would not be happy if they loose their work since "save dialog" will not be shown just because Google is giving good practices as terrible ones!
So far I have this (among million combinations) inside my fragment:
private void showStoreDialog() {
if (someConditionIsOk) {
StringBuilder sb = new StringBuilder();
sb.append(getString(R.string.someText));
sb.append(MINIMUM_LENGTH);
sb.append(getString(R.string.someText2));
Toast.makeText(getActivity(), sb.toString(), Toast.LENGTH_LONG).show();
return;
}
...
Exception occures on this line: sb.append(getString(R.string.someText));
Cause: android.support.v4.app.Fragment.requireContext (Fragment.java:614)
android.support.v4.app.Fragment.getResources (Fragment.java:678)
android.support.v4.app.Fragment.getString (Fragment.java:700)
solutions.lunalabs.gpsracer.fragments.RecordingFragment.showStoreDialoglog (RecordingFragment.java:152)
I know I could solve this by routing through activity with callbacks but I do not want this because I should route all dialogs through activity even if it is needed only in one fragment. Is there a safe solution to this?
Thank you!
I have implemented a FAB's setOnClickListener() in MainActivity and it is working without any issue.
I have 8 different Fragments (one shows a WebView, anoother loads a RecyclerView, etc..) based on the selection from the navigation drawer.
The FAB appears and its current action is playing a default song, which is working fine.
Now, from my 8 different Fragments, 3 Fragments show a different song list.
Since the FAB appears as default, I want to use the FAB as a play Button.
So, if the user is in one Fragment and selects a mp3 and clicks the FAB, it should play that song.
That means that I should somehow overwrite MainActivitiy's setOnClickListener with one implemented in the Fragment.
In the same way, I have other 2 Fragments which show some mp3 song list. By selecting an mp3 in that ones and clicking the FAB, the Fragment should listen for that FAB action.
Basically, the FAB should override its action and take inputs from the current Fragment.
One way I can think of is when fab.setOnClickListener is called, I will check the current Fragment and act according to that.
But the problem is to play the song, I am creating an Intent with many parameters and calling startservice.
If I can do something in the Fragment itself, I can create an Intent, bundle the required arguments etc.. on setOnClickListener().
Any suggestions to resolve my confusions is highly appreciated.
I tried below way and it is working fine. Adding it as answer to my question since it is working.
Approach:
Implemented a function with common name playAudio() in all fragments where media suppose to play. In MainActivity, onClick() of floating action button, I am checking the instance of current fragment. If it is among 3 of 8 which needs to play respective audio when fab click, i am calling the fragment's playAudio().
for rest of fragments, doing something else.
Below is code snip:
#Override
onClick(View v) {
Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.container_body);
if (fragment != null && fragment.isMenuVisible()) {
if (fragment instanceof FragmentAudioMessage) {
((FragmentAudioMessage) fragment).initPlay();
} else {
//do something else.
}
}
This way it is working differently for each fragment.
Hope this will help others.