Swap Fragment in an Activity via Animation

swap fragment in an activity via animation

Old questiion and you probably already figured it out, but for future reference:

here's what you use to set a custom animation when you replace a fragment via code:

FragmentTransaction ft = getSupportFragmentManager().beginTransaction();

ft.setCustomAnimations(R.anim.slide_in_left, R.anim.slide_out_right);
ft.replace(R.id.fragment_container, newFragment, "fragment");
// Start the animated transition.
ft.commit();

Here is an example of the slide_in_left animation:

<?xml version="1.0" encoding="utf-8"?>
<set>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:fromXDelta="-100%"
android:toXDelta="0"
android:interpolator="@android:anim/decelerate_interpolator"
android:duration="500"/>
</set>

Note that this is the animation if you are using the compatibility library. Instead if you are using and SDK with native support for the FragmentManager then your animation will look like this:

<?xml version="1.0" encoding="utf-8"?>
<set>
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
android:propertyName="x"
android:valueType="floatType"
android:valueFrom="-1280"
android:valueTo="0"
android:duration="500"/>
</set>

This is because the compatibility library does not support the new objectAnimator type and instead only implement the old animation framework.

Transition animation between fragment

Google released the new Navigation UI library

So, now we can do the same fragment transitions from a your_named_navigation.xml resource (main > res > navigation > your_named_navigation.xml),

this an snippet code of my implementation:

<?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"
app:startDestination="@+id/first_fragment">

<fragment
android:id="@+id/first_fragment"
android:name="com.yourpackage.FirstFragment"
android:label="@string/title_first"
tools:layout="@layout/fragment_first">

<action
android:id="@+id/second_fragment_action"
app:destination="@id/second_fragment"
app:enterAnim="@anim/slide_in_right"
app:exitAnim="@anim/slide_out_left"
app:popEnterAnim="@anim/slide_in_left"
app:popExitAnim="@anim/slide_out_right" />

</fragment>

<fragment
android:id="@+id/second_fragment"
android:name="com.yourpackage.SecondFragment"
android:label="@string/title_second"
tools:layout="@layout/fragment_second">

<action ...next fragment/>

</fragment>

</navigation>

it also helps to handle clicks on back button and up button,

so, after have NavigationUi implementation in our proyect, we can call from our firstFragment instance the Navigation.findNavController method

myButton.setOnClickListener(View.OnClickListener {
//This opens our second fragment creating a stack of fragments
Navigation.findNavController(it).navigate(R.id.second_fragment_action)
})

The next Google's Codelab helped me, maybe can help you, greetings

Animate the transition between fragments

You need to use the new android.animation framework (object animators) with FragmentTransaction.setCustomAnimations as well as FragmentTransaction.setTransition.

Here's an example on using setCustomAnimations from ApiDemos' FragmentHideShow.java:

ft.setCustomAnimations(android.R.animator.fade_in, android.R.animator.fade_out);

and here's the relevant animator XML from res/animator/fade_in.xml:

<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@android:interpolator/accelerate_quad"
android:valueFrom="0"
android:valueTo="1"
android:propertyName="alpha"
android:duration="@android:integer/config_mediumAnimTime" />

Note that you can combine multiple animators using <set>, just as you could with the older animation framework.


EDIT: Since folks are asking about slide-in/slide-out, I'll comment on that here.

Slide-in and slide-out

You can of course animate the translationX, translationY, x, and y properties, but generally slides involve animating content to and from off-screen. As far as I know there aren't any transition properties that use relative values. However, this doesn't prevent you from writing them yourself. Remember that property animations simply require getter and setter methods on the objects you're animating (in this case views), so you can just create your own getXFraction and setXFraction methods on your view subclass, like this:

public class MyFrameLayout extends FrameLayout {
...
public float getXFraction() {
return getX() / getWidth(); // TODO: guard divide-by-zero
}

public void setXFraction(float xFraction) {
// TODO: cache width
final int width = getWidth();
setX((width > 0) ? (xFraction * width) : -9999);
}
...
}

