diff --git a/library/src/main/java/com/github/clans/fab/FloatingActionButton.java b/library/src/main/java/com/github/clans/fab/FloatingActionButton.java index 2973a44..a17a47d 100755 --- a/library/src/main/java/com/github/clans/fab/FloatingActionButton.java +++ b/library/src/main/java/com/github/clans/fab/FloatingActionButton.java @@ -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; @@ -254,7 +255,6 @@ void setColors(int colorNormal, int colorPressed, int colorRipple) { mColorNormal = colorNormal; mColorPressed = colorPressed; mColorRipple = colorRipple; - updateBackground(); } @TargetApi(Build.VERSION_CODES.LOLLIPOP) diff --git a/library/src/main/java/com/github/clans/fab/FloatingActionMenu.java b/library/src/main/java/com/github/clans/fab/FloatingActionMenu.java index e504edc..3d8fd86 100755 --- a/library/src/main/java/com/github/clans/fab/FloatingActionMenu.java +++ b/library/src/main/java/com/github/clans/fab/FloatingActionMenu.java @@ -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; @@ -19,6 +17,7 @@ import android.view.animation.AnticipateInterpolator; import android.view.animation.Interpolator; import android.view.animation.OvershootInterpolator; +import android.widget.ImageView; public class FloatingActionMenu extends ViewGroup { @@ -26,8 +25,9 @@ public class FloatingActionMenu extends ViewGroup { 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; @@ -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; @@ -145,34 +146,19 @@ 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) { @@ -180,7 +166,27 @@ public void onClick(View v) { } }); + 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 @@ -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()); @@ -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(); @@ -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, @@ -279,6 +296,7 @@ private int adjustForOvershoot(int dimension) { protected void onFinishInflate() { super.onFinishInflate(); bringChildToFront(mMenuButton); + bringChildToFront(mImageToggle); mButtonsCount = getChildCount(); createLabels(); } @@ -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(); @@ -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); @@ -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; @@ -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; @@ -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; + } } diff --git a/library/src/main/java/com/github/clans/fab/Util.java b/library/src/main/java/com/github/clans/fab/Util.java index 5e9ad4f..909f4fb 100755 --- a/library/src/main/java/com/github/clans/fab/Util.java +++ b/library/src/main/java/com/github/clans/fab/Util.java @@ -3,7 +3,7 @@ import android.content.Context; import android.os.Build; -public final class Util { +final class Util { private Util() { } diff --git a/sample/proguard-rules.pro b/sample/proguard-rules.pro index bb0d74f..80fd597 100755 --- a/sample/proguard-rules.pro +++ b/sample/proguard-rules.pro @@ -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*(); -} \ No newline at end of file +#} \ No newline at end of file diff --git a/sample/src/main/java/com/github/clans/fab/sample/FloatingMenusActivity.java b/sample/src/main/java/com/github/clans/fab/sample/FloatingMenusActivity.java index 4dbee74..a175264 100755 --- a/sample/src/main/java/com/github/clans/fab/sample/FloatingMenusActivity.java +++ b/sample/src/main/java/com/github/clans/fab/sample/FloatingMenusActivity.java @@ -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; @@ -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 diff --git a/sample/src/main/res/drawable-hdpi/ic_close.png b/sample/src/main/res/drawable-hdpi/ic_close.png new file mode 100755 index 0000000..d664996 Binary files /dev/null and b/sample/src/main/res/drawable-hdpi/ic_close.png differ diff --git a/sample/src/main/res/drawable-mdpi/ic_close.png b/sample/src/main/res/drawable-mdpi/ic_close.png new file mode 100755 index 0000000..f3d2658 Binary files /dev/null and b/sample/src/main/res/drawable-mdpi/ic_close.png differ diff --git a/sample/src/main/res/drawable-xhdpi/ic_close.png b/sample/src/main/res/drawable-xhdpi/ic_close.png new file mode 100755 index 0000000..a7046c5 Binary files /dev/null and b/sample/src/main/res/drawable-xhdpi/ic_close.png differ diff --git a/sample/src/main/res/drawable-xxhdpi/ic_close.png b/sample/src/main/res/drawable-xxhdpi/ic_close.png new file mode 100755 index 0000000..a712745 Binary files /dev/null and b/sample/src/main/res/drawable-xxhdpi/ic_close.png differ diff --git a/sample/src/main/res/layout/floating_menus_activity.xml b/sample/src/main/res/layout/floating_menus_activity.xml index 45a8668..4d763ab 100755 --- a/sample/src/main/res/layout/floating_menus_activity.xml +++ b/sample/src/main/res/layout/floating_menus_activity.xml @@ -64,7 +64,7 @@ android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_alignParentRight="true" - android:layout_marginRight="90dp" + android:layout_marginRight="80dp" android:layout_marginBottom="10dp" android:layout_marginLeft="10dp" android:layout_marginTop="10dp" @@ -103,13 +103,54 @@ + + + + + + + + + + #62B2FF + +