Fragment shared element transition with add() instead of replace()? - android

I am trying to make a shared element transition between fragments, everything works fine when using replace() to add the second fragment, however in the codebase add() is used a lot, but when using that, transition just skips to end values
Is it possible to have the transition between added fragments?
Thanks
#Override
public void onClick(View v) {
setSharedElementReturnTransition(TransitionInflater.from(getActivity())
.inflateTransition(android.R.transition.move));
FragmentB secondFragment = new FragmentB();
secondFragment.setSharedElementEnterTransition(TransitionInflater.from(getActivity())
.inflateTransition(android.R.transition.move));
getFragmentManager().beginTransaction()
.add(R.id.container, secondFragment)
.addToBackStack(null)
.addSharedElement(imageView, imageView.getTransitionName())
.commit();
}

Try this
getSupportFragmentManager().beginTransaction()
.addSharedElement(myImage, "mytransition")
.add(R.id.recycler_view_container, myFragment2)
.hide(myFragment1)
commit();
worked for me

since the system isnt going through the onPause from the first fragment its not going to happen. becuase when you add a new fragment, the new fragment comes on the top of the old fragment.
but you can fake it though you will have more code !
there is a sample below:
https://github.com/Kisty/FragmentTransitionExample
and a video not compeletely related but helps you to get the idea:
https://www.youtube.com/watch?v=CPxkoe2MraA

Try add .detach() method for FragmentTransaction.
FragmentManager manager = activity.getSupportFragmentManager ();
Fragment currentFragment = manager.findFragmentById (CONTAINER_ID);
int intoContainerId = currentFragment.getId ();
manager.beginTransaction ()
.setTransition (FragmentTransaction.TRANSIT_FRAGMENT_FADE)
.addSharedElement(view, transitionName)
.addToBackStack (withTag)
.detach(currentFragment)
.add(intoContainerId, newFragment, withTag)
.commit();

Related

Replacing fragment wrong order when not using addToBackStack

I have a menu where the user can click on different buttons and swap between different bottom bars.
When a user clicks button1 following happens:
setFragment(R.id.bottom_bar_container, new FooBottomBar());
When they click button2 it will call:
setFragment(R.id.bottom_bar_container, new BarBottomBar());
Heres the setFragment method:
private void setFragment(int layout, Fragment newFragment) {
String tag = newFragment.getClass().getCanonicalName();
Fragment fragment = getFragmentManager().findFragmentByTag(tag);
if (fragment == null) {
fragment = newFragment;
}
Log.d("Tag", "Replacing with " + tag);
getFragmentManager()
.beginTransaction()
.replace(layout, fragment, tag)
.commit();
}
This works overall well but if user spam clicks on both buttons something interesting can happen.
Logcat:
Tag: Replacing with com.example.FooBottomBar
Tag: Replacing with com.example.BarBottomBar
But in the UI I can see FooBottomBar even though the last replacement was BarBottomBar.
I found that if I add addToBackStack(null) the problem goes away but I don't want to have this on the back stack.
getFragmentManager()
.beginTransaction()
.replace(layout, fragment, tag)
.addToBackStack(null)
.commit();
I would like to know the reason for this issue and if there's any solution to it.
Use getChildFragmentManager() with commitNow() instead.
getChildFragmentManager()
.beginTransaction()
.replace(layout, fragment, tag)
.commitNow();

FragmentManager.popBackStack() adds fragment below the current one

I'm learning how to use fragments properly.
I have 4 fragments:
A
B
Loading
Complete
It should replace each other in this sequence:
A ==> Loading ==> B ==> Loading ==> Complete
Method that change my Fragment A into Loading:
LoadingFragment loadingFragment = new LoadingFragment();
Bundle bundle = new Bundle();
bundle.putBoolean("fragmentA", true);
loadingFragment.setArguments(bundle);
getFragmentManager().beginTransaction()
.replace(R.id.fragment_container, loadingFragment)
.addToBackStack(null)
.commit();
Changing Fragment B into Loading looks similarly (only arguments are changed obviously)
Method that change Loading fragment into Fragment B:
new Thread(new Runnable() {
#Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
getFragmentManager().beginTransaction()
.replace(R.id.fragment_container, new FragmentB())
.commit();
}
}).start();
Thread.sleep(...) is just a long operation...
Obviously I'm not adding .addToBackStack(null) this time.
Ok. Now the problem:
When I use getFragmentManager().popBackStack() method, the previous fragment (A) is added below (my fragment container is LinearLayout) my actual fragment instead of replaceing it. According to this: https://stackoverflow.com/a/17793959/3279023 it should work, but it's not.
The behavior you are experiencing looks OK to me. popBackStack() reverses the last saved operation, which in your case is: replace A with Loading.
So it replaces Loading with A, but that has no effect on B obviously, which you added without recording the transaction.
A solution could be using addToBackStack() in both cases, so that both transaction are registered, and then call popBackStack() twice.
Several fragments can make one activity/screen. Backstack is like back arrow or go back to the last activity/screen.

