Skip to content
This repository has been archived by the owner on Oct 23, 2020. It is now read-only.

Commit

Permalink
Merge pull request #5 from Clans/dev
Browse files Browse the repository at this point in the history
Added option to set custom AnimatorSet to animate FAM icon. Misc optimiz...
  • Loading branch information
Clans committed Mar 29, 2015
2 parents a55c1e7 + c08cd41 commit 4d6db15
Show file tree
Hide file tree
Showing 11 changed files with 181 additions and 89 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,16 @@ public class FloatingActionButton extends ImageButton {
public static final int SIZE_NORMAL = 0;
public static final int SIZE_MINI = 1;

private int mFabSize;
int mFabSize;
boolean mShowShadow;
int mShadowColor;
int mShadowRadius = Util.dpToPx(getContext(), 4f);
int mShadowXOffset = Util.dpToPx(getContext(), 1f);
int mShadowYOffset = Util.dpToPx(getContext(), 3f);

private int mColorNormal;
private int mColorPressed;
private int mColorRipple;
private boolean mShowShadow;
private int mShadowColor;
private int mShadowRadius = Util.dpToPx(getContext(), 4f);
private int mShadowXOffset = Util.dpToPx(getContext(), 1f);
private int mShadowYOffset = Util.dpToPx(getContext(), 3f);
private Drawable mIcon;
private int mIconSize = Util.dpToPx(getContext(), 24f);
private Animation mShowAnimation;
Expand Down Expand Up @@ -254,7 +255,6 @@ void setColors(int colorNormal, int colorPressed, int colorRipple) {
mColorNormal = colorNormal;
mColorPressed = colorPressed;
mColorRipple = colorRipple;
updateBackground();
}

@TargetApi(Build.VERSION_CODES.LOLLIPOP)
Expand Down
157 changes: 85 additions & 72 deletions library/src/main/java/com/github/clans/fab/FloatingActionMenu.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,8 @@
import android.animation.ObjectAnimator;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.os.Handler;
import android.text.TextUtils;
import android.util.AttributeSet;
Expand All @@ -19,15 +17,17 @@
import android.view.animation.AnticipateInterpolator;
import android.view.animation.Interpolator;
import android.view.animation.OvershootInterpolator;
import android.widget.ImageView;

public class FloatingActionMenu extends ViewGroup {

private static final int ANIMATION_DURATION = 300;
private static final float CLOSED_PLUS_ROTATION = 0f;
private static final float OPENED_PLUS_ROTATION = -90f - 45f;

private AnimatorSet mOpenAnimatorSet = new AnimatorSet().setDuration(ANIMATION_DURATION);
private AnimatorSet mCloseAnimatorSet = new AnimatorSet().setDuration(ANIMATION_DURATION);
private AnimatorSet mOpenAnimatorSet = new AnimatorSet();
private AnimatorSet mCloseAnimatorSet = new AnimatorSet();
private AnimatorSet mIconToggleSet;

private int mButtonSpacing = Util.dpToPx(getContext(), 0f);
private FloatingActionButton mMenuButton;
Expand Down Expand Up @@ -69,6 +69,7 @@ public class FloatingActionMenu extends ViewGroup {
private int mMenuFabSize;
private int mLabelsStyle;
private boolean mIconAnimated = true;
private ImageView mImageToggle;

private OnMenuToggleListener mToggleListener;

Expand Down Expand Up @@ -145,42 +146,47 @@ private void initPadding(int padding) {
}

private void createMenuButton() {
mMenuButton = new FloatingActionButton(getContext()) {
mMenuButton = new FloatingActionButton(getContext());

@Override
protected Drawable getIconDrawable() {
RotatingDrawable rotatingDrawable = new RotatingDrawable(mIcon);

ObjectAnimator collapseAnimator = ObjectAnimator.ofFloat(rotatingDrawable, "rotation", OPENED_PLUS_ROTATION, CLOSED_PLUS_ROTATION);
ObjectAnimator expandAnimator = ObjectAnimator.ofFloat(rotatingDrawable, "rotation", CLOSED_PLUS_ROTATION, OPENED_PLUS_ROTATION);

mOpenAnimatorSet.play(expandAnimator);
mCloseAnimatorSet.play(collapseAnimator);

mOpenAnimatorSet.setInterpolator(mOpenInterpolator);
mCloseAnimatorSet.setInterpolator(mCloseInterpolator);

return rotatingDrawable;
}
};

mMenuButton.setShowShadow(mMenuShowShadow);
mMenuButton.mShowShadow = mMenuShowShadow;
if (mMenuShowShadow) {
mMenuButton.setShadowRadius(mMenuShadowRadius);
mMenuButton.setShadowXOffset(mMenuShadowXOffset);
mMenuButton.setShadowYOffset(mMenuShadowYOffset);
mMenuButton.mShadowRadius = Util.dpToPx(getContext(), mMenuShadowRadius);
mMenuButton.mShadowXOffset = Util.dpToPx(getContext(), mMenuShadowXOffset);
mMenuButton.mShadowYOffset = Util.dpToPx(getContext(), mMenuShadowYOffset);
}
mMenuButton.setColors(mMenuColorNormal, mMenuColorPressed, mMenuColorRipple);
mMenuButton.setShadowColor(mMenuShadowColor);
mMenuButton.setButtonSize(mMenuFabSize);
mMenuButton.mShadowColor = mMenuShadowColor;
mMenuButton.mFabSize = mMenuFabSize;
mMenuButton.updateBackground();

mMenuButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
toggle(mIsAnimated);
}
});

mImageToggle = new ImageView(getContext());
mImageToggle.setImageDrawable(mIcon);

addView(mMenuButton, super.generateDefaultLayoutParams());
addView(mImageToggle);

createDefaultIconAnimation();
}