Now you can animate the 'xFraction' property, like this:

res/animator/slide_in.xml:

<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@android:anim/linear_interpolator"
android:valueFrom="-1.0"
android:valueTo="0"
android:propertyName="xFraction"
android:duration="@android:integer/config_mediumAnimTime" />

Note that if the object you're animating in isn't the same width as its parent, things won't look quite right, so you may need to tweak your property implementation to suit your use case.

replace two fragments with each other using animation

Here is the right solution:

FragmentTransaction fragmentTransactionSearch = getChildFragmentManager().beginTransaction();
fragmentTransactionSearch.add(childSearchFragment, "SEARCH");
fragmentTransactionSearch.replace(R.id.area_for_fragments, childSearchFragment);
fragmentTransactionSearch.commit();

Thats the start to place the first fragment into the defined <FrameLayout>

If you want to replace this childFragment with an other childFragment, then use:

FragmentTransaction fragmentTransactionDetail = getChildFragmentManager().beginTransaction();
fragmentTransactionDetail.setCustomAnimations(R.anim.slide_in_up, R.anim.slide_out_up, R.anim.slide_out_down, R.anim.slide_in_down);
fragmentTransactionDetail.remove(childSearchFragment);
fragmentTransactionDetail.add(childDetailFragment, "DETAIL");
fragmentTransactionDetail.replace(R.id.area_for_fragments, childDetailFragment);
fragmentTransactionDetail.addToBackStack("ShowDetail");
fragmentTransactionDetail.commit();

And as an Extra. If you want to control the BackButtonFunction, then you have to Overwrite the OnBackPressed function in the parentFragment with following code:

 if(parentFragment.getChildFragmentManager().getBackStackEntryCount() > 0)
{ this.getChildFragmentManager().popBackStack(); }

Android Fragments and animation

To animate the transition between fragments, or to animate the process of showing or hiding a fragment you use the Fragment Manager to create a Fragment Transaction.

Within each Fragment Transaction you can specify in and out animations that will be used for show and hide respectively (or both when replace is used).

The following code shows how you would replace a fragment by sliding out one fragment and sliding the other one in it's place.

FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.setCustomAnimations(R.anim.slide_in_left, R.anim.slide_out_right);

DetailsFragment newFragment = DetailsFragment.newInstance();

ft.replace(R.id.details_fragment_container, newFragment, "detailFragment");

// Start the animated transition.
ft.commit();

To achieve the same thing with hiding or showing a fragment you'd simply call ft.show or ft.hide, passing in the Fragment you wish to show or hide respectively.

For reference, the XML animation definitions would use the objectAnimator tag. An example of slide_in_left might look something like this:

<?xml version="1.0" encoding="utf-8"?>
<set>
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
android:propertyName="x"
android:valueType="floatType"
android:valueFrom="-1280"
android:valueTo="0"
android:duration="500"/>
</set>

FragmentTransaction animation to slide in over top

I found a solution which works for me. I ended up using a ViewPager with a FragmentStatePagerAdapter. The ViewPager provides the swiping behavior and the FragmentStatePagerAdapter swaps in the fragments. The final trick to achieve the effect of having one page visible "under" the incoming page is to use a PageTransformer. The PageTransformer overrides the ViewPager's default transition between pages. Here is an example PageTransformer that achieves the effect with translation and a small amount of scaling on the left-hand side page.

public class ScalePageTransformer implements PageTransformer {
private static final float SCALE_FACTOR = 0.95f;

private final ViewPager mViewPager;

public ScalePageTransformer(ViewPager viewPager) {
this.mViewPager = viewPager;
}

@SuppressLint("NewApi")
@Override
public void transformPage(View page, float position) {
if (position <= 0) {
// apply zoom effect and offset translation only for pages to
// the left
final float transformValue = Math.abs(Math.abs(position) - 1) * (1.0f - SCALE_FACTOR) + SCALE_FACTOR;
int pageWidth = mViewPager.getWidth();
final float translateValue = position * -pageWidth;
page.setScaleX(transformValue);
page.setScaleY(transformValue);
if (translateValue > -pageWidth) {
page.setTranslationX(translateValue);
} else {
page.setTranslationX(0);
}
}
}

}

