package com.tumblr.backboard.example; import android.app.Fragment; import android.content.res.TypedArray; import android.os.Bundle; import android.util.TypedValue; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.widget.RelativeLayout; import com.facebook.rebound.Spring; import com.facebook.rebound.SpringSystem; import com.tumblr.backboard.Actor; import com.tumblr.backboard.MotionProperty; import com.tumblr.backboard.imitator.Imitator; import com.tumblr.backboard.imitator.MotionImitator; import com.tumblr.backboard.imitator.ToggleImitator; import com.tumblr.backboard.performer.MapPerformer; /** * A ring of views that bloom and then contract, with a selector that follows the finger. * <p/> * Created by ericleong on 5/7/14. */ public class FlowerFragment extends Fragment { private static final int DIAMETER = 50; private static final int RING_DIAMETER = 7 * DIAMETER; private RelativeLayout mRootView; private View mCircle; private View[] mCircles; private static final int OPEN = 1; private static final int CLOSED = 0; private static double distSq(double x1, double y1, double x2, double y2) { return Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2); } private static View nearest(float x, float y, View[] views) { double minDistSq = Double.MAX_VALUE; View minView = null; for (View view : views) { double distSq = distSq(x, y, view.getX() + view.getMeasuredWidth() / 2, view.getY() + view.getMeasuredHeight() / 2); if (distSq < Math.pow(1.5f * view.getMeasuredWidth(), 2) && distSq < minDistSq) { minDistSq = distSq; minView = view; } } return minView; } /** * Snaps to the nearest circle. */ private class SnapImitator extends MotionImitator { public SnapImitator(MotionProperty property) { super(property, 0, Imitator.TRACK_ABSOLUTE, Imitator.FOLLOW_SPRING); } @Override public void mime(float offset, float value, float delta, float dt, MotionEvent event) { // find the nearest view final View nearest = nearest( event.getX() + mCircle.getX(), event.getY() + mCircle.getY(), mCircles); if (nearest != null) { // snap to it - remember to compensate for translation switch (mProperty) { case X: getSpring().setEndValue(nearest.getX() + nearest.getWidth() / 2 - mCircle.getLeft() - mCircle.getWidth() / 2); break; case Y: getSpring().setEndValue(nearest.getY() + nearest.getHeight() / 2 - mCircle.getTop() - mCircle.getHeight() / 2); break; } } else { // follow finger super.mime(offset, value, delta, dt, event); } } } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { mRootView = (RelativeLayout) inflater.inflate(R.layout.fragment_flower, container, false); mCircles = new View[6]; mCircle = mRootView.findViewById(R.id.circle); final float diameter = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, DIAMETER, getResources().getDisplayMetrics()); final TypedArray circles = getResources().obtainTypedArray(R.array.circles); // layout params final RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams((int) diameter, (int) diameter); params.addRule(RelativeLayout.CENTER_IN_PARENT); // create the circle views int colorIndex = 0; for (int i = 0; i < mCircles.length; i++) { mCircles[i] = new View(getActivity()); mCircles[i].setLayoutParams(params); mCircles[i].setBackgroundDrawable(getResources().getDrawable( circles.getResourceId(colorIndex, -1))); colorIndex++; if (colorIndex >= circles.length()) { colorIndex = 0; } mRootView.addView(mCircles[i], 0); } circles.recycle(); /* Animations! */ final SpringSystem springSystem = SpringSystem.create(); // create spring final Spring spring = springSystem.createSpring(); // add listeners along arc final double arc = 2 * Math.PI / mCircles.length; for (int i = 0; i < mCircles.length; i++) { View view = mCircles[i]; // map spring to a line segment from the center to the edge of the ring spring.addListener(new MapPerformer(view, View.TRANSLATION_X, 0, 1, 0, (float) (RING_DIAMETER * Math.cos(i * arc)))); spring.addListener(new MapPerformer(view, View.TRANSLATION_Y, 0, 1, 0, (float) (RING_DIAMETER * Math.sin(i * arc)))); spring.setEndValue(CLOSED); } final ToggleImitator imitator = new ToggleImitator(spring, CLOSED, OPEN); // move circle using finger, snap when near another circle, and bloom when touched new Actor.Builder(SpringSystem.create(), mCircle) .addMotion(new SnapImitator(MotionProperty.X), View.TRANSLATION_X) .addMotion(new SnapImitator(MotionProperty.Y), View.TRANSLATION_Y) .onTouchListener(imitator) .build(); return mRootView; } }