private void createDefaultIconAnimation() {
ObjectAnimator collapseAnimator = ObjectAnimator.ofFloat(mImageToggle, "rotation", OPENED_PLUS_ROTATION, CLOSED_PLUS_ROTATION);
ObjectAnimator expandAnimator = ObjectAnimator.ofFloat(mImageToggle, "rotation", CLOSED_PLUS_ROTATION, OPENED_PLUS_ROTATION);

mOpenAnimatorSet.play(expandAnimator);
mCloseAnimatorSet.play(collapseAnimator);

mOpenAnimatorSet.setInterpolator(mOpenInterpolator);
mCloseAnimatorSet.setInterpolator(mCloseInterpolator);

mOpenAnimatorSet.setDuration(ANIMATION_DURATION);
mCloseAnimatorSet.setDuration(ANIMATION_DURATION);
}

@Override
Expand All @@ -190,10 +196,12 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
mMaxButtonWidth = 0;
int maxLabelWidth = 0;

measureChildWithMargins(mImageToggle, widthMeasureSpec, 0, heightMeasureSpec, 0);

for (int i = 0; i < mButtonsCount; i++) {
View child = getChildAt(i);

if (child.getVisibility() == GONE) continue;
if (child.getVisibility() == GONE || child == mImageToggle) continue;

measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
mMaxButtonWidth = Math.max(mMaxButtonWidth, child.getMeasuredWidth());
Expand All @@ -203,7 +211,7 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int usedWidth = 0;
View child = getChildAt(i);

if (child.getVisibility() == GONE) continue;
if (child.getVisibility() == GONE || child == mImageToggle) continue;

usedWidth += child.getMeasuredWidth();
height += child.getMeasuredHeight();
Expand Down Expand Up @@ -235,30 +243,39 @@ protected void onLayout(boolean changed, int l, int t, int r, int b) {
mMenuButton.layout(menuButtonLeft, menuButtonTop, menuButtonLeft + mMenuButton.getMeasuredWidth(),
menuButtonTop + mMenuButton.getMeasuredHeight());

int imageLeft = buttonsHorizontalCenter - mImageToggle.getMeasuredWidth() / 2;
int imageTop = menuButtonTop + mMenuButton.getMeasuredHeight() / 2 - mImageToggle.getMeasuredHeight() / 2;

mImageToggle.layout(imageLeft, imageTop, imageLeft + mImageToggle.getMeasuredWidth(),
imageTop + mImageToggle.getMeasuredHeight());

int nextY = menuButtonTop - mButtonSpacing;

for (int i = mButtonsCount - 1; i >= 0; i--) {
FloatingActionButton child = (FloatingActionButton) getChildAt(i);
View child = getChildAt(i);

if (child == mMenuButton || child.getVisibility() == GONE) continue;
if (child == mImageToggle) continue;

int childX = buttonsHorizontalCenter - child.getMeasuredWidth() / 2;
int childY = nextY - child.getMeasuredHeight();
child.layout(childX, childY, childX + child.getMeasuredWidth(),
childY + child.getMeasuredHeight());
FloatingActionButton fab = (FloatingActionButton) child;

if (fab == mMenuButton || fab.getVisibility() == GONE) continue;

int childX = buttonsHorizontalCenter - fab.getMeasuredWidth() / 2;
int childY = nextY - fab.getMeasuredHeight();
fab.layout(childX, childY, childX + fab.getMeasuredWidth(),
childY + fab.getMeasuredHeight());

if (!mMenuOpened) {
child.hide(false);
fab.hide(false);
}

View label = (View) child.getTag(R.id.fab_label);
View label = (View) fab.getTag(R.id.fab_label);
if (label != null) {
int labelsOffset = child.getMeasuredWidth() / 2 + mLabelsMargin;
int labelsOffset = fab.getMeasuredWidth() / 2 + mLabelsMargin;
int labelXNearButton = buttonsHorizontalCenter - labelsOffset;

int labelXAwayFromButton = labelXNearButton - label.getMeasuredWidth();
int labelTop = childY - mLabelsVerticalOffset + (child.getMeasuredHeight()
int labelTop = childY - mLabelsVerticalOffset + (fab.getMeasuredHeight()
- label.getMeasuredHeight()) / 2;

label.layout(labelXAwayFromButton, labelTop,
Expand All @@ -279,6 +296,7 @@ private int adjustForOvershoot(int dimension) {
protected void onFinishInflate() {
super.onFinishInflate();
bringChildToFront(mMenuButton);
bringChildToFront(mImageToggle);
mButtonsCount = getChildCount();
createLabels();
}
Expand All @@ -287,6 +305,9 @@ private void createLabels() {
Context context = new ContextThemeWrapper(getContext(), mLabelsStyle);

for (int i = 0; i < mButtonsCount; i++) {

if (getChildAt(i) == mImageToggle) continue;

final FloatingActionButton fab = (FloatingActionButton) getChildAt(i);
String text = fab.getLabelText();

Expand Down Expand Up @@ -359,34 +380,6 @@ private void setLabelEllipsize(Label label) {
}
}

private static class RotatingDrawable extends LayerDrawable {

public RotatingDrawable(Drawable drawable) {
super(new Drawable[]{drawable});
}

private float mRotation;

@SuppressWarnings("UnusedDeclaration")
public float getRotation() {
return mRotation;
}

@SuppressWarnings("UnusedDeclaration")
public void setRotation(float rotation) {
mRotation = rotation;
invalidateSelf();
}

@Override
public void draw(Canvas canvas) {
canvas.save();
canvas.rotate(mRotation, getBounds().centerX(), getBounds().centerY());
super.draw(canvas);
canvas.restore();
}
}

@Override
public MarginLayoutParams generateLayoutParams(AttributeSet attrs) {
return new MarginLayoutParams(getContext(), attrs);
Expand Down Expand Up @@ -425,8 +418,12 @@ public void toggle(boolean animate) {
public void open(final boolean animate) {
if (!isOpened()) {
if (mIconAnimated) {
mCloseAnimatorSet.cancel();
mOpenAnimatorSet.start();
if (mIconToggleSet != null) {
mIconToggleSet.start();
} else {
mCloseAnimatorSet.cancel();
mOpenAnimatorSet.start();
}
}
mMenuOpened = true;
int delay = 0;
Expand Down Expand Up @@ -459,8 +456,12 @@ public void run() {
public void close(final boolean animate) {
if (isOpened()) {
if (mIconAnimated) {
mCloseAnimatorSet.start();
mOpenAnimatorSet.cancel();
if (mIconToggleSet != null) {
mIconToggleSet.start();
} else {
mCloseAnimatorSet.start();
mOpenAnimatorSet.cancel();
}
}
mMenuOpened = false;
int delay = 0;
Expand Down Expand Up @@ -542,4 +543,16 @@ public void setIconAnimated(boolean animated) {
public boolean isIconAnimated() {
return mIconAnimated;
}

public ImageView getMenuIconView() {
return mImageToggle;
}

public void setIconToggleAnimatorSet(AnimatorSet toggleAnimatorSet) {
mIconToggleSet = toggleAnimatorSet;
}

public AnimatorSet getIconToggleAnimatorSet() {
return mIconToggleSet;
}
}
2 changes: 1 addition & 1 deletion library/src/main/java/com/github/clans/fab/Util.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import android.content.Context;
import android.os.Build;

public final class Util {
final class Util {

private Util() {
}
Expand Down
8 changes: 1 addition & 7 deletions sample/proguard-rules.pro
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,4 @@
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}

# keep getters/setters in RotatingDrawable so that animations can still work.
-keepclassmembers class com.github.clans.fab.FloatingActionMenu$RotatingDrawable {
void set*(***);
*** get*();
}
#}
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
package com.github.clans.fab.sample;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
Expand Down Expand Up @@ -81,6 +85,40 @@ public void onClick(View v) {
startActivity(new Intent(FloatingMenusActivity.this, RecyclerViewActivity.class));
}
});

createCustomAnimation();
}

private void createCustomAnimation() {
final FloatingActionMenu menu4 = (FloatingActionMenu) findViewById(R.id.menu4);

AnimatorSet set = new AnimatorSet();

ObjectAnimator scaleOutX = ObjectAnimator.ofFloat(menu4.getMenuIconView(), "scaleX", 1.0f, 0.2f);
ObjectAnimator scaleOutY = ObjectAnimator.ofFloat(menu4.getMenuIconView(), "scaleY", 1.0f, 0.2f);

ObjectAnimator scaleInX = ObjectAnimator.ofFloat(menu4.getMenuIconView(), "scaleX", 0.2f, 1.0f);
ObjectAnimator scaleInY = ObjectAnimator.ofFloat(menu4.getMenuIconView(), "scaleY", 0.2f, 1.0f);

scaleOutX.setDuration(50);
scaleOutY.setDuration(50);

scaleInX.setDuration(150);
scaleInY.setDuration(150);

scaleInX.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
menu4.getMenuIconView().setImageResource(menu4.isOpened()
? R.drawable.ic_close : R.drawable.ic_star);
}
});

set.play(scaleOutX).with(scaleOutY);
set.play(scaleInX).with(scaleInY).after(scaleOutX);
set.setInterpolator(new OvershootInterpolator(2));

menu4.setIconToggleAnimatorSet(set);
}

@Override
Expand Down
Binary file added sample/src/main/res/drawable-hdpi/ic_close.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added sample/src/main/res/drawable-mdpi/ic_close.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added sample/src/main/res/drawable-xhdpi/ic_close.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added sample/src/main/res/drawable-xxhdpi/ic_close.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 4d6db15

Please sign in to comment.