GetFragmentManager.findFragmentByTag() returns null

getFragmentManager().beginTransaction()
.replace(R.id.graph_fragment_holder, new GraphFragment(), "GRAPH_FRAGMENT")
.commit();
getFragmentManager().beginTransaction()
.replace(R.id.list_fragment_holder, new ListFragment(), "LIST_FRAGMENT")
.commit();
//getFragmentManager().executePendingTransactions();
GraphFragment graphFragment = (GraphFragment) getFragmentManager().findFragmentByTag("GRAPH_FRAGMENT");
graphFragment.setData(data);
ListFragment listFragment = (ListFragment) getFragmentManager().findFragmentByTag("LIST_FRAGMENT");
listFragment.setData(data);
I've supplied a tag so I'm not sure why findFragmentByTag() returns null.
What I've tried from reading other questions:
this.setRetainInstance(true) in the oncreate of both fragments.
Both fragment constructors are empty public fragmentName(){}.
tried executePendingTransactions after adding the fragments.
tried add instead of replace on the fragments (edited)
I was confused about this for a long time. First, you need to save the fragment you are replacing by pushing it onto the back stack. The tag you supply is put on the fragment you are adding, not the one you are pushing onto the back stack. Later, when you do push it onto the back stack, that tag goes with it. Here's code with objects broken out to make it easier to trace. You must call 'addToBackStack' before 'commit'.
GraphFragment grFrag = new GraphFragment();
FragmentTransaction tr = getSupportFragmentManager().beginTransaction();
tr.replace(R.id.fragment_container, grFrag, "GRAPH_FRAGMENT");
// grFrag is about to become the current fragment, with the tag "GRAPH_FRAGMENT"
tr.addToBackStack(null);
// 'addToBackStack' also takes a string, which can be null, but this is not the tag
tr.commit();
// any previous fragment has now been pushed to the back stack, with it's tag
ListFragment liFrag = new ListFragment();
FragmentTransaction tr = getSupportFragmentManager().beginTransaction();
tr.replace(R.id.fragment_container, liFrag, "LIST_FRAGMENT");
// liFrag is is about to become the current fragment, with the tag "LIST_FRAGMENT"
tr.addToBackStack(null);
tr.commit();
// 'grFrag' has now been pushed to the back stack, with it's tag being "GRAPH_FRAGMENT"
Call getFragmentManager().executePendingTransactions() after fragment transaction.
getFragmentManager()
.beginTransaction()
.replace(R.id.container, new ExampleFragment(), "YOUR TAG HERE");
.commit();
//after transaction you must call the executePendingTransaction
getFragmentManager().executePendingTransactions();
//now you can get fragment which is added with tag
ExampleFragment exampleFragment = getFragmentManager().findFragmentByTag("YOUR TAG HERE");
I was having the same problem of findFragmentByTag() always returning null.
Eventually I tracked it down, I was overriding onSaveInstanceState() in my Activity but not calling super. As soon as I fixed that findFragmentByTag() returned the Fragment as expected.
You can use
fragmentTransaction.addToBackStack(yourFragmentTag);
After that you can reuse it with
getSupportFragmentManager().findFragmentByTag(yourFragmentTag);
Answered here, just need to call getSupportFragmentManager().executePendingTransactions(); after your findByTag or findById
In my case I had to create a class level FragmentManager object and then use it instead of using getSupportFragmentManager() directly.
public class Main extends BaseActivity {
FragmentManager fragmentManager;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.fragmain);
fragmentManager = getSupportFragmentManager();
initFrag1();
}
private void initFrag1() {
String name = Frag1.class.getSimpleName();
if (fragmentManager.findFragmentByTag(name) == null) {
fragmentManager.beginTransaction()
.add(R.id.frag_container, new Frag1(), name)
.addToBackStack(name)
.commit();
}
}
}

