|  | @@ -3,27 +3,38 @@ package com.reactnativenavigation.views;
 | 
	
		
			
			| 3 | 3 |  import android.animation.Animator;
 | 
	
		
			
			| 4 | 4 |  import android.animation.AnimatorListenerAdapter;
 | 
	
		
			
			| 5 | 5 |  import android.graphics.drawable.Drawable;
 | 
	
		
			
			|  | 6 | +import android.support.annotation.FloatRange;
 | 
	
		
			
			|  | 7 | +import android.support.annotation.NonNull;
 | 
	
		
			
			| 6 | 8 |  import android.support.design.widget.CoordinatorLayout;
 | 
	
		
			
			| 7 | 9 |  import android.support.design.widget.FloatingActionButton;
 | 
	
		
			
			| 8 | 10 |  import android.view.Gravity;
 | 
	
		
			
			| 9 | 11 |  import android.view.View;
 | 
	
		
			
			| 10 | 12 |  import android.view.ViewGroup;
 | 
	
		
			
			| 11 | 13 |  
 | 
	
		
			
			|  | 14 | +import com.reactnativenavigation.params.FabActionParams;
 | 
	
		
			
			| 12 | 15 |  import com.reactnativenavigation.params.FabParams;
 | 
	
		
			
			| 13 | 16 |  import com.reactnativenavigation.utils.ViewUtils;
 | 
	
		
			
			| 14 | 17 |  
 | 
	
		
			
			|  | 18 | +import java.util.ArrayList;
 | 
	
		
			
			|  | 19 | +
 | 
	
		
			
			| 15 | 20 |  public class FloatingActionButtonCoordinator {
 | 
	
		
			
			| 16 | 21 |  
 | 
	
		
			
			|  | 22 | +    private static final int INITIAL_EXPENDED_FAB_ROTATION = -90;
 | 
	
		
			
			| 17 | 23 |      private CoordinatorLayout parent;
 | 
	
		
			
			| 18 | 24 |      private FabParams params;
 | 
	
		
			
			| 19 |  | -    FloatingActionButton collapsedFab;
 | 
	
		
			
			| 20 |  | -    FloatingActionButton expendedFab;
 | 
	
		
			
			| 21 |  | -    final int crossFadeAnimationDuration;
 | 
	
		
			
			|  | 25 | +    private FloatingActionButton collapsedFab;
 | 
	
		
			
			|  | 26 | +    private FloatingActionButton expendedFab;
 | 
	
		
			
			|  | 27 | +    private final int crossFadeAnimationDuration;
 | 
	
		
			
			|  | 28 | +    private final int actionSize;
 | 
	
		
			
			|  | 29 | +    final int margin = (int) ViewUtils.convertDpToPixel(16);
 | 
	
		
			
			|  | 30 | +    private final ArrayList<FloatingActionButton> actions;
 | 
	
		
			
			| 22 | 31 |  
 | 
	
		
			
			| 23 | 32 |      public FloatingActionButtonCoordinator(CoordinatorLayout parent, FabParams params) {
 | 
	
		
			
			| 24 | 33 |          this.parent = parent;
 | 
	
		
			
			| 25 | 34 |          this.params = params;
 | 
	
		
			
			|  | 35 | +        actions = new ArrayList<>();
 | 
	
		
			
			| 26 | 36 |          crossFadeAnimationDuration = parent.getResources().getInteger(android.R.integer.config_shortAnimTime);
 | 
	
		
			
			|  | 37 | +        actionSize = (int) ViewUtils.convertDpToPixel(40);
 | 
	
		
			
			| 27 | 38 |          createCollapsedFab();
 | 
	
		
			
			| 28 | 39 |          createExpendedFab();
 | 
	
		
			
			| 29 | 40 |          setStyle();
 | 
	
	
		
			
			|  | @@ -31,19 +42,22 @@ public class FloatingActionButtonCoordinator {
 | 
	
		
			
			| 31 | 42 |  
 | 
	
		
			
			| 32 | 43 |      private void createCollapsedFab() {
 | 
	
		
			
			| 33 | 44 |          collapsedFab = createFab(params.collapsedIcon);
 | 
	
		
			
			|  | 45 | +        parent.addView(collapsedFab, createFabLayoutParams());
 | 
	
		
			
			| 34 | 46 |          collapsedFab.setOnClickListener(new View.OnClickListener() {
 | 
	
		
			
			| 35 | 47 |              @Override
 | 
	
		
			
			| 36 | 48 |              public void onClick(View v) {
 | 
	
		
			
			| 37 | 49 |                  hideCollapsed();
 | 
	
		
			
			| 38 | 50 |                  showExpended();
 | 
	
		
			
			|  | 51 | +                showActions();
 | 
	
		
			
			| 39 | 52 |              }
 | 
	
		
			
			| 40 | 53 |          });
 | 
	
		
			
			| 41 | 54 |      }
 | 
	
		
			
			| 42 | 55 |  
 | 
	
		
			
			| 43 | 56 |      private void createExpendedFab() {
 | 
	
		
			
			| 44 | 57 |          expendedFab = createFab(params.expendedIcon);
 | 
	
		
			
			|  | 58 | +        parent.addView(expendedFab, createFabLayoutParams());
 | 
	
		
			
			| 45 | 59 |          expendedFab.setVisibility(View.GONE);
 | 
	
		
			
			| 46 |  | -        expendedFab.setRotation(-90);
 | 
	
		
			
			|  | 60 | +        expendedFab.setRotation(INITIAL_EXPENDED_FAB_ROTATION);
 | 
	
		
			
			| 47 | 61 |          expendedFab.setOnClickListener(new View.OnClickListener() {
 | 
	
		
			
			| 48 | 62 |              @Override
 | 
	
		
			
			| 49 | 63 |              public void onClick(View v) {
 | 
	
	
		
			
			|  | @@ -53,6 +67,13 @@ public class FloatingActionButtonCoordinator {
 | 
	
		
			
			| 53 | 67 |          });
 | 
	
		
			
			| 54 | 68 |      }
 | 
	
		
			
			| 55 | 69 |  
 | 
	
		
			
			|  | 70 | +    private FloatingActionButton createFab(Drawable icon) {
 | 
	
		
			
			|  | 71 | +        FloatingActionButton fab = new FloatingActionButton(parent.getContext());
 | 
	
		
			
			|  | 72 | +        fab.setId(ViewUtils.generateViewId());
 | 
	
		
			
			|  | 73 | +        fab.setImageDrawable(icon);
 | 
	
		
			
			|  | 74 | +        return fab;
 | 
	
		
			
			|  | 75 | +    }
 | 
	
		
			
			|  | 76 | +
 | 
	
		
			
			| 56 | 77 |      private void hideCollapsed() {
 | 
	
		
			
			| 57 | 78 |          animateFab(collapsedFab, 0, 90);
 | 
	
		
			
			| 58 | 79 |      }
 | 
	
	
		
			
			|  | @@ -90,20 +111,12 @@ public class FloatingActionButtonCoordinator {
 | 
	
		
			
			| 90 | 111 |                  .start();
 | 
	
		
			
			| 91 | 112 |      }
 | 
	
		
			
			| 92 | 113 |  
 | 
	
		
			
			| 93 |  | -
 | 
	
		
			
			| 94 |  | -    private FloatingActionButton createFab(Drawable icon) {
 | 
	
		
			
			| 95 |  | -        FloatingActionButton fab = new FloatingActionButton(parent.getContext());
 | 
	
		
			
			| 96 |  | -        fab.setImageDrawable(icon);
 | 
	
		
			
			| 97 |  | -        parent.addView(fab, createFabLayoutParams());
 | 
	
		
			
			| 98 |  | -        return fab;
 | 
	
		
			
			| 99 |  | -    }
 | 
	
		
			
			| 100 |  | -
 | 
	
		
			
			| 101 | 114 |      private CoordinatorLayout.LayoutParams createFabLayoutParams() {
 | 
	
		
			
			| 102 | 115 |          final CoordinatorLayout.LayoutParams lp = new CoordinatorLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
 | 
	
		
			
			| 103 | 116 |          lp.gravity = Gravity.RIGHT | Gravity.BOTTOM;
 | 
	
		
			
			| 104 |  | -        final int margin = (int) ViewUtils.convertDpToPixel(16);
 | 
	
		
			
			| 105 | 117 |          lp.bottomMargin = margin;
 | 
	
		
			
			| 106 | 118 |          lp.rightMargin = margin;
 | 
	
		
			
			|  | 119 | +        lp.topMargin = margin;
 | 
	
		
			
			| 107 | 120 |          return lp;
 | 
	
		
			
			| 108 | 121 |      }
 | 
	
		
			
			| 109 | 122 |  
 | 
	
	
		
			
			|  | @@ -114,4 +127,71 @@ public class FloatingActionButtonCoordinator {
 | 
	
		
			
			| 114 | 127 |      public void show() {
 | 
	
		
			
			| 115 | 128 |  
 | 
	
		
			
			| 116 | 129 |      }
 | 
	
		
			
			|  | 130 | +
 | 
	
		
			
			|  | 131 | +    private void showActions() {
 | 
	
		
			
			|  | 132 | +        if (actions.size() > 0) {
 | 
	
		
			
			|  | 133 | +            return;
 | 
	
		
			
			|  | 134 | +        }
 | 
	
		
			
			|  | 135 | +
 | 
	
		
			
			|  | 136 | +        for (int i = 0; i < params.actions.size(); i++) {
 | 
	
		
			
			|  | 137 | +            FloatingActionButton action = createAction(i);
 | 
	
		
			
			|  | 138 | +            actions.add(action);
 | 
	
		
			
			|  | 139 | +            parent.addView(action);
 | 
	
		
			
			|  | 140 | +        }
 | 
	
		
			
			|  | 141 | +    }
 | 
	
		
			
			|  | 142 | +
 | 
	
		
			
			|  | 143 | +    private FloatingActionButton createAction(int index) {
 | 
	
		
			
			|  | 144 | +        FabActionParams actionParams = params.actions.get(index);
 | 
	
		
			
			|  | 145 | +        FloatingActionButton action = createFab(actionParams.icon);
 | 
	
		
			
			|  | 146 | +        action.setLayoutParams(createActionLayoutParams(index));
 | 
	
		
			
			|  | 147 | +     //   action.setAlpha(0);
 | 
	
		
			
			|  | 148 | +        return action;
 | 
	
		
			
			|  | 149 | +    }
 | 
	
		
			
			|  | 150 | +
 | 
	
		
			
			|  | 151 | +    @NonNull
 | 
	
		
			
			|  | 152 | +    private CoordinatorLayout.LayoutParams createActionLayoutParams(int actionIndex) {
 | 
	
		
			
			|  | 153 | +        CoordinatorLayout.LayoutParams lp = new CoordinatorLayout.LayoutParams(actionSize, actionSize);
 | 
	
		
			
			|  | 154 | +        lp.setAnchorId(expendedFab.getId());
 | 
	
		
			
			|  | 155 | +        lp.anchorGravity = Gravity.CENTER_HORIZONTAL;
 | 
	
		
			
			|  | 156 | +        lp.setBehavior(new ActionBehaviour(expendedFab, (actionIndex + 1) * (actionSize + margin / 2)));
 | 
	
		
			
			|  | 157 | +        return lp;
 | 
	
		
			
			|  | 158 | +    }
 | 
	
		
			
			|  | 159 | +
 | 
	
		
			
			|  | 160 | +    public static class ActionBehaviour extends CoordinatorLayout.Behavior<FloatingActionButton> {
 | 
	
		
			
			|  | 161 | +        private final int MAX_VALUE = 90;
 | 
	
		
			
			|  | 162 | +        private int dependencyId;
 | 
	
		
			
			|  | 163 | +        private float yStep;
 | 
	
		
			
			|  | 164 | +
 | 
	
		
			
			|  | 165 | +        public ActionBehaviour(View anchor, float yStep) {
 | 
	
		
			
			|  | 166 | +            this.yStep = yStep;
 | 
	
		
			
			|  | 167 | +            this.dependencyId = anchor.getId();
 | 
	
		
			
			|  | 168 | +        }
 | 
	
		
			
			|  | 169 | +
 | 
	
		
			
			|  | 170 | +        @Override
 | 
	
		
			
			|  | 171 | +        public boolean layoutDependsOn(CoordinatorLayout parent, FloatingActionButton child, View dependency) {
 | 
	
		
			
			|  | 172 | +            return dependency.getId() == dependencyId;
 | 
	
		
			
			|  | 173 | +        }
 | 
	
		
			
			|  | 174 | +
 | 
	
		
			
			|  | 175 | +        @Override
 | 
	
		
			
			|  | 176 | +        public boolean onDependentViewChanged(CoordinatorLayout parent, FloatingActionButton child, View dependency) {
 | 
	
		
			
			|  | 177 | +            final float dependentValue = dependency.getRotation();
 | 
	
		
			
			|  | 178 | +            float fraction = calculateTransitionFraction(dependentValue);
 | 
	
		
			
			|  | 179 | +            child.setY(calculateY(parent, fraction));
 | 
	
		
			
			|  | 180 | +            child.setAlpha(calculateAlpha(fraction));
 | 
	
		
			
			|  | 181 | +            return true;
 | 
	
		
			
			|  | 182 | +        }
 | 
	
		
			
			|  | 183 | +
 | 
	
		
			
			|  | 184 | +        private float calculateAlpha(float fraction) {
 | 
	
		
			
			|  | 185 | +            return 1 * fraction;
 | 
	
		
			
			|  | 186 | +        }
 | 
	
		
			
			|  | 187 | +
 | 
	
		
			
			|  | 188 | +        private float calculateY(CoordinatorLayout parent, float fraction) {
 | 
	
		
			
			|  | 189 | +            return parent.findViewById(dependencyId).getY() - yStep * fraction;
 | 
	
		
			
			|  | 190 | +        }
 | 
	
		
			
			|  | 191 | +
 | 
	
		
			
			|  | 192 | +        @FloatRange(from=0.0, to=1.0)
 | 
	
		
			
			|  | 193 | +        private float calculateTransitionFraction(float dependentValue) {
 | 
	
		
			
			|  | 194 | +            return 1 - Math.abs(dependentValue / MAX_VALUE);
 | 
	
		
			
			|  | 195 | +        }
 | 
	
		
			
			|  | 196 | +    }
 | 
	
		
			
			| 117 | 197 |  }
 |