TL;DR: How should multi-pane apps with deep navigation similar to the Spotify iPad app look and work on Android, and how to implement this?
I had some research time and came up with a solution to this question (a question that I've wanted to see the solution for LONG time, even before you asked it). I can't really show the whole code as there's some IP boundaries, but I'll put it here the main parts for this animation to works.
There're two key tools: setCustomAnimations
and LayoutTransition
Yes, as far as I've been able to do it, you need to separate set animations to make it work.
So let's get to some code, you'll define your XML with a horizontal LinearLayout and make sure to include the following line on it.
android:animateLayoutChanges="true"
this will auto-generate a standard LayoutTransition
which does translate the fragment/view that is staying in the layout and alpha (in or out) the fragment/view that is being included or removed from the layout. Give it a try.
So after this layout is inflated we gonna capture this LayoutTransition and trick it out to our needs:
LayoutTransition lt = myLinearLayout.getLayoutTransition();
lt.setAnimator(LayoutTransition.APPEARING, null);
lt.setAnimator(LayoutTransition.DISAPPEARING, null);
lt.setStartDelay(LayoutTransition.CHANGE_APPEARING, 0);
lt.setStartDelay(LayoutTransition.CHANGE_DISAPPEARING, 0);
with that code, we're removing the alpha animations and removing any delay from the transition (because we want all the translations to fire together).
And now it's just a few simple fragment transactions to make it work, during initialisation we inflate that layout and put a few fragments on it:
setContentView(R.layout.main); // the layout with that Linear Layout
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.add(R.id.main, frag1, FRAG_1_TAG); // it's good to have tags so you can find them later
ft.add(R.id.main, frag2, FRAG_2_TAG);
ft.add(R.id.main, frag3, FRAG_3_TAG);
ft.hide(frag3);
ft.commit();
now on the transaction it's a simple:
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.setCustomAnimations(R.anim.push_left_in, R.anim.push_left_out, R.anim.push_right_in, R.anim.push_right_out);
Fragment left = getFragmentManager().findFragmentByTag(FRAG_1_TAG);
Fragment right = getFragmentManager().findFragmentByTag(FRAG_3_TAG);
ft.hide(left);
ft.show(right);
ft.addToBackStack(null);
ft.commit();
final notes:
to make deeper navigation it's just a matter of firing FragmentTransactions
to add fragments to the LinearLayout and hide
or detach
the left side fragment.
Also to make the fragments work on the linear layout is important to set their LinearLayout.LayoutParams.weight during runtime, something similar to the following code applied to the fragment view
((LinearLayout.LayoutParams) view.getLayoutParams()).weight = 1;
to make it work on phones as well it's just a matter of applying the common multiple screen support patterns.
last note, be careful on proper managing the layout status during device rotation because it's not all automagically handled by the system.
Happy coding!
Partial answer to the animation part: You can do animations with the FragmentTransaction:
ft.setCustomAnimations(android.R.anim.slide_in_left,
android.R.anim.slide_out_right);
Update: see this answer from Reto Meier himself about fragment animation: https://stackoverflow.com/a/4819665/1007169
We ran into the same problem with our app. The constraints we gave ourselves:
In light of those constraints, we built a new layout we call PanesLayout. You can check it out here:
https://github.com/cricklet/Android-PanesLibrary
It basically allows you to easily add any number of dynamically sized panes and attach fragments to those panes. Hope you find it useful! :)