I am attempting to create a toggling animation for a custom menu. There are multiple menus that can be chosen by different buttons. When no menu is open, tapping on a button should open that menu. If another menu is open, the open one is closed and after that animation, the chosen one should be opened. every closing/opening action is coupled with a ConstraintLayout transition.
As it didn't work properly, I created the following test procedure:
@Override
protected void onCreate(Bundle savedInstanceState) {
...
ConstraintLayout layout_main;
ConstraintSet l_mh0c = new ConstraintSet();
ConstraintSet l_mh1o = new ConstraintSet();
ConstraintSet l_mh1c = new ConstraintSet();
Button btn;
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
toggleOne();
}
});
layout_main = (ConstraintLayout) findViewById(R.id.constraintMain);
l_mh1o.clone(this, R.layout.main_menue_header1_open);
l_mh1c.clone(this, R.layout.main_menue_header1_closed);
l_mh0c.clone(this, R.layout.activity_main);
...
}
int openedMenue = -1;
long animationTime = 1000;
private void toggleOne() {
TransitionManager.endTransitions(layout_main); //<- makes no difference when commented
if(openedMenue==1) {
System.out.println("sync closing");
Runnable r = () -> toggleOne();
closeMenue(1, animationTime, r);
} else {
System.out.println("sync opening");
startMenue(1, animationTime);
}
}
public void startMenue(Integer index, final Long maxtime) {
Transition t;
switch (index) {
case 1:
t = new ChangeBounds();
t.setDuration(0).addListener(new TransitionEndListener() {
@Override
public void onTransitionEnd(Transition transition) {
Transition t = new ChangeBounds();
t.setDuration(maxtime / 2).addListener(new TransitionEndListener() {
@Override
public void onTransitionEnd(Transition transition) {
Transition t = new ChangeBounds();
t.setDuration(maxtime / 2);
TransitionManager.beginDelayedTransition(layout_main, t);
l_mh0o.applyTo(layout_main);
openedMenue = 1;
System.out.println("sync start finished");
}
});
TransitionManager.beginDelayedTransition(layout_main, t);
l_mh1o.applyTo(layout_main);
}
});
TransitionManager.beginDelayedTransition(layout_main, t);
l_mh1c.applyTo(layout_main);
// the following does not provoke any changes
Scene s = new Scene(layout_main);
TransitionManager.go(s);
break;
}
}
private void closeMenue(int index, final long maxtime, Runnable callback) {
System.out.println("sync closing menue " + openedMenue);
Transition t;
switch (index) {
case 1:
t = new ChangeBounds();
t.setDuration(maxtime/2).addListener(new TransitionEndListener() {
@Override
public void onTransitionEnd(Transition transition) {
Transition t = new ChangeBounds();
t.setDuration(maxtime/2).addListener(new TransitionEndListener() {
@Override
public void onTransitionEnd(Transition transition) {
Transition t = new ChangeBounds();
t.setDuration(0);
TransitionManager.beginDelayedTransition(layout_main, t);
l_mh0c.applyTo(layout_main);
openedMenue = -1;
try {
callback.run();
} catch (Exception e) { e.printStackTrace(); }
}
});
TransitionManager.beginDelayedTransition(layout_main, t);
l_mh1c.applyTo(layout_main);
openedMenue = 1;
}
});
TransitionManager.beginDelayedTransition(layout_main, t);
l_mh1o.applyTo(layout_main);
break;
}
}
When I run it, I get the following output:
[Button Click]
sync opening
sync start finished
[Button Click]
sync closing
sync closing menue 1
sync opening
sync start finished // problem
However, the last line is never printed; It seems as if (after the closing action starts the opening action again) the first transition in startMenue() never does a callback to the onTransitionEnd().
Here is the TransitionEndListener (just a simple wrapper for the interface)
public abstract class TransitionEndListener implements Transition.TransitionListener {
@Override
public final void onTransitionStart(Transition transition) {}
@Override
public final void onTransitionCancel(Transition transition) {}
@Override
public final void onTransitionPause(Transition transition) {}
@Override
public final void onTransitionResume(Transition transition) {}
}
I already checked if the second starting Transition is cancelled by putting a print statement in the onTransitionCancel() which doesn't seem to be the case.
Can you please explain why this is happening?
UPDATE
I found this post on TransitionManager callbacks;
The Transition from mh0c t0 mh1c is a ConstraintLayout transition, because the constraints actually change; however, the transition is not visible on the UI because the width of the element in transition is 0. (This transition is a jump from one menu point to the other that should not be visible.)
Could this be a reason that the Transition does not do the callback?
And if so; how can I get around this?
UPDATE 2
I was reading the documentation and may have found a solution using TransitionManager.go(scene, transition)
.
--> Unfortunately this did not work; see code of startMenue() for changes
After more and more struggling and reviewing these posts:
I have found out what caused the trouble:
In the most inner part (the most inner callback) of closeMenue()
Transition t = new ChangeBounds();
t.setDuration(0);
TransitionManager.beginDelayedTransition(layout_main, t);
l_mh0c.applyTo(layout_main);
openedMenue = -1;
try {
callback.run();
} catch (Exception e) { e.printStackTrace(); }
The sequence of commands is slightly wrong. It should look like this:
Transition t = new ChangeBounds();
t.setDuration(0).addListener(new TransitionEndListener() {
@Override
public void onTransitionEnd(Transition transition) {
try {
callback.run();
} catch (Exception e) { e.prtinStackTrace(); }
}
});
TransitionManager.beginDelayedTransition(layout_main, t);
l_mh0c.applyTo(layout_main);
openedMenue = -1;
This makes sure to first finish the closing transition and THEN starting the opening transition.