package com.facebook.rebound.playground.examples;
import android.content.Context;
import android.content.res.Resources;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.widget.FrameLayout;
import com.facebook.rebound.SimpleSpringListener;
import com.facebook.rebound.Spring;
import com.facebook.rebound.SpringConfig;
import com.facebook.rebound.SpringConfigRegistry;
import com.facebook.rebound.SpringSystem;
import com.facebook.rebound.SpringUtil;
import com.facebook.rebound.playground.R;
import com.facebook.rebound.ui.SpringConfiguratorView;
import com.facebook.rebound.ui.Util;
public class OrigamiExample extends FrameLayout {
// Create a spring configuration based on Origami values from the Photo Grid example.
private static final SpringConfig ORIGAMI_SPRING_CONFIG = SpringConfig.fromOrigamiTensionAndFriction(40, 7);
private final Spring mSpring;
private final View mSelectedPhoto;
private final View mPhotoGrid;
private final View mFeedbackBar;
private final SpringConfiguratorView mSpringConfiguratorView;
public OrigamiExample(Context context) {
this(context, null);
}
public OrigamiExample(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public OrigamiExample(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// Inflate the layout.
LayoutInflater inflater = LayoutInflater.from(context);
ViewGroup root = (ViewGroup) inflater.inflate(R.layout.origami_example, this, false);
addView(root);
// Listen for clicks on the root view.
root.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
handleClick(v);
}
});
// Get references to our views.
mPhotoGrid = root.findViewById(R.id.grid);
mSelectedPhoto = root.findViewById(R.id.selection);
mFeedbackBar = root.findViewById(R.id.feedback);
mSpringConfiguratorView = (SpringConfiguratorView) root.findViewById(R.id.spring_configurator);
// Setup the Spring by creating a SpringSystem adding a SimpleListener that renders the
// animation whenever the spring is updated.
mSpring = SpringSystem
.create()
.createSpring()
.setSpringConfig(ORIGAMI_SPRING_CONFIG)
.addListener(new SimpleSpringListener() {
@Override
public void onSpringUpdate(Spring spring) {
// Just tell the UI to update based on the springs current state.
render();
}
});
// Here we just wait until the first layout pass finishes and call our render method to update
// the animation to the initial resting state of the spring.
mPhotoGrid.getViewTreeObserver().addOnGlobalLayoutListener(
new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
render();
mPhotoGrid.getViewTreeObserver().removeOnGlobalLayoutListener(this);
}
});
/** Optional - Live Spring Tuning **/
// Put our config into a registry. This is optional, but it gives you the ability to live tune
// the spring using the SpringConfiguratorView which will show up at the bottom of the screen.
SpringConfigRegistry.getInstance().addSpringConfig(ORIGAMI_SPRING_CONFIG, "origami animation spring");
// Tell the SpringConfiguratorView that we've updated the registry to allow you to live tune the animation spring.
mSpringConfiguratorView.refreshSpringConfigurations();
// Uncomment this line to actually show the SpringConfiguratorView allowing you to live tune
// the Spring constants as you manipulate the UI.
mSpringConfiguratorView.setVisibility(View.VISIBLE);
}
/**
* On click we just move the springs end state from 0 to 1. This allows the Spring to act much
* like an Origami switch.
*/
public void handleClick(View view) {
if (mSpring.getEndValue() == 0) {
mSpring.setEndValue(1);
} else {
mSpring.setEndValue(0);
}
}
/**
* This method takes the current state of the spring and maps it to all the values for each UI
* element that is animated on this spring. This allows the Spring to act as a common timing
* function for the animation ensuring that all element transitions are synchronized.
*
* You can think of these mappings as similiar to Origami transitions.
* SpringUtil#mapValueFromRangeToRange converts the spring's 0 to 1 transition and maps it to the
* range of animation for a property on a view such as translation, scale, rotation, and alpha.
*/
private void render() {
Resources resources = getResources();
// Get the current spring value.
double value = mSpring.getCurrentValue();
// Map the spring to the feedback bar position so that its hidden off screen and bounces in on tap.
float barPosition =
(float) SpringUtil.mapValueFromRangeToRange(value, 0, 1, mFeedbackBar.getHeight(), 0);
mFeedbackBar.setTranslationY(barPosition);
// Map the spring to the selected photo scale as it moves into and out of the grid.
float selectedPhotoScale =
(float) SpringUtil.mapValueFromRangeToRange(value, 0, 1, 0.33, 1);
selectedPhotoScale = Math.max(selectedPhotoScale, 0); // Clamp the value so we don't go below 0.
mSelectedPhoto.setScaleX(selectedPhotoScale);
mSelectedPhoto.setScaleY(selectedPhotoScale);
// Map the spring to the selected photo translation from its position in the grid
float selectedPhotoTranslateX = (float) SpringUtil.mapValueFromRangeToRange(value, 0, 1, Util.dpToPx(-106.667f, resources), 0);
float selectedPhotoTranslateY = (float) SpringUtil.mapValueFromRangeToRange(value, 0, 1, Util.dpToPx(46.667f, resources), 0);
mSelectedPhoto.setTranslationX(selectedPhotoTranslateX);
mSelectedPhoto.setTranslationY(selectedPhotoTranslateY);
// Map the spring to the photo grid alpha as it fades to black when the photo is selected.
float gridAlpha =
(float) SpringUtil.mapValueFromRangeToRange(value, 0, 1, 1, 0);
mPhotoGrid.setAlpha(gridAlpha);
// Map the spring to the photo grid scale so that it scales down slightly as the selected photo // zooms in.
float gridScale =
(float) SpringUtil.mapValueFromRangeToRange(value, 0, 1, 1, 0.95);
gridScale = Math.max(gridScale, 0); // Clamp the value so we don't go below 0.
mPhotoGrid.setScaleX(gridScale);
mPhotoGrid.setScaleY(gridScale);
}
}