Doing a push fragment animation

Although this question is a duplicate, the answer that was provided on that question does not work for many viewers and requires them to make a few assumptions on their end.

This is an excellent question considering this functionality exists in many applications, however it requires a very elaborate answer. I will try to break the answer down into a series of compartmentalized steps to ensure that it is repeatable!

The Problem

The problem inhibiting most of us from doing this easily is one of a compound nature. To fully understand this problem, let me list the issues:

  • We should not use typical animation frameworks as the FragmentManager and FragmentTransaction classes provide a convenience method, setCustomAnimations(int, int, int int), that require ObjectAnimators
  • Further, we cannot use percentage markers in ObjectAnimators (the traditional approach)
  • Finally, we can only determine the screen width at runtime, therefore must have custom functionality in our own layout

The Solution

In order to tackle this problem and provide the immersive experience desired, we must tackle the it from many angles. These next few steps explain exactly how to acquire this functionality!

  1. We first need to define a custom layout for each Fragment as we must have runtime access to the width of the screen, and a self-contained method to manipulate the x-position of the View (the layout) based on this width.

    FractionTranslateLinearLayout.java

    public class FractionTranslateLinearLayout extends LinearLayout{

    private int screenWidth;
    private float fractionX;

    protected void onSizeChanged(int w, int h, int oldW, int oldh){

    // Assign the actual screen width to our class variable.
    screenWidth = w;

    super.onSizeChanged(w, h, oldW, oldH);
    }

    public float getFractionX(){

    return fractionX;
    }

    public void setFractionX(float xFraction){

    this.fractionX = xFraction;

    // When we modify the xFraction, we want to adjust the x translation
    // accordingly. Here, the scale is that if xFraction is -1, then
    // the layout is off screen to the left, if xFraction is 0, then the
    // layout is exactly on the screen, and if xFraction is 1, then the
    // layout is completely offscreen to the right.
    setX((screenWidth > 0) ? (xFraction * screenWidth) : 0);
    }
    }
  2. Now, since we have a special layout that will allow us to translate based on the physical width of the screen, we can use it in the associated Fragment XML files.

    fragment_1.xml

    <com.[your_package_here].FractionTranslateLinearLayout
    // Omitted namespace.
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <TextView
    android:id="@+id/text_view_1"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:text="Fragment 1" />

    </com.[your_package_here].FractionTranslateLinearLayout>

    fragment_2.xml

    <com.[your_package_here].FractionTranslateLinearLayout
    // Omitted namespace.
    android:id="@+id/layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <TextView
    android:id="@+id/text_view_2"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:text="Fragment 2" />

    </com.[your_package_here].FractionTranslateLinearLayout>
  3. Then, we must create the Fragment classes that will contain the logic to implement the transitions.

    Fragment1.java

    public class Fragment1 extends Fragment {

    public View onCreateView(LayoutInflater inf, ViewGroup vg, Bundle b){

    // Simply inflate the View from the .xml file.
    return inf.inflate(R.layout.fragment_1, vg, false);
    }
    }

    Fragment2.java

    public class Fragment2 extends Fragment {

    public View onCreateView(LayoutInflater inf, ViewGroup vg, Bundle b){

    // Simply inflate the View from the .xml file.
    return inf.inflate(R.layout.fragment_2, vg, false);
    }

    public void onActivityCreated (Bundle savedInstanceState){

    View v = getView();

    FractionTranslateLinearLayout layout;
    layout = (FractionTranslateLinearLayout) v.findViewById(R.id.layout);

    // Move the entire View off to the right of the screen for now.
    layout.setFractionX(1.0f);
    }
    }
  4. Let's now create the objectAnimator .xml files that we will use to translate the Views across the screen. Note that we will need four of these files because we need one for each process (out and in), and one for each side (left and right).

    slide_left_out.xml

    <objectAnimator
    // Omitted namespace.
    android:valueFrom="0"
    android:valueTo="-1"
    // This String must be the exact name of the class variable.
    android:propertyName="xFraction"
    android:valueType="floatType"
    // Duration in milliseconds.
    android:duration="500"/>

    slide_right_out.xml

    <objectAnimator
    // Omitted namespace.
    android:valueFrom="0"
    android:valueTo="1"
    // This String must be the exact name of the class variable.
    android:propertyName="xFraction"
    android:valueType="floatType"
    // Duration in milliseconds.
    android:duration="500"/>

    slide_left_in.xml

    <objectAnimator
    // Omitted namespace.
    android:valueFrom="-1"
    android:valueTo="0"
    // This String must be the exact name of the class variable.
    android:propertyName="xFraction"
    android:valueType="floatType"
    // Duration in milliseconds.
    android:duration="500"/>

    slide_right_in.xml

    <objectAnimator
    // Omitted namespace.
    android:valueFrom="1"
    android:valueTo="0"
    // This String must be the exact name of the class variable.
    android:propertyName="xFraction"
    android:valueType="floatType"
    // Duration in milliseconds.
    android:duration="500"/>

