Home | History | Annotate | Download | only in slidingfragments
      1 /*
      2  * Copyright (C) 2013 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.example.android.slidingfragments;
     18 
     19 import android.animation.Animator;
     20 import android.animation.Animator.AnimatorListener;
     21 import android.animation.AnimatorListenerAdapter;
     22 import android.animation.AnimatorSet;
     23 import android.animation.ObjectAnimator;
     24 import android.animation.PropertyValuesHolder;
     25 import android.app.Activity;
     26 import android.app.FragmentManager;
     27 import android.app.FragmentTransaction;
     28 import android.os.Bundle;
     29 import android.view.View;
     30 
     31 /**
     32  * This application shows a simple technique to animate and overlay two fragments
     33  * on top of each other in order to provide a more immersive experience,
     34  * as opposed to only having full screen transitions. When additional content
     35  * (text) related to the currently displayed content (image) is to be shown,
     36  * the currently visible content can be moved into the background instead of
     37  * being removed from the screen entirely. This effect can therefore
     38  * provide a more natural way of displaying additional information to the user
     39  * using a different fragment.
     40  *
     41  * In this specific demo, tapping on the screen toggles between the two
     42  * animated states of the fragment. When the animation is called,
     43  * the fragment with an image animates into the background while the fragment
     44  * containing text slides up on top of it. When the animation is toggled once
     45  * more, the text fragment slides back down and the image fragment regains
     46  * focus.
     47  */
     48 public class SlidingFragments extends Activity implements
     49         OnTextFragmentAnimationEndListener, FragmentManager.OnBackStackChangedListener {
     50 
     51     ImageFragment mImageFragment;
     52     TextFragment mTextFragment;
     53     View mDarkHoverView;
     54 
     55     boolean mDidSlideOut = false;
     56     boolean mIsAnimating = false;
     57 
     58     @Override
     59     protected void onCreate(Bundle savedInstanceState) {
     60         super.onCreate(savedInstanceState);
     61         setContentView(R.layout.sliding_fragments_layout);
     62 
     63         mDarkHoverView = findViewById(R.id.dark_hover_view);
     64         mDarkHoverView.setAlpha(0);
     65 
     66         mImageFragment = (ImageFragment) getFragmentManager().findFragmentById(R.id.move_fragment);
     67         mTextFragment = new TextFragment();
     68 
     69         getFragmentManager().addOnBackStackChangedListener(this);
     70 
     71         mImageFragment.setClickListener(mClickListener);
     72         mTextFragment.setClickListener(mClickListener);
     73         mTextFragment.setOnTextFragmentAnimationEnd(this);
     74         mDarkHoverView.setOnClickListener(mClickListener);
     75 
     76     }
     77 
     78     View.OnClickListener mClickListener = new View.OnClickListener () {
     79         @Override
     80         public void onClick(View view) {
     81             switchFragments();
     82         }
     83     };
     84 
     85     /**
     86      * This method is used to toggle between the two fragment states by
     87      * calling the appropriate animations between them. The entry and exit
     88      * animations of the text fragment are specified in R.animator resource
     89      * files. The entry and exit animations of the image fragment are
     90      * specified in the slideBack and slideForward methods below. The reason
     91      * for separating the animation logic in this way is because the translucent
     92      * dark hover view must fade in at the same time as the image fragment
     93      * animates into the background, which would be difficult to time
     94      * properly given that the setCustomAnimations method can only modify the
     95      * two fragments in the transaction.
     96      */
     97     private void switchFragments () {
     98         if (mIsAnimating) {
     99             return;
    100         }
    101         mIsAnimating = true;
    102         if (mDidSlideOut) {
    103             mDidSlideOut = false;
    104             getFragmentManager().popBackStack();
    105         } else {
    106             mDidSlideOut = true;
    107 
    108             AnimatorListener listener = new AnimatorListenerAdapter() {
    109                 @Override
    110                 public void onAnimationEnd(Animator arg0) {
    111                     FragmentTransaction transaction = getFragmentManager().beginTransaction();
    112                     transaction.setCustomAnimations(R.animator.slide_fragment_in, 0, 0,
    113                             R.animator.slide_fragment_out);
    114                     transaction.add(R.id.move_to_back_container, mTextFragment);
    115                     transaction.addToBackStack(null);
    116                     transaction.commit();
    117                 }
    118             };
    119             slideBack (listener);
    120         }
    121     }
    122 
    123     @Override
    124     public void onBackStackChanged() {
    125         if (!mDidSlideOut) {
    126             slideForward(null);
    127         }
    128 
    129     }
    130 
    131     /**
    132      * This method animates the image fragment into the background by both
    133      * scaling and rotating the fragment's view, as well as adding a
    134      * translucent dark hover view to inform the user that it is inactive.
    135      */
    136     public void slideBack(AnimatorListener listener)
    137     {
    138         View movingFragmentView = mImageFragment.getView();
    139 
    140         PropertyValuesHolder rotateX =  PropertyValuesHolder.ofFloat("rotationX", 40f);
    141         PropertyValuesHolder scaleX =  PropertyValuesHolder.ofFloat("scaleX", 0.8f);
    142         PropertyValuesHolder scaleY =  PropertyValuesHolder.ofFloat("scaleY", 0.8f);
    143         ObjectAnimator movingFragmentAnimator = ObjectAnimator.
    144                 ofPropertyValuesHolder(movingFragmentView, rotateX, scaleX, scaleY);
    145 
    146         ObjectAnimator darkHoverViewAnimator = ObjectAnimator.
    147                 ofFloat(mDarkHoverView, "alpha", 0.0f, 0.5f);
    148 
    149         ObjectAnimator movingFragmentRotator = ObjectAnimator.
    150                 ofFloat(movingFragmentView, "rotationX", 0);
    151         movingFragmentRotator.setStartDelay(getResources().
    152                 getInteger(R.integer.half_slide_up_down_duration));
    153 
    154         AnimatorSet s = new AnimatorSet();
    155         s.playTogether(movingFragmentAnimator, darkHoverViewAnimator, movingFragmentRotator);
    156         s.addListener(listener);
    157         s.start();
    158     }
    159 
    160     /**
    161      * This method animates the image fragment into the foreground by both
    162      * scaling and rotating the fragment's view, while also removing the
    163      * previously added translucent dark hover view. Upon the completion of
    164      * this animation, the image fragment regains focus since this method is
    165      * called from the onBackStackChanged method.
    166      */
    167     public void slideForward(AnimatorListener listener)
    168     {
    169         View movingFragmentView = mImageFragment.getView();
    170 
    171         PropertyValuesHolder rotateX =  PropertyValuesHolder.ofFloat("rotationX", 40f);
    172         PropertyValuesHolder scaleX =  PropertyValuesHolder.ofFloat("scaleX", 1.0f);
    173         PropertyValuesHolder scaleY =  PropertyValuesHolder.ofFloat("scaleY", 1.0f);
    174         ObjectAnimator movingFragmentAnimator = ObjectAnimator.
    175                 ofPropertyValuesHolder(movingFragmentView, rotateX, scaleX, scaleY);
    176 
    177         ObjectAnimator darkHoverViewAnimator = ObjectAnimator.
    178                 ofFloat(mDarkHoverView, "alpha", 0.5f, 0.0f);
    179 
    180         ObjectAnimator movingFragmentRotator = ObjectAnimator.
    181                 ofFloat(movingFragmentView, "rotationX", 0);
    182         movingFragmentRotator.setStartDelay(
    183                 getResources().getInteger(R.integer.half_slide_up_down_duration));
    184 
    185         AnimatorSet s = new AnimatorSet();
    186         s.playTogether(movingFragmentAnimator, movingFragmentRotator, darkHoverViewAnimator);
    187         s.setStartDelay(getResources().getInteger(R.integer.slide_up_down_duration));
    188         s.addListener(new AnimatorListenerAdapter() {
    189             @Override
    190             public void onAnimationEnd(Animator animation) {
    191                 mIsAnimating = false;
    192             }
    193         });
    194         s.start();
    195     }
    196 
    197     public void onAnimationEnd() {
    198         mIsAnimating = false;
    199     }
    200 }
    201