Fragment Animation like Gmail Honeycomb App

2020-03-05 23:13发布

问题:

How can I recreate the layout animation affects you see in the Gmail app on Android 3.0+

I know you can use PropertyAnimators, ObjectAnimators, etc., but in my scenario, I have a several fragments on the screen which can be interchanged, so they're all contained within their own FrameLayouts, and all the FrameLayouts are in a RelativeLayout.

I can't figure out how to change the width of the FrameLayouts, since you have to edit that through the LayoutParams object.

Any ideas?

回答1:

My take on this can be viewed on GitHub here

It demonstrates one way of hiding a fragment with an animation, like Gmail does.

The animation isn't perfect as the left fragment is moving slightly faster than the right fragment is.

The method that does the hiding defines the animation in code and looks as follows:

/**
 * Besides showing/hiding the left fragment, this method specifies that a
 * layout animation should be used. It is defined as a sliding animation.
 * The right fragment will use the default animation, which is a sliding
 * animation also.
 * 
 * If the left fragment's animation is removed from this method, the default
 * animation will be used which is a fading animation.
 * 
 * Please note that this method will only have an effect in those screen
 * configurations where the list is hideable; by default, a width between
 * 600 and 1024 dip which corresponds to a portrait view on tablets. Change
 * the boolean value in layout_constants.xml to allow for it in other screen
 * sizes.
 * 
 * @param visible
 */
protected void setLeftFragmentVisible(boolean visible) {
    if (leftFragment != null && (leftFragment.isVisible() || visible)
            && getResources().getBoolean(R.bool.leftHideable)) {
        final float listWidth = getLeftFragment().getView().getWidth();
        ViewGroup container = (ViewGroup) findViewById(R.id.dual_layout);
        // Don't clip the children, we want to draw the entire fragment even
        // if it is partially off-screen.
        container.setClipChildren(false);
        final LayoutTransition trans = container.getLayoutTransition();
        /**
         * This specifies the delay before the leftFragment will appear.
         * Change if you want the right fragment to move before.
         */
        trans.setStartDelay(LayoutTransition.APPEARING, 0);
        /**
         * This is the delay before the right fragment will start to occupy
         * the space left by the left fragment
         */
        trans.setStartDelay(LayoutTransition.CHANGE_DISAPPEARING, 100);

        /**
         * Adding, specifies that the left fragment should animate by
         * sliding into view.
         */
        ObjectAnimator animIn = ObjectAnimator.ofFloat(null, "x",
                -listWidth, 0f).setDuration(
                trans.getDuration(LayoutTransition.CHANGE_APPEARING));
        trans.setAnimator(LayoutTransition.APPEARING, animIn);

        /**
         * Removing, specifies that the left fragment should animate by
         * sliding out of view.
         */
        ObjectAnimator animOut = ObjectAnimator.ofFloat(null, "x", 0f,
                -listWidth).setDuration(
                trans.getDuration(LayoutTransition.CHANGE_DISAPPEARING));
        trans.setAnimator(LayoutTransition.DISAPPEARING, animOut);

        FragmentManager fragmentManager = getFragmentManager();
        FragmentTransaction fragmentTransaction = fragmentManager
                .beginTransaction();
        if (getLeftFragment().isVisible()) {
            fragmentTransaction.hide(getLeftFragment());
        } else {
            fragmentTransaction.show(getLeftFragment());
        }
        // The hiding/showing will automatically initiate the animations
        // since
        // we have specified that we want layout animations in the layout
        // xml
        fragmentTransaction.commit();

        /*
         * Display home as up to be able to view the list
         */
        getActionBar().setDisplayHomeAsUpEnabled(!visible);
    }
}

To make this work, you need to define that you want layout transition to be animated. One line in the top of your layout's xml structure will do that:

android:animateLayoutChanges="true"

If you don't want custom animations, you can remove everything before FragmentManager fragmentManager = getFragmentManager().

Now in my example I use fragments but the same principle should apply to any view.

Edit: Instead of changing the widths of your views manually, you should use layout weights instead to allow automatic resizing.