I have several fragments in my xml (4 of them). The first time I run the activity with this code:
private void loadSenderFragment(int sender_fragment) {
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
// TODO: Animation for later
if (loadRunOnce) {
//ft.setCustomAnimations(R.anim.fade_in, R.anim.fade_out, R.anim.fade_in, R.anim.fade_out);
}
switch (sender_fragment) {
case FILES_TO_SEND_FRAGMENT:
Log.i(StaticValues.TAG, "files to send fragment visisble");
ft.hide(fragmentSendDev);
ft.show(fragmentFilesSend);
break;
case SEND_TO_FRAGMENT:
Log.i(StaticValues.TAG, "hiding filesSend, loading senddev");
ft.hide(fragmentFilesSend);
ft.show(fragmentSendDev);
break;
}
if (loadRunOnce)
ft.addToBackStack(null);
else {
ft.hide(fragmentReceiveWait);
ft.hide(fragmentReceiving);
loadRunOnce = true;
}
ft.commit();
}
It shows the proper view (Files_to_send_fragment), then from that fragment via a callback I call loadSenderFragment again except this time with the case of SEND_TO_FRAGMENT.
I know that this gets called because my log : hiding fileSend, loading senddev shows up on my logcat and the ft.addToBackStack works as well because pressing theback button does not cancel the activity this is in . But the layout from fragmentFilesSend keeps showing while the other does not not(a page with a white background currently).
From what I understand from my code and my intention is, hide filessend and show senddev.
Does anyone know why that hide/show might not be working.
BTW I am using the compatibility library. (also tried the regular api Honeycomb+ library and still nothing).
I have figured out why this happens. For some reason if you use a style that has no window background, even though you hide/show different fragments all of them will be drawn (for some reason I have no idea why). So my style which I added a
<item name="windowBackground">#null</item>
in order to reduce how many pixels are drawn, was the thing that was blocking me. Who knew.
You have to add these fragments to the View they are meant to be displayed in before calling hide() and show() functions. Just initialize them beforehand.
Related
The scenario is this...I click on a button that loads the next fragment. While the animation is still going I hit the recent apps button. After the recent apps screen is shown i return to my app.
Then I see the two fragments drawn on top of each other. As if Android forgot to remove the previous fragment.
I click back to remove all fragments until I reach the first fragment loaded in this FragmentActivity. I log all the fragments I get from FragmentManager and it only shows one. But I still see two fragments drawn one on top of the other. The one that should have been removed is not responding to touch events and the other one responds as it should.
Is this an Android bug or my fault? Is there any way to fix this or prevent it from happening ?
It does not happen every time. This is how I load my fragments
public void loadNewFragment(AnimFragment newFragment, boolean addToSTack, boolean animate, String tag) {
if (newFragment != null) {
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.setCustomAnimations(R.anim.fragment_enter_from_right, R.anim.fragment_exit_to_left, R.anim.fragment_enter_from_left, R.anim.fragment_exit_to_right);
transaction.replace(R.id.fragment_container, newFragment, tag);
if (addToSTack) {
transaction.addToBackStack(tag);
}
int transactionId = transaction.commit();
newFragment.setTransactionId(transactionId);
}
}
and I remove them on back pressed with
getSupportFragmentManager().popBackStackImmediate();
In my opinion "below" fragment is onPause() state and it's still alive and visible.
Try to use white background on the root layout of the front fragment to avoid showing the below fragment. Moreover the root layout size (width and height) of the front fragment should be match_parent.
Check this site to understand Fragment lifecycle.
https://google-developer-training.gitbooks.io/android-developer-advanced-course-concepts/unit-1-expand-the-user-experience/lesson-1-fragments/1-2-c-fragment-lifecycle-and-communications/1-2-c-fragment-lifecycle-and-communications.html
When I start my app it runs an AsyncTask to load up and then in onPostExecute, I then setContentView to the new layout then add a fragment with two buttons offering two modes by an add FragmentTransaction. After one of the two modes is clicked, it then replaces the fragment with yet another FragmentTransaction using the replace method.
If the app crashes it returns to the first screen, loading up the two buttons offering the two modes. In this case if either mode is selected, the second fragment is loaded but is now the background is suddenly transparent showing the two buttons below and they remain clickable. If they are clicked again they properly replace the fragment so that it isn't visible below. This is just weird, I can't understand what could cause this.
I've researched and seen these two similar questions, one and two, which suggested that it might be because the ID is wrong or I have defined the fragment in XML. Neither of these two factors are the case.
My code is shown below:
Below I replace the loading screen.
#Override
protected void onPostExecute(Void result) {
setContentView(R.layout.activity_main_screen);
FragmentTransaction transaction = getSupportFragmentManager()
.beginTransaction();
transaction.add(R.id.fragment_container, new ModeFragment())
.commit();
}
After which, when a button is clicked I pass the fragment I wish to replace the current with into this method below:
private void replaceCurrentFragment(Fragment fragment) {
FragmentTransaction transaction = getSupportFragmentManager()
.beginTransaction();
transaction.replace(R.id.fragment_container, fragment)
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
.addToBackStack(null).commit();
}
This works the first time, however if a crash occurs then the app returns to the first fragment and the second time this method is passed, the new replacing fragment is semi-invisible. Clicking the button on the first fragment again calls this method again and it is now fine.
Obviously I don't want the app to crash so this shouldn't occur, but I get this feeling that there's something wrong with how I'm writing my code.
I've had the same problem happen to me, and it was because I loaded a fragment in the OnCreate of my Activity, without checking if there was a savedInstanceState, so android first reopen all old fragments, then do the OnCreate, which added the fragment over the old ones without replacing them so when you navigate to another fragment, it only replaces the top one, but not the bottom one, so you will see the fragments under it.
Might not be exactly the same thing for you, but it might help you figure it out.
If it helps, what I want is similar to what is done in this google tutorial
But there a fragment is created prior to the transition. If I do that the transition works fine; but I can't use this approach
=====
Aiming to API 7+ I am just trying to have one Fragment visible in the whole screen and using a button (a drawn button, with an onTouch event) then alternate to a second fragment and viceversa.
But I either get a blank screen when I replace the first fragment with the second, or if I use fragmentTransaction.show and fragmentTransaction.hide; I can switch two times before I get blank screen. I dont want to have on backstack.
I am creating the fragments in MainActivity's onCreate:
DiceTable diceTable = new DiceTable();
Logger logger = new Logger();
fragmentTransaction.add(diceTable, DICETABLE_TAG);
fragmentTransaction.add(logger, LOGGER_TAG);
fragmentTransaction.add(R.id.fragment_container, logger);
fragmentTransaction.add(R.id.fragment_container, diceTable);
Then on one method (called from the fragments) I do the switch:
Logger logger = (Logger)fragmentManager.findFragmentByTag(LOGGER_TAG);
DiceTable diceTable = (DiceTable)fragmentManager.findFragmentByTag(DICETABLE_TAG);
if (diceTable.isVisible()) {
fragmentTransaction.replace(R.id.fragment_container, logger);
fragmentTransaction.commit();
fragmentTransaction.hide(diceTable);
fragmentTransaction.show(logger);
}
else if (logger.isVisible()) {
fragmentTransaction.replace(R.id.fragment_container, diceTable);
fragmentTransaction.commit();
fragmentTransaction.hide(logger);
fragmentTransaction.show(diceTable);
}
This is not how I should do this?
Blank screen when replacing fragments
Try to initialize fragments in that way:
private void initFragments() {
mDiceTable = new DiceTable();
mLogger = new Logger();
isDiceTableVisible = true;
FragmentManager fm = getSupportFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
ft.add(R.id.fragment_container, mDiceTable);
ft.add(R.id.fragment_container, mLogger);
ft.hide(mLogger);
ft.commit();
}
And then flip between them in that way:
private void flipFragments() {
FragmentManager fm = getSupportFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
if (isDiceTableVisible) {
ft.hide(mDiceTable);
ft.show(mLogger);
} else {
ft.hide(mLogger);
ft.show(mDiceTable);
}
ft.commit();
isDiceTableVisible = !isDiceTableVisible;
}
You are combining two different methods of changing which Fragment is shown:
Calling replace() to replace the contents of the container with a different Fragment
Calling hide() to remove a Fragment, then calling show() to show another Fragment.
Pick one method and stick with it. The Building a Flexible UI guide uses just the replace() method, so I would start by trying to remove all of your calls to show() and hide().
Also see Android Fragments: When to use hide/show or add/remove/replace? for a quick summary of when it might be beneficial to use hide/show instead of replace.
In case anyone has tried all of the already suggested answers and is still facing the issue, like I was a few moments ago, the problem may come from a different source than your fragments.
In my case, I was switching between fragments correctly, but the reason for the blank screen was that in each new fragment I was trying to asynchronously load images by starting a new Thread every time (something a bit like this) instead of the recommended AsyncTask, or better yet the newSingleThreadExecutor, since AsyncTask is deprecated.
I disturbed the background enough that the fragment was just not showing up unless I navigated to a different app then back.
So unless your mistake is similar to mine my suggestion might be kind of vague but try to see if anything is happening in your fragment that may be intensive on the resources (commenting out different pieces of code may help in investigating this).
Since I had to do a custom menu for my application, I've made a fragment which act like an actionbar at the bottom of my app!
But I have some conflicts with the stack (when pressing the back button).
How should I program the following method?
public void changerContenu(int frameLayout, Fragment frag, boolean ajouterAuStack)
{
if(R.id.frameContent != frag.getId())
// * if the current displaying
// fragment in the layout is the same as the one that I received in param#2 (frag),
// then don't do anything.
{
ft = fragmentManager.beginTransaction();
ft.replace(frameLayout, frag);
if(ajouterAuStack)
ft.addToBackStack(null);
ft.commit();
}
}
I know my "if" doesn't make any sense right now, trying to figure out how to come to the desired result :( Right now, when I visit the fragment once (and it's pushed into the stack) I cannot revisit it using my button in my "home made actionbar" until I hit the back button a few times to clean the stack.
I only want to not duplicate items in my stack if I click few times the same button on my menu...
Thanks guys!
I want to understand the question better.
You have a fragment that acts like an action bar. If the fragment is already displaying, you don't want to do anything; otherwise, you want to display it. In what circumstances do you want to go Back, and what should happen?
As a note, a single actionbar at the bottom doesn't fit in with the typical Android design. Users may not understand how to use your app. I'm interested in knowing why the regular ActionBar doesn't work for you.
For future reference, I got it working that way:
public void changerContenu(int frameLayout, Fragment frag, boolean ajouterAuStack, String tag)
{
ft = fragmentManager.beginTransaction();
ft.replace(frameLayout, frag);
if(ajouterAuStack && (fragmentManager.getBackStackEntryCount() == 0
|| fragmentManager.getBackStackEntryAt(fragmentManager.getBackStackEntryCount()-1).getName() != tag))
ft.addToBackStack(tag);
ft.commit();
}
I call this method to go forward from AFrag to BFrag:
showFragment()
{
FragmentTransaction fragmentTransaction = mFragmentMgr.beginTransaction();
// Add fragment to the container ContentView
fragmentTransaction.replace(R.id.operation_fragments_frame, mBFrag, mBFrag.getTag());
// Add FADE effect
fragmentTransaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
// Keep the transaction in the back stack so it will be reversed when backbutton is pressed
fragmentTransaction.addToBackStack(null);
// Commit transaction
fragmentTransaction.commit();
}
It shows a new fragment (BFrag), replace the previous one (AFrag) and keep info about the transaction, so it can be reversed/undone automatically on back button pressed.
When back button is pressed everything looks fine, the previous fragment is shown (AFrag). But when I go forward again (AFrag -> BFrag) I got a "Fragment already added exception".
Didn't the reverse/undone operation remove the new fragment (BFrag)? Is this the expected behaviour?
That's weird because after this, I decided to set a check:
if(mBFrag.isAdded())
{
fragmentTransaction.show(mBFrag);
}
else
{
fragmentTransaction.replace(R.id.operation_fragments_frame, mBFrag, mBFrag.getTag());
}
and stills, it gets into the else statement... and I get the exception.
Any insight on what am I doing wrong, please?
Thx.
have you tried to use an other method, like remove(), then do an add(). or anything similar? I saw on some other post that the replace() method does not always behave correctly.