blob: 9bfd8d4a2965ce8fb3f91fe4e9e056978a85d019 [file] [log] [blame]
/*
* Copyright (C) 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.android.slidingfragments;
import android.animation.Animator;
import android.animation.Animator.AnimatorListener;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
import android.app.Activity;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.os.Bundle;
import android.view.View;
/**
* This application shows a simple technique to animate and overlay two fragments
* on top of each other in order to provide a more immersive experience,
* as opposed to only having full screen transitions. When additional content
* (text) related to the currently displayed content (image) is to be shown,
* the currently visible content can be moved into the background instead of
* being removed from the screen entirely. This effect can therefore
* provide a more natural way of displaying additional information to the user
* using a different fragment.
*
* In this specific demo, tapping on the screen toggles between the two
* animated states of the fragment. When the animation is called,
* the fragment with an image animates into the background while the fragment
* containing text slides up on top of it. When the animation is toggled once
* more, the text fragment slides back down and the image fragment regains
* focus.
*/
public class SlidingFragments extends Activity implements
OnTextFragmentAnimationEndListener, FragmentManager.OnBackStackChangedListener {
ImageFragment mImageFragment;
TextFragment mTextFragment;
View mDarkHoverView;
boolean mDidSlideOut = false;
boolean mIsAnimating = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.sliding_fragments_layout);
mDarkHoverView = findViewById(R.id.dark_hover_view);
mDarkHoverView.setAlpha(0);
mImageFragment = (ImageFragment) getFragmentManager().findFragmentById(R.id.move_fragment);
mTextFragment = new TextFragment();
getFragmentManager().addOnBackStackChangedListener(this);
mImageFragment.setClickListener(mClickListener);
mTextFragment.setClickListener(mClickListener);
mTextFragment.setOnTextFragmentAnimationEnd(this);
mDarkHoverView.setOnClickListener(mClickListener);
}
View.OnClickListener mClickListener = new View.OnClickListener () {
@Override
public void onClick(View view) {
switchFragments();
}
};
/**
* This method is used to toggle between the two fragment states by
* calling the appropriate animations between them. The entry and exit
* animations of the text fragment are specified in R.animator resource
* files. The entry and exit animations of the image fragment are
* specified in the slideBack and slideForward methods below. The reason
* for separating the animation logic in this way is because the translucent
* dark hover view must fade in at the same time as the image fragment
* animates into the background, which would be difficult to time
* properly given that the setCustomAnimations method can only modify the
* two fragments in the transaction.
*/
private void switchFragments () {
if (mIsAnimating) {
return;
}
mIsAnimating = true;
if (mDidSlideOut) {
mDidSlideOut = false;
getFragmentManager().popBackStack();
} else {
mDidSlideOut = true;
AnimatorListener listener = new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator arg0) {
FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.setCustomAnimations(R.animator.slide_fragment_in, 0, 0,
R.animator.slide_fragment_out);
transaction.add(R.id.move_to_back_container, mTextFragment);
transaction.addToBackStack(null);
transaction.commit();
}
};
slideBack (listener);
}
}
@Override
public void onBackStackChanged() {
if (!mDidSlideOut) {
slideForward(null);
}
}
/**
* This method animates the image fragment into the background by both
* scaling and rotating the fragment's view, as well as adding a
* translucent dark hover view to inform the user that it is inactive.
*/
public void slideBack(AnimatorListener listener)
{
View movingFragmentView = mImageFragment.getView();
PropertyValuesHolder rotateX = PropertyValuesHolder.ofFloat("rotationX", 40f);
PropertyValuesHolder scaleX = PropertyValuesHolder.ofFloat("scaleX", 0.8f);
PropertyValuesHolder scaleY = PropertyValuesHolder.ofFloat("scaleY", 0.8f);
ObjectAnimator movingFragmentAnimator = ObjectAnimator.
ofPropertyValuesHolder(movingFragmentView, rotateX, scaleX, scaleY);
ObjectAnimator darkHoverViewAnimator = ObjectAnimator.
ofFloat(mDarkHoverView, "alpha", 0.0f, 0.5f);
ObjectAnimator movingFragmentRotator = ObjectAnimator.
ofFloat(movingFragmentView, "rotationX", 0);
movingFragmentRotator.setStartDelay(getResources().
getInteger(R.integer.half_slide_up_down_duration));
AnimatorSet s = new AnimatorSet();
s.playTogether(movingFragmentAnimator, darkHoverViewAnimator, movingFragmentRotator);
s.addListener(listener);
s.start();
}
/**
* This method animates the image fragment into the foreground by both
* scaling and rotating the fragment's view, while also removing the
* previously added translucent dark hover view. Upon the completion of
* this animation, the image fragment regains focus since this method is
* called from the onBackStackChanged method.
*/
public void slideForward(AnimatorListener listener)
{
View movingFragmentView = mImageFragment.getView();
PropertyValuesHolder rotateX = PropertyValuesHolder.ofFloat("rotationX", 40f);
PropertyValuesHolder scaleX = PropertyValuesHolder.ofFloat("scaleX", 1.0f);
PropertyValuesHolder scaleY = PropertyValuesHolder.ofFloat("scaleY", 1.0f);
ObjectAnimator movingFragmentAnimator = ObjectAnimator.
ofPropertyValuesHolder(movingFragmentView, rotateX, scaleX, scaleY);
ObjectAnimator darkHoverViewAnimator = ObjectAnimator.
ofFloat(mDarkHoverView, "alpha", 0.5f, 0.0f);
ObjectAnimator movingFragmentRotator = ObjectAnimator.
ofFloat(movingFragmentView, "rotationX", 0);
movingFragmentRotator.setStartDelay(
getResources().getInteger(R.integer.half_slide_up_down_duration));
AnimatorSet s = new AnimatorSet();
s.playTogether(movingFragmentAnimator, movingFragmentRotator, darkHoverViewAnimator);
s.setStartDelay(getResources().getInteger(R.integer.slide_up_down_duration));
s.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
mIsAnimating = false;
}
});
s.start();
}
public void onAnimationEnd() {
mIsAnimating = false;
}
}