Note that these folders must be placed in the 'res/animator' directory in your project structure.

  1. Create the container layout that will hold each Fragment as we transition between them.

    main.xml

    <com.android.FrameLayout
    // Omitted namespace.
    android:id="@+id/main_container"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />
  2. Now, we must create the Activity that will wrap everything together!

    Main.java

    public class Main extends Activity {

    private boolean showingFirstFragment;

    public void onCreate(Bundle savedInstanceState){

    setContentView(R.layout.main);

    FragmentManager manager = getFragmentManager();

    FragmentTransaction trans = manager.beginTransaction();

    // Keep track of which Fragment we are facing.
    showingFirstFragment = true;

    // Add the first Fragment to the container.
    trans.add(R.id.main_container, new Fragment1(), "fragment_1");

    trans.commit();
    }

    public void onBackPressed(){

    // Override the back button functionality to simply switch
    // Fragments. Note that this would normally be done in a click
    // click listener.
    switchFragments();
    }

    private void switchFragments(){

    FragmentManager manager = getFragmentManager();
    FragmentTransaction trans = manager.beginTransaction();

    // Set the animations that will emulate the functionality you
    // requested.
    int rightIn = R.animator.slide_right_in;
    int rightOut = R.animator.slide_right_out;
    int leftIn = R.animator.slide_left_in;
    int leftOut = R.animator.slide_left_out;

    // Note that we pass in 4 animations here. Please see the
    // documentation on this method as it is critical to the
    // understanding of this solution.
    trans.setCustomAnimations(rightIn, leftOut, leftIn, rightOut);

    if(showingFirstFragment){

    int container = R.id.main_container;

    // Show the second Fragment.
    trans.replace(container, new Fragment2(), "fragment_2");
    trans.commit();

    showingFirstFragment = false;
    }
    else{

    // Show the first Fragment by popping the back stack!
    manager.popBackStack(null);

    showingFirstFragment = true;
    }
    }
    }

In this code example, this class is used heavily. Its methods are crucial to the execution of the process!

Some Considerations

Note that I have made a few assumptions here, and that this code is quite unique:

  • This example uses the original Fragment class, and thus will not work with the support package unless the necessary modifications are made.
  • This example requires that you are using Android API 13.0 or above.
  • The intent of this example was to be informative and to hopefully explain the issues involved here. Piecing the code together in your own editor is something that you should do at your own discretion!

Hopefully there is sufficient detail here to answer your question. Please let me know if you require any more.

At this point, there should be enough code to implement your own twist on the solution. I did type this up without the crutch of the Eclipse editor, so if there are any errors, please accept my apologies in advance!



Related Topics



Leave a reply



Submit