how to save the state of a fragment for further use (and to avoid reconstruction)?

i'm a beginner on android development and the problem really troubles me a lot
now i have code lines like this:
getFragmentManager()
.beginTransaction()
.replace(R.id.activity_blank, new WordlistPage(unit, false), "wordlistitems")
.commit();
each time the above section is executed, a new WordlistPage is created and the fragment view is initialized.
for example, currently i am reading the content in fragmentA, and after a while i switch to fragmentB and in one minute i return back to fragmentA. what i want is that the fragmentA be the same as it was and not be reconstructed.
so any suggestions?
To avoid to create a new fragment each time I switch fragments I've done somethig like this:
FragmentManager fragmentManager = getFragmentManager();
Fragment fragment = fragmentManager.findFragmentByTag("FragmentTag");
if (fragment == null) {
fragment = new CustomFragment();
fragmentManager.beginTransaction().replace(R.id.frag, fragment, "FragmentTag").commit();
} else {
fragmentManager.beginTransaction().attach(fragment).commit();
}

Android Fragment - move from one View to another?

Can i first add a Fragment to a View, then "detach" it, and then "re-attach" it to another View?
In code, i want to:
fragOne one = new fragOne();
getSupportFragmentManager().beginTransaction()
.add(R.id.left, one, "tag").commit();
getSupportFragmentManager().beginTransaction()
.detach(one).commit(); // or .remove(), or .addToBackStack(null).remove()
getSupportFragmentManager().executePendingTransactions();
getSupportFragmentManager().beginTransaction()
.add(R.id.right, one).commit();
But it throws error:
04-05 13:28:03.492: E/AndroidRuntime(7195): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.trybackstack/com.example.trybackstack.MainActivity}: java.lang.IllegalStateException: Can't change container ID of fragment fragOne{40523130 #0 id=0x7f080000 tag}: was 2131230720 now 2131230721
Thanks for help!
I had the same problem but I found that what I really needed was to reparent the fragment's view and not the fragment itself, thus avoiding any fragmentManager transaction.
View vv = fragment.getView();
ViewGroup parent = (ViewGroup)vv.getParent();
parent.removeView(vv);
newparent.addView(vv, layoutParams);
after trying all the answers from similar questions, looks like i've found a way to do the trick.
First issue - you really have to commit and execute remove transaction before trying to add fragment to another container. Thanks for that goes to nave's answer
But this doesn't work every time. The second issue is a back stack. It somehow blocks the transaction.
So the complete code, that works for me looks like:
manager.popBackStackImmediate(null, manager.POP_BACK_STACK_INCLUSIVE);
manager.beginTransaction().remove(detailFragment).commit();
manager.executePendingTransactions();
manager.beginTransaction()
.replace(R.id.content, masterFragment, masterTag)
.add(R.id.detail, detailFragment, activeTag)
.commit();
I guess you would have this figured out by now, but i dont't see any satisfactory answer.
So, I'm posting this for others who may refer to this in the future.
If you want to move a fragment from one view to another you do the following:
android.app.FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.remove(fragment1);
fragmentTransaction.commit();
fragmentManager.executePendingTransactions();
fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.add(R.id.containerToMoveTo, fragment1);
fragmentTransaction.commit();
This way you do not have to duplicate the fragment.
Please check the solution,you need to create the new instance of the same fragment and instantiate it with state of the old fragment if you want to save the state of the old fragment.
FragmentTransaction ft = mFragmentManager.beginTransaction();
ft.remove(one);
Fragment newInstance = fetchOldState(one);
ft.add(R.id.right, newInstance);
ft.commit();
//TO fetch the old state
private Fragment fetchOldState(Fragment f)
{
try {
Fragment.SavedState oldState= mFragmentManager.saveFragmentInstanceState(f);
Fragment newInstance = f.getClass().newInstance();
newInstance.setInitialSavedState(oldState);
return newInstance;
}
catch (Exception e) // InstantiationException, IllegalAccessException
{
}
}
I have run into that problem as well. Sometimes moving fragments works, sometimes you have to create a new instance. It seems that moving fragments does not work, if the fragment keeps being in the "isRemoving" state.
Being removed also seems to be prevented by having a fragment in the backstack.
Adding to the Yan. Yurkin's answer. Make sure not to have any transitions applied to the transaction as these seem to delay the fragment from being detached.

Categories

Resources