I have an app which is using the one-activity-multiple-fragments architecture using navigation component.
For each fragment, I have a slide in/out animation like so:
anim_in.xml:
<?xml version="1.0" encoding="utf-8"?>
<translate
xmlns:android="http://schemas.android.com/apk/res/android"
android:fromXDelta="100%p"
android:toXDelta="0%"
android:duration="#android:integer/config_shortAnimTime" />
anim_out.xml:
<?xml version="1.0" encoding="utf-8"?>
<translate
xmlns:android="http://schemas.android.com/apk/res/android"
android:fromXDelta="0%p"
android:toXDelta="100%"
android:duration="#android:integer/config_shortAnimTime" />
nav_graph.xml:
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/nav_graph"
app:startDestination="#id/mainFragment">
<fragment
android:id="#+id/mainFragment"
android:name="com.example.monettests.MainFragment"
android:label="#string/app_name"
tools:layout="#layout/fragment_main" >
<action
android:id="#+id/action_mainFragment_to_settingsFragment"
app:enterAnim="#anim/anim_in"
app:popEnterAnim="#anim/anim_in"
app:popExitAnim="#anim/anim_out"
app:destination="#id/settingsFragment" />
<action
android:id="#+id/action_mainFragment_to_feedbackFragment"
app:enterAnim="#anim/anim_in"
app:popEnterAnim="#anim/anim_in"
app:popExitAnim="#anim/anim_out"
app:destination="#id/feedbackFragment" />
</fragment>
<fragment
android:id="#+id/settingsFragment"
android:name="com.example.monettests.SettingsFragment"
android:label="Settings"
tools:layout="#layout/fragment_settings" />
<fragment
android:id="#+id/feedbackFragment"
android:name="com.example.monettests.FeedbackFragment"
android:label="Send Feedback"
tools:layout="#layout/fragment_feedback" />
</navigation>
Navigation code in MainFragment:
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
requireActivity().addMenuProvider(object : MenuProvider {
override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
menuInflater.inflate(R.menu.menu_main, menu)
}
override fun onMenuItemSelected(menuItem: MenuItem): Boolean {
return when (menuItem.itemId) {
R.id.menuMain_settingsItem -> {
findNavController().navigate(R.id.action_mainFragment_to_settingsFragment)
true
}
R.id.menuMain_feedbackItem -> {
findNavController().navigate(R.id.action_mainFragment_to_feedbackFragment)
true
}
else -> {
false
}
}
}
}, viewLifecycleOwner, Lifecycle.State.RESUMED)
}
The problem with this is that it just animates the fragment itself but not the action bar.
What I have tried:
Manually animating the action bar in each fragment using listeners (unsuccessful)
Searching documentation for a way to do this (tried for hours but unsuccessful - shocked that there isn't a way to do this in the SDK)
I understand that this is desired fragment behavior, but I want to give the end user the illusion of animating between different pages and for that it's best if the action bar itself is part of the transition.
Is there any way to have a fragment transition that animates the fragment and the action bar, similar to how it's done with activities?
My project uses fragment navigation to transition between destinations,
In the video it is seen that the page is offset up or down.
I would like the page to only enter the screen from left to right.
Where might the problem be?
thanks for your help.
app video
Host Activity
class ProductHostActivity: AppCompatActivity() {
private lateinit var navHostFragment: NavHostFragment
private lateinit var navController: NavController
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_product_host)
navHostFragment = supportFragmentManager.findFragmentById(R.id.fragment) as NavHostFragment
navController = findNavController(R.id.fragment)
navController.setGraph(R.navigation.nav_product)
}
}
HomeFragment
button_add_to_cart.setOnClickListener {
findNavController().navigate(R.id.action_productDetailFragment_to_productIntroDetailFragment)
}
navigation
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/nav_login"
app:startDestination="#id/homeFragment">
<fragment
android:id="#+id/homeFragment"
android:name="com.presentation.view.homeFragment"
tools:layout="#layout/fragment_product_detail">
<action
android:id="#+id/action_homeFragment_to_detailFragment"
app:destination="#id/detailFragment"
app:enterAnim="#anim/slide_in_right_test"
app:exitAnim="#anim/hold_page"
app:popEnterAnim="#anim/hold_page"
app:popExitAnim="#anim/slide_out_right_test" />
</fragment>
<fragment
android:id="#+id/detailFragment"
android:name="com.fragment.ProductReadMoreFragment"
android:label="detailFragment" />
</navigation>
in animation
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="3000"
android:fromXDelta="100%"
android:toXDelta="0%" />
</set>
out animation
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="3000"
android:fromXDelta="0%"
android:toXDelta="100%" />
</set>
hold page
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate android:fromYDelta="0%" android:toYDelta="0%"
android:duration="3000"/>
</set>
I want to add new fragment transition when BottomNavigationView change its current fragment.
I want to enter the next fragment from left to right.
I don't see this for Jetpack BottomNavigationView to add custom fragment transition animations because there are no actions available for BottomNavigationView.
As per this issue:
NavigationUI follows the material design guidelines, which specifically calls [see the 'Transitions' section] for a cross fade animation between BottomNavigationView items.
Setting your own listener is indeed the correct approach if you want to deviate from the guidelines and what NavigationUI provides.
Therefore you'll want to look at the NavigationUI source code for its onNavDestinationSelected() method and make your own version that passes in what custom animations you want, calling it from your own OnNavigationItemSelectedListener.
I have posted this question and I have get a great help from the answer of ianhanniballake ( The answer I accepted) to fulfil my expectation. For people who refer this Quection and answer in future I'm further add some code following to further reference and understanding.
Feel free to comment if you need any help after you go through the code.
Following is my Source code in MainActivity.java" OnCreate() Method
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final BottomNavigationView bottomNavigationView=findViewById(R.id.bottomNavigationView);
final NavController navController= Navigation.findNavController(this,R.id.nav_host_fragment);
selectedItem=R.id.firstFragment1;
bottomNavigationView.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
switch (item.getItemId()){
case R.id.firstFragment1:
if(selectedItem != R.id.firstFragment1){
selectedItem = R.id.firstFragment1;
navController.popBackStack();
}
break;
case R.id.secondFragment1:
if(selectedItem != R.id.secondFragment1) {
selectedItem= R.id.secondFragment1;
Log.d("palvision.dev", "action to first fragment");
navController.navigate(R.id.action_firstFragment_to_secondFragment2);
}
break;
}
return true;
}
});
}
Following is my source code in nav_graph.xml which is navigation graph.
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/nav_graph"
app:startDestination="#id/firstFragment">
<fragment
android:id="#+id/firstFragment"
android:name="com.dehan.myapplicationnavtest.FirstFragment"
tools:layout="#layout/fragment_first" >
<action
android:id="#+id/action_firstFragment_to_secondFragment2"
app:destination="#+id/secondFragment"
app:enterAnim="#anim/enter_from_right"
app:exitAnim="#anim/exit_to_left"
app:popEnterAnim="#anim/enter_from_left"
app:popExitAnim="#anim/exit_to_right" />
</fragment>
<fragment
android:id="#+id/secondFragment"
android:name="com.dehan.myapplicationnavtest.SecondFragment"
tools:layout="#layout/fragment_second" />
Following is the code for FirstFragment.java
public class FirstFragment extends Fragment {
public FirstFragment() {
// Required empty public constructor
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_first, container, false);
}
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
Toolbar toolbar=view.findViewById(R.id.toolbar);
NavController navController= Navigation.findNavController(getActivity(),R.id.nav_host_fragment);
AppBarConfiguration appBarConfiguration =new AppBarConfiguration.Builder(navController.getGraph()).build();
NavigationUI.setupWithNavController(toolbar,navController,appBarConfiguration);
}
}
following is the code for SecondFragment.java
public class SecondFragment extends Fragment {
public SecondFragment() {
// Required empty public constructor
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_second, container, false);
}
}
following is the code for enter_from_right.xml which is located in anim folder.
all the other animation files are also placed in anim folder.
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false">
<translate
android:fromXDelta="100%" android:toXDelta="0%"
android:fromYDelta="0%" android:toYDelta="0%"
android:duration="400" />
following is the code for exit_to_left.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false">
<translate
android:fromXDelta="0%" android:toXDelta="-100%"
android:fromYDelta="0%" android:toYDelta="0%"
android:duration="400"/>
enter_from_left.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false">
<translate
android:fromXDelta="-100%" android:toXDelta="0%"
android:fromYDelta="0%" android:toYDelta="0%"
android:duration="400"/>
exit_to_right.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false">
<translate
android:fromXDelta="0%" android:toXDelta="100%"
android:fromYDelta="0%" android:toYDelta="0%"
android:duration="400" />
I've check some tutorials for animate transaction between fragments. I've used this method for animation and it works:
fragmentTransaction.setCustomAnimations(android.R.anim.slide_in_left,
android.R.anim.slide_out_right);
But I want to invert this animation: old fragment slide out to the left, and new fragment slide in to the right, but no value of R.anim file seems to be useful for my scope.
How can I do it?
UPDATE For Android v19+ see this link via #Sandra
You can create your own animations. Place animation XML files in res > anim
enter_from_left.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false">
<translate
android:fromXDelta="-100%p" android:toXDelta="0%"
android:fromYDelta="0%" android:toYDelta="0%"
android:duration="#android:integer/config_mediumAnimTime"/>
</set>
enter_from_right.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false">
<translate
android:fromXDelta="100%p" android:toXDelta="0%"
android:fromYDelta="0%" android:toYDelta="0%"
android:duration="#android:integer/config_mediumAnimTime" />
</set>
exit_to_left.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false">
<translate
android:fromXDelta="0%" android:toXDelta="-100%p"
android:fromYDelta="0%" android:toYDelta="0%"
android:duration="#android:integer/config_mediumAnimTime"/>
</set>
exit_to_right.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false">
<translate
android:fromXDelta="0%" android:toXDelta="100%p"
android:fromYDelta="0%" android:toYDelta="0%"
android:duration="#android:integer/config_mediumAnimTime" />
</set>
you can change the duration to short animation time
android:duration="#android:integer/config_shortAnimTime"
or long animation time
android:duration="#android:integer/config_longAnimTime"
USAGE
(note that the order in which you call methods on the transaction matters. Add the animation before you call .replace, .commit):
FragmentTransaction transaction = supportFragmentManager.beginTransaction();
transaction.setCustomAnimations(R.anim.enter_from_right, R.anim.exit_to_left, R.anim.enter_from_left, R.anim.exit_to_right);
transaction.replace(R.id.content_frame, fragment);
transaction.addToBackStack(null);
transaction.commit();
There is three way to transaction animation in fragment.
Transitions
So need to use one of the built-in Transitions, use the setTranstion()
method:
getSupportFragmentManager()
.beginTransaction()
.setTransition( FragmentTransaction.TRANSIT_FRAGMENT_OPEN )
.show( m_topFragment )
.commit()
Custom Animations
You can also customize the animation by using the setCustomAnimations() method:
getSupportFragmentManager()
.beginTransaction()
.setCustomAnimations( R.anim.slide_up, 0, 0, R.anim.slide_down)
.show( m_topFragment )
.commit()
slide_up.xml
<?xml version="1.0" encoding="utf-8"?>
<objectAnimator
xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="#android:anim/accelerate_decelerate_interpolator"
android:propertyName="translationY"
android:valueType="floatType"
android:valueFrom="1280"
android:valueTo="0"
android:duration="#android:integer/config_mediumAnimTime"/>
slide_down.xml
<?xml version="1.0" encoding="utf-8"?>
<objectAnimator
xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="#android:anim/accelerate_decelerate_interpolator"
android:propertyName="translationY"
android:valueType="floatType"
android:valueFrom="0"
android:valueTo="1280"
android:duration="#android:integer/config_mediumAnimTime"/>
Multiple Animations
Finally, It's also possible to kick-off multiple fragment animations
in a single transaction. This allows for a pretty cool effect where
one fragment is sliding up and the other slides down at the same time:
getSupportFragmentManager()
.beginTransaction()
.setCustomAnimations( R.anim.abc_slide_in_top, R.anim.abc_slide_out_top ) // Top Fragment Animation
.show( m_topFragment )
.setCustomAnimations( R.anim.abc_slide_in_bottom, R.anim.abc_slide_out_bottom ) // Bottom Fragment Animation
.show( m_bottomFragment )
.commit()
To more detail you can visit URL
Note:- You can check animation according to your requirement because above may be have issue.
slide_in_down.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="#android:integer/config_longAnimTime"
android:fromYDelta="0%p"
android:toYDelta="100%p" />
</set>
slide_in_up.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="#android:integer/config_longAnimTime"
android:fromYDelta="100%p"
android:toYDelta="0%p" />
</set>
slide_out_down.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="#android:integer/config_longAnimTime"
android:fromYDelta="-100%"
android:toYDelta="0"
/>
</set>
slide_out_up.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="#android:integer/config_longAnimTime"
android:fromYDelta="0%p"
android:toYDelta="-100%p"
/>
</set>
direction = down
activity.getSupportFragmentManager()
.beginTransaction()
.setCustomAnimations(R.anim.slide_out_down, R.anim.slide_in_down)
.replace(R.id.container, new CardFrontFragment())
.commit();
direction = up
activity.getSupportFragmentManager()
.beginTransaction()
.setCustomAnimations(R.anim.slide_in_up, R.anim.slide_out_up)
.replace(R.id.container, new CardFrontFragment())
.commit();
I have same issue, i used simple solution
1)create sliding_out_right.xml in anim folder
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate android:fromXDelta="0" android:toXDelta="-50%p"
android:duration="#android:integer/config_mediumAnimTime"/>
<alpha android:fromAlpha="1.0" android:toAlpha="0.0"
android:duration="#android:integer/config_mediumAnimTime" />
</set>
2) create sliding_in_left.xml in anim folder
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate android:fromXDelta="50%p" android:toXDelta="0"
android:duration="#android:integer/config_mediumAnimTime"/>
<alpha android:fromAlpha="0.0" android:toAlpha="1.0"
android:duration="#android:integer/config_mediumAnimTime" />
</set>
3) simply using fragment transaction setCustomeAnimations() with two custom xml and two default xml for animation as follows :-
fragmentTransaction.setCustomAnimations(R.anim.sliding_in_left, R.anim.sliding_out_right, android.R.anim.slide_in_left, android.R.anim.slide_out_right );
This is another solution which I use:
public class CustomAnimator {
private static final String TAG = "com.example.CustomAnimator";
private static Stack<AnimationEntry> animation_stack = new Stack<>();
public static final int DIRECTION_LEFT = 1;
public static final int DIRECTION_RIGHT = -1;
public static final int DIRECTION_UP = 2;
public static final int DIRECTION_DOWN = -2;
static class AnimationEntry {
View in;
View out;
int direction;
long duration;
}
public static boolean hasHistory() {
return !animation_stack.empty();
}
public static void reversePrevious() {
if (!animation_stack.empty()) {
AnimationEntry entry = animation_stack.pop();
slide(entry.out, entry.in, -entry.direction, entry.duration, false);
}
}
public static void clearHistory() {
animation_stack.clear();
}
public static void slide(final View in, View out, final int direction, long duration) {
slide(in, out, direction, duration, true);
}
private static void slide(final View in, final View out, final int direction, final long duration, final boolean save) {
ViewGroup in_parent = (ViewGroup) in.getParent();
ViewGroup out_parent = (ViewGroup) out.getParent();
if (!in_parent.equals(out_parent)) {
return;
}
int parent_width = in_parent.getWidth();
int parent_height = in_parent.getHeight();
ObjectAnimator slide_out;
ObjectAnimator slide_in;
switch (direction) {
case DIRECTION_LEFT:
default:
slide_in = ObjectAnimator.ofFloat(in, "translationX", parent_width, 0);
slide_out = ObjectAnimator.ofFloat(out, "translationX", 0, -out.getWidth());
break;
case DIRECTION_RIGHT:
slide_in = ObjectAnimator.ofFloat(in, "translationX", -out.getWidth(), 0);
slide_out = ObjectAnimator.ofFloat(out, "translationX", 0, parent_width);
break;
case DIRECTION_UP:
slide_in = ObjectAnimator.ofFloat(in, "translationY", parent_height, 0);
slide_out = ObjectAnimator.ofFloat(out, "translationY", 0, -out.getHeight());
break;
case DIRECTION_DOWN:
slide_in = ObjectAnimator.ofFloat(in, "translationY", -out.getHeight(), 0);
slide_out = ObjectAnimator.ofFloat(out, "translationY", 0, parent_height);
break;
}
AnimatorSet animations = new AnimatorSet();
animations.setDuration(duration);
animations.playTogether(slide_in, slide_out);
animations.addListener(new Animator.AnimatorListener() {
#Override
public void onAnimationCancel(Animator arg0) {
}
#Override
public void onAnimationEnd(Animator arg0) {
out.setVisibility(View.INVISIBLE);
if (save) {
AnimationEntry ae = new AnimationEntry();
ae.in = in;
ae.out = out;
ae.direction = direction;
ae.duration = duration;
animation_stack.push(ae);
}
}
#Override
public void onAnimationRepeat(Animator arg0) {
}
#Override
public void onAnimationStart(Animator arg0) {
in.setVisibility(View.VISIBLE);
}
});
animations.start();
}
}
The usage of class. Let's say you have two fragments (list and details fragments)as shown below
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/ui_container"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<FrameLayout
android:id="#+id/list_container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<FrameLayout
android:id="#+id/details_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone" />
</FrameLayout>
Usage
View details_container = findViewById(R.id.details_container);
View list_container = findViewById(R.id.list_container);
// You can select the direction left/right/up/down and the duration
CustomAnimator.slide(list_container, details_container,CustomAnimator.DIRECTION_LEFT, 400);
You can use the function CustomAnimator.reversePrevious();to get the previous view when the user pressed back.
Have the same problem with white screen during transition from one fragment to another. Have navigation and animations set in action in navigation.xml.
Background in all fragments the same but white blank screen. So i set navOptions in fragment during executing transition
//Transition options
val options = navOptions {
anim {
enter = R.anim.slide_in_right
exit = R.anim.slide_out_left
popEnter = R.anim.slide_in_left
popExit = R.anim.slide_out_right
}
}
.......................
this.findNavController().navigate(SampleFragmentDirections.actionSampleFragmentToChartFragment(it),
options)
It worked for me. No white screen between transistion. Magic )
For me the only way I was able to get rid of the white background on the animation is if I changed to a DialogFragment instead of a Fragment and added the following code:
Created the following styles:
<style name="WindowSlideInAndOutAnimation">
<item name="android:windowEnterAnimation">#anim/slide_in_bottom</item>
<item name="android:windowExitAnimation">#anim/slide_out_top</item>
</style>
<style name="AppFullScreenDialogTransparentTitleBar" parent="Theme.MaterialComponents.Light.Dialog">
<item name="android:windowBackground">#android:color/transparent</item>
<item name="android:windowNoTitle">true</item>
<item name="android:windowIsFloating">false</item>
<item name="colorPrimaryDark">#android:color/transparent</item>
</style>
Then reference these styles in the dialog fragment as such:
override fun getTheme() = R.style.AppFullScreenDialogTransparentTitleBar
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
dialog?.window?.setWindowAnimations(R.style.WindowSlideInAndOutAnimation)
//rest of code on view created here
}
Then I just navigate to the fragment as normal via the navigation component (also note to change the type in the navigation xml file for the fragment to be dialog)
I have a fragment that is to replace another fragment. I want to specify the animation. But the animation is ignored.
transaction.replace(R.id.my_fragment, newFrag);
transaction.addToBackStack(null);
transaction.setCustomAnimations(R.anim.slide_in_up, R.anim.slide_out_up);
slide_in_up
<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="#android:integer/config_longAnimTime"
android:fromYDelta="0%p"
android:toYDelta="100%p" />
slide_out_up
<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="#android:integer/config_longAnimTime"
android:fromYDelta="100%p"
android:toYDelta="0%p" />
All I am really trying to achieve is for the new fragment to slide in from the bottom. My animations are ignored. What is the code missing?
transaction.setCustomAnimations(R.anim.slide_in_up, R.anim.slide_out_up);
transaction.addToBackStack(null);
transaction.replace(R.id.my_fragment, newFrag);
slide_in_up
<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="#android:integer/config_longAnimTime"
android:fromYDelta="100%p"
android:toYDelta="0%p" />
slide_out_up
<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="#android:integer/config_longAnimTime"
android:fromYDelta="0%p"
android:toYDelta="-100%p" />
It's been some time since this question was asked but here is an answer for other people coming here:
e1da is right insofar as that setCustomAnimation() call has to be called before replace(). Otherwise the animation will not show.
The second problem is that you probably are using native fragments that cannot be animated with the view animations.
Use the following files:
slide_in_up.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:fillAfter="true" >
<objectAnimator
android:duration="500"
android:propertyName="y"
android:valueFrom="1280"
android:valueTo="0"
android:valueType="floatType" />
</set>
slide_out_up.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:fillAfter="true" >
<objectAnimator
android:duration="500"
android:propertyName="y"
android:valueFrom="0"
android:valueTo="-1280"
android:valueType="floatType" />
</set>
A little explanation:
You have to differentiate between view animation for support fragments on one hand and property animation for native fragments on the other hand.
View animation:
Is the pre-android 3.0 way to animate views. Sample code for this is the slide_in.xml and slide_up.xml by user3093402
<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="#android:integer/config_longAnimTime"
android:fromYDelta="0%p"
android:toYDelta="100%p" />
It is worth mentioning you cannot animate fragments with view animation. The exception to this are fragments from the support library (android.support.v4.app.Fragment).
Property animation
This is the way to animate objects after android 3.0. It is also declared as .xml files but makes use of the "valueAnimator" tag (objectAnimator extends valueAnimator). Examples are in the answer to the question.
This is the way how native fragments (android.app.Fragment) can be animated.
See also:
http://developer.android.com/guide/topics/graphics/overview.html
swap fragment in an activity via animation
Hope this helps,
Kai
EDIT: As pointed out by Raphael Royer-Rivard the fixed screen size is bad practice. It would be better to use a constant from the OS like in
getWindowManager().getDefaultDisplay().getMetrics(metrics).xdpi
(see DisplayMetrics). But I haven't done any Android development for some time so I don't know which one.
code for slide_in_up :
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="#android:integer/config_mediumAnimTime"
android:fromYDelta="100%p"
android:toYDelta="0%p" />
</set>
code for slide_in_down:
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="#android:integer/config_mediumAnimTime"
android:fromYDelta="0%p"
android:toYDelta="100%p" />
</set>
code for slide_out_up:
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="#android:integer/config_mediumAnimTime"
android:fromXDelta="0"
android:toYDelta="100%" />
</set>
code for slide_out_down:
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="#android:integer/config_mediumAnimTime"
android:fromXDelta="0"
android:toYDelta="-100%" />
</set>
and after that in your activity or fragment set animation like below:
Fragment fragment = new Fragment();
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.setCustomAnimations(R.anim.slide_in_up, R.anim.slide_in_down, R.anim.slide_out_down, R.anim.slide_out_up);
transaction.replace(container, fragment).commit();
Currently with android.transition this is as simple as fragment.enterTransition = Slide() or fragment.enterTransition = Fade()
Note: Min version L.
R.anim will not work there but R.animator wil do.
for example
transaction.setCustomAnimations(android.R.animator.fade_in, android.R.animator.fade_out);
This answer is written in Kotlin. For sake of the example, I'm just hiding or showing it based on it's current isHidden attribute. I'm using androidx.transition.Slide
Link: https://developer.android.com/reference/androidx/transition/Slide
val frag = supportFragmentManager.findFragmentById(R.id.frag_id_from_xml_layout)
frag?.let {
val ft = supportFragmentManager.beginTransaction()
frag.enterTransition = Slide()
frag.exitTransition = Slide()
if (frag.isHidden) ft.show(frag) else ft.hide(frag)
ft.commit()
}
here is complete working example
Pressing the button toggles between 2 fragments A and B (by slide animation right to left). The fragments are just stupid text (AAAAAA and BBBBB) with different backgrounds.
MainActivity.java
package com.example.slidetrans;
import android.app.Activity;
import android.app.Fragment;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
public class MainActivity extends Activity {
boolean showingA = true;
Button button;
A a;
B b;
private void incarnate(FragmentManager fm){
int layoutId = R.id.frame;
boolean fragmentWasNull = false;
Fragment f = fm.findFragmentById(layoutId);
if (f == null){
if (showingA){
f = a = new A();
} else {
f = b = new B();
}
fragmentWasNull = true;
}
if (fragmentWasNull){
FragmentTransaction ft = fm.beginTransaction();
ft.add(layoutId, showingA ? a : b, "main").commit();
}
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
FragmentManager fm = getFragmentManager();
incarnate(fm);
button = (Button)findViewById(R.id.button);
OnClickListener listener = new OnClickListener() {
#Override
public void onClick(View v) {
FragmentManager fm = getFragmentManager();
FragmentTransaction transaction = fm.beginTransaction();
transaction.setCustomAnimations(R.anim.in, R.anim.out);
transaction.replace(R.id.frame, showingA ? new B() : new A()).commit();
showingA = !showingA;
button.setText(showingA ? "slide in B" : "slide in A");
}
};
button.setOnClickListener(listener);
}
}
LL.java
package com.example.slidetrans;
import android.content.Context;
import android.util.AttributeSet;
import android.widget.LinearLayout;
public class LL extends LinearLayout {
public LL(Context context) {
super(context);
}
public LL(Context context, AttributeSet attrs) {
super(context, attrs);
}
public float getXFraction() {
final int width = getWidth();
if (width != 0) return getX() / getWidth();
else return getX();
}
public void setXFraction(float xFraction) {
final int width = getWidth();
float newWidth = (width > 0) ? (xFraction * width) : -9999;
setX(newWidth);
}
}
main.xml (layout)
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
>
<Button
android:id="#+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="slide in B" />
<FrameLayout
android:id="#+id/frame"
android:layout_width="match_parent"
android:layout_height="match_parent" >
</FrameLayout>
</LinearLayout>
a.xml (layout)
<?xml version="1.0" encoding="utf-8"?>
<com.example.slidetrans.LL xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="#00FF00"
>
<TextView
android:id="#+id/aText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="AAAAAAAAAAAAAAAAAA"
android:textSize="30sp"
android:textStyle="bold"
/>
</com.example.slidetrans.LL>
b.xml (layout)
<?xml version="1.0" encoding="utf-8"?>
<com.example.slidetrans.LL xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="#FFFF00"
>
<TextView
android:id="#+id/bText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="30sp"
android:textStyle="bold"
android:text="BBBBBBBBBB"
/>
</com.example.slidetrans.LL>
in.xml (anim)
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<objectAnimator
android:duration="500"
android:interpolator="#android:anim/linear_interpolator"
android:propertyName="xFraction"
android:valueFrom="1.0"
android:valueTo="0.0"
android:valueType="floatType" />
</set>
out.xml (anim)
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<objectAnimator
android:duration="500"
android:interpolator="#android:anim/linear_interpolator"
android:propertyName="xFraction"
android:valueFrom="0.0"
android:valueTo="-1.0"
android:valueType="floatType" />
</set>