From 0d32dcbcea265385873e02ab1c72509b5f4b7ea2 Mon Sep 17 00:00:00 2001 From: Nick Butcher Date: Fri, 13 May 2016 17:41:49 +0100 Subject: [PATCH] Switch from fab morphing to revealing. --- .../java/io/plaidapp/ui/DribbbleLogin.java | 5 +- .../java/io/plaidapp/ui/DribbbleShot.java | 10 +- .../java/io/plaidapp/ui/HomeActivity.java | 5 +- .../plaidapp/ui/PostNewDesignerNewsStory.java | 3 +- .../ui/transitions/FabDialogMorphSetup.java | 4 +- .../ui/transitions/FabDialogReveal.java | 264 ++++++++++++++++++ .../ui/transitions/MorphFabToDialog.java | 83 ++++-- .../res/values/attrs_fab_dialog_reveal.xml | 25 ++ 8 files changed, 366 insertions(+), 33 deletions(-) create mode 100644 app/src/main/java/io/plaidapp/ui/transitions/FabDialogReveal.java create mode 100644 app/src/main/res/values/attrs_fab_dialog_reveal.xml diff --git a/app/src/main/java/io/plaidapp/ui/DribbbleLogin.java b/app/src/main/java/io/plaidapp/ui/DribbbleLogin.java index af11458f2..db4f59825 100644 --- a/app/src/main/java/io/plaidapp/ui/DribbbleLogin.java +++ b/app/src/main/java/io/plaidapp/ui/DribbbleLogin.java @@ -49,7 +49,7 @@ import io.plaidapp.data.api.dribbble.model.AccessToken; import io.plaidapp.data.api.dribbble.model.User; import io.plaidapp.data.prefs.DribbblePrefs; -import io.plaidapp.ui.transitions.FabDialogMorphSetup; +import io.plaidapp.ui.transitions.FabDialogReveal; import io.plaidapp.util.ScrimUtil; import io.plaidapp.util.glide.CircleTransform; import retrofit2.Call; @@ -74,8 +74,7 @@ public class DribbbleLogin extends Activity { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_dribbble_login); - FabDialogMorphSetup.setupSharedEelementTransitions(this, container, - getResources().getDimensionPixelSize(R.dimen.dialog_corners)); + FabDialogReveal.setup(this, container); container = (ViewGroup) findViewById(R.id.container); message = (TextView) findViewById(R.id.login_message); diff --git a/app/src/main/java/io/plaidapp/ui/DribbbleShot.java b/app/src/main/java/io/plaidapp/ui/DribbbleShot.java index 6b4a21c7b..0e2ff8348 100644 --- a/app/src/main/java/io/plaidapp/ui/DribbbleShot.java +++ b/app/src/main/java/io/plaidapp/ui/DribbbleShot.java @@ -88,7 +88,7 @@ import io.plaidapp.data.api.dribbble.model.Like; import io.plaidapp.data.api.dribbble.model.Shot; import io.plaidapp.data.prefs.DribbblePrefs; -import io.plaidapp.ui.transitions.FabDialogMorphSetup; +import io.plaidapp.ui.transitions.FabDialogReveal; import io.plaidapp.ui.widget.AuthorTextView; import io.plaidapp.ui.widget.CheckableImageButton; import io.plaidapp.ui.widget.ElasticDragDismissFrameLayout; @@ -589,8 +589,8 @@ public void onClick(View view) { doLike(); } else { final Intent login = new Intent(DribbbleShot.this, DribbbleLogin.class); - login.putExtra(FabDialogMorphSetup.EXTRA_SHARED_ELEMENT_START_COLOR, - ContextCompat.getColor(DribbbleShot.this, R.color.dribbble)); + FabDialogReveal.addExtras(login, ContextCompat.getColor(DribbbleShot.this, R + .color.dribbble), R.drawable.ic_heart_empty_56dp); ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation (DribbbleShot.this, fab, getString(R.string.transition_dribbble_login)); startActivityForResult(login, RC_LOGIN_LIKE, options.toBundle()); @@ -821,8 +821,8 @@ public void onFailure(Call call, Throwable t) { }); } else { Intent login = new Intent(DribbbleShot.this, DribbbleLogin.class); - login.putExtra(FabDialogMorphSetup.EXTRA_SHARED_ELEMENT_START_COLOR, ContextCompat.getColor - (this, R.color.background_light)); + FabDialogReveal.addExtras(login, ContextCompat.getColor(DribbbleShot.this, R + .color.background_light), R.drawable.ic_comment_add); ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(DribbbleShot.this, postComment, getString(R.string.transition_dribbble_login)); diff --git a/app/src/main/java/io/plaidapp/ui/HomeActivity.java b/app/src/main/java/io/plaidapp/ui/HomeActivity.java index 8cf59df02..97adb87fb 100644 --- a/app/src/main/java/io/plaidapp/ui/HomeActivity.java +++ b/app/src/main/java/io/plaidapp/ui/HomeActivity.java @@ -93,6 +93,7 @@ import io.plaidapp.ui.recyclerview.GridItemDividerDecoration; import io.plaidapp.ui.recyclerview.InfiniteScrollListener; import io.plaidapp.ui.transitions.FabDialogMorphSetup; +import io.plaidapp.ui.transitions.FabDialogReveal; import io.plaidapp.util.AnimUtils; import io.plaidapp.util.ViewUtils; @@ -456,8 +457,8 @@ public void onScrollStateChanged(RecyclerView recyclerView, int newState) { protected void fabClick() { if (designerNewsPrefs.isLoggedIn()) { Intent intent = new Intent(this, PostNewDesignerNewsStory.class); - intent.putExtra(FabDialogMorphSetup.EXTRA_SHARED_ELEMENT_START_COLOR, - ContextCompat.getColor(this, R.color.accent)); + FabDialogReveal.addExtras(intent, + ContextCompat.getColor(this, R.color.accent), R.drawable.ic_add_dark); intent.putExtra(PostStoryService.EXTRA_BROADCAST_RESULT, true); registerPostStoryResultListener(); ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(this, fab, diff --git a/app/src/main/java/io/plaidapp/ui/PostNewDesignerNewsStory.java b/app/src/main/java/io/plaidapp/ui/PostNewDesignerNewsStory.java index 296358068..ab6c033c4 100644 --- a/app/src/main/java/io/plaidapp/ui/PostNewDesignerNewsStory.java +++ b/app/src/main/java/io/plaidapp/ui/PostNewDesignerNewsStory.java @@ -44,6 +44,7 @@ import io.plaidapp.data.api.designernews.PostStoryService; import io.plaidapp.data.prefs.DesignerNewsPrefs; import io.plaidapp.ui.transitions.FabDialogMorphSetup; +import io.plaidapp.ui.transitions.FabDialogReveal; import io.plaidapp.ui.widget.BottomSheet; import io.plaidapp.ui.widget.ObservableScrollView; import io.plaidapp.util.AnimUtils; @@ -71,7 +72,7 @@ protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_post_new_designer_news_story); ButterKnife.bind(this); - FabDialogMorphSetup.setupSharedEelementTransitions(this, bottomSheetContent, 0); + FabDialogReveal.setup(this, bottomSheetContent); bottomSheet.registerCallback(new BottomSheet.Callbacks() { @Override diff --git a/app/src/main/java/io/plaidapp/ui/transitions/FabDialogMorphSetup.java b/app/src/main/java/io/plaidapp/ui/transitions/FabDialogMorphSetup.java index 67f7e4a9a..eca696c90 100644 --- a/app/src/main/java/io/plaidapp/ui/transitions/FabDialogMorphSetup.java +++ b/app/src/main/java/io/plaidapp/ui/transitions/FabDialogMorphSetup.java @@ -31,9 +31,9 @@ public class FabDialogMorphSetup { public static final String EXTRA_SHARED_ELEMENT_START_COLOR = - "EXTRA_SHARED_ELEMENT_START_COLOR"; + "EXTRA_FAB_COLOR"; public static final String EXTRA_SHARED_ELEMENT_START_CORNER_RADIUS = - "EXTRA_SHARED_ELEMENT_START_CORNER_RADIUS"; + "EXTRA_FAB_ICON_RES_ID"; private FabDialogMorphSetup() { } diff --git a/app/src/main/java/io/plaidapp/ui/transitions/FabDialogReveal.java b/app/src/main/java/io/plaidapp/ui/transitions/FabDialogReveal.java new file mode 100644 index 000000000..a855e390c --- /dev/null +++ b/app/src/main/java/io/plaidapp/ui/transitions/FabDialogReveal.java @@ -0,0 +1,264 @@ +/* + * Copyright 2015 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.plaidapp.ui.transitions; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.AnimatorSet; +import android.animation.ObjectAnimator; +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.content.res.TypedArray; +import android.graphics.Color; +import android.graphics.Outline; +import android.graphics.Rect; +import android.graphics.drawable.ColorDrawable; +import android.graphics.drawable.Drawable; +import android.support.annotation.ColorInt; +import android.support.annotation.DrawableRes; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v4.content.ContextCompat; +import android.transition.Transition; +import android.transition.TransitionValues; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewAnimationUtils; +import android.view.ViewGroup; +import android.view.ViewOutlineProvider; +import android.view.animation.Interpolator; + +import java.util.ArrayList; +import java.util.List; + +import io.plaidapp.R; +import io.plaidapp.util.AnimUtils; + +import static android.view.View.MeasureSpec.makeMeasureSpec; + +/** + * A transition between a FAB & a dialog using a circular reveal and following an arced path. + */ +public class FabDialogReveal extends Transition { + + private static final String EXTRA_FAB_COLOR = "EXTRA_FAB_COLOR"; + private static final String EXTRA_FAB_ICON_RES_ID = "EXTRA_FAB_ICON_RES_ID"; + private static final String PROP_BOUNDS = "plaid:fabDialogReveal:bounds"; + private static final String[] TRANSITION_PROPERTIES = { + PROP_BOUNDS + }; + + private final int color; + private final int icon; + + public FabDialogReveal(@ColorInt int fabColor, @DrawableRes int iconResId) { + color = fabColor; + icon = iconResId; + setPathMotion(new GravityArcMotion()); + } + + public FabDialogReveal(Context context, AttributeSet attrs) { + TypedArray a = null; + try { + a = context.obtainStyledAttributes(attrs, R.styleable.FabDialogReveal); + if (!a.hasValue(R.styleable.FabDialogReveal_android_color) + || !a.hasValue(R.styleable.FabDialogReveal_android_icon)) { + throw new IllegalArgumentException("Must provide both color & icon."); + } + color = a.getColor(R.styleable.FabDialogReveal_android_color, Color.TRANSPARENT); + icon = a.getResourceId(R.styleable.FabDialogReveal_android_icon, 0); + setPathMotion(new GravityArcMotion()); + } finally { + a.recycle(); + } + } + + public static void addExtras(@NonNull Intent intent, @ColorInt int fabColor, + @DrawableRes int iconResId) { + intent.putExtra(FabDialogReveal.EXTRA_FAB_COLOR, fabColor); + intent.putExtra(FabDialogReveal.EXTRA_FAB_ICON_RES_ID, iconResId); + } + + public static void setup(@NonNull Activity activity, @Nullable View target) { + final Intent intent = activity.getIntent(); + if (!intent.hasExtra(EXTRA_FAB_COLOR) || !intent.hasExtra(EXTRA_FAB_ICON_RES_ID)) return; + final int color = intent. + getIntExtra(EXTRA_FAB_COLOR, Color.TRANSPARENT); + final int icon = intent.getIntExtra(EXTRA_FAB_ICON_RES_ID, -1); + final FabDialogReveal sharedEnter = new FabDialogReveal(color, icon); + if (target != null) { + sharedEnter.addTarget(target); + } + activity.getWindow().setSharedElementEnterTransition(sharedEnter); + } + + @Override + public String[] getTransitionProperties() { + return TRANSITION_PROPERTIES; + } + + @Override + public void captureStartValues(TransitionValues transitionValues) { + captureValues(transitionValues); + } + + @Override + public void captureEndValues(TransitionValues transitionValues) { + captureValues(transitionValues); + } + + @Override + public Animator createAnimator(final ViewGroup sceneRoot, + TransitionValues startValues, + final TransitionValues endValues) { + if (startValues == null || endValues == null) return null; + + final Rect startBounds = (Rect) startValues.values.get(PROP_BOUNDS); + final Rect endBounds = (Rect) endValues.values.get(PROP_BOUNDS); + + final boolean fabToDialog = endBounds.width() > startBounds.width(); + final View view = endValues.view; + final Rect dialogBounds = fabToDialog ? endBounds : startBounds; + final Rect fabBounds = fabToDialog ? startBounds : endBounds; + final Interpolator fastOutSlowInInterpolator = + AnimUtils.getFastOutSlowInInterpolator(sceneRoot.getContext()); + + if (!fabToDialog) { + // force measure / layout the dialog back to it's orig bounds + view.measure( + makeMeasureSpec(startBounds.width(), View.MeasureSpec.EXACTLY), + makeMeasureSpec(startBounds.height(), View.MeasureSpec.EXACTLY)); + view.layout(startBounds.left, startBounds.top, startBounds.right, startBounds.bottom); + } + + final int translationX = startBounds.centerX() - endBounds.centerX(); + final int translationY = startBounds.centerY() - endBounds.centerY(); + if (fabToDialog) { + view.setTranslationX(translationX); + view.setTranslationY(translationY); + } + + final ColorDrawable fabColor = new ColorDrawable(color); + fabColor.setBounds(0, 0, dialogBounds.width(), dialogBounds.height()); + if (!fabToDialog) fabColor.setAlpha(0); + view.getOverlay().add(fabColor); + + final Drawable fabIcon = + ContextCompat.getDrawable(sceneRoot.getContext(), icon).mutate(); + final int iconLeft = (dialogBounds.width() - fabIcon.getIntrinsicWidth()) / 2; + final int iconTop = (dialogBounds.height() - fabIcon.getIntrinsicHeight()) / 2; + fabIcon.setBounds(iconLeft, iconTop, + iconLeft + fabIcon.getIntrinsicWidth(), + iconTop + fabIcon.getIntrinsicHeight()); + if (!fabToDialog) fabIcon.setAlpha(0); + view.getOverlay().add(fabIcon); + + final Animator circularReveal; + if (fabToDialog) { + circularReveal = ViewAnimationUtils.createCircularReveal(view, + (view.getRight() - view.getLeft()) / 2, + (view.getBottom() - view.getTop()) / 2, + startBounds.width() / 2, + (float) Math.hypot(endBounds.width() / 2, endBounds.width() / 2)); + circularReveal.setInterpolator( + AnimUtils.getFastOutLinearInInterpolator(sceneRoot.getContext())); + } else { + circularReveal = ViewAnimationUtils.createCircularReveal(view, + (view.getRight() - view.getLeft()) / 2, + (view.getBottom() - view.getTop()) / 2, + (float) Math.hypot(startBounds.width() / 2, startBounds.width() / 2), + endBounds.width() / 2); + circularReveal.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + view.setOutlineProvider(new ViewOutlineProvider() { + @Override + public void getOutline(View view, Outline outline) { + final int left = (view.getWidth() - fabBounds.width()) / 2; + final int top = (view.getHeight() - fabBounds.height()) / 2; + outline.setOval( + left, top, left + fabBounds.width(), top + fabBounds.height()); + view.setClipToOutline(true); + } + }); + } + }); + circularReveal.setInterpolator( + AnimUtils.getLinearOutSlowInInterpolator(sceneRoot.getContext())); + } + circularReveal.setDuration(240L); + + final Animator translate = ObjectAnimator.ofFloat( + view, + View.TRANSLATION_X, + View.TRANSLATION_Y, + fabToDialog ? getPathMotion().getPath(translationX, translationY, 0, 0) + : getPathMotion().getPath(0, 0, -translationX, -translationY)); + translate.setDuration(240L); + translate.setInterpolator(fastOutSlowInInterpolator); + + List fadeContents = null; + if (view instanceof ViewGroup) { + final ViewGroup vg = ((ViewGroup) view); + fadeContents = new ArrayList<>(vg.getChildCount()); + for (int i = vg.getChildCount() - 1; i >= 0; i--) { + final View child = vg.getChildAt(i); + final Animator fade = + ObjectAnimator.ofFloat(child, View.ALPHA, fabToDialog ? 1f : 0f); + if (fabToDialog) { + child.setAlpha(0f); + } + fade.setDuration(160L); + fade.setInterpolator(fastOutSlowInInterpolator); + fadeContents.add(fade); + } + } + + final Animator colorFade = ObjectAnimator.ofArgb(fabColor, "alpha", fabToDialog ? 0 : 255); + final Animator iconFade = ObjectAnimator.ofInt(fabIcon, "alpha", fabToDialog ? 0 : 255); + if (!fabToDialog) { + colorFade.setStartDelay(120L); + iconFade.setStartDelay(120L); + } + colorFade.setDuration(120L); + iconFade.setDuration(120L); + colorFade.setInterpolator(fastOutSlowInInterpolator); + iconFade.setInterpolator(fastOutSlowInInterpolator); + + final AnimatorSet transition = new AnimatorSet(); + transition.playTogether(circularReveal, translate, colorFade, iconFade); + transition.playTogether(fadeContents); + if (fabToDialog) { + transition.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + view.getOverlay().clear(); + } + }); + } + return new AnimUtils.NoPauseAnimator(transition); + } + + private void captureValues(TransitionValues transitionValues) { + final View view = transitionValues.view; + if (view == null || view.getWidth() <= 0 || view.getHeight() <= 0) return; + + transitionValues.values.put(PROP_BOUNDS, new Rect(view.getLeft(), view.getTop(), + view.getRight(), view.getBottom())); + } +} diff --git a/app/src/main/java/io/plaidapp/ui/transitions/MorphFabToDialog.java b/app/src/main/java/io/plaidapp/ui/transitions/MorphFabToDialog.java index e2d70356c..ead918434 100644 --- a/app/src/main/java/io/plaidapp/ui/transitions/MorphFabToDialog.java +++ b/app/src/main/java/io/plaidapp/ui/transitions/MorphFabToDialog.java @@ -21,13 +21,18 @@ import android.animation.ObjectAnimator; import android.content.Context; import android.graphics.Color; +import android.graphics.Rect; +import android.graphics.drawable.ColorDrawable; import android.support.annotation.ColorInt; import android.support.v4.content.ContextCompat; -import android.transition.ChangeBounds; +import android.transition.ArcMotion; +import android.transition.Transition; import android.transition.TransitionValues; import android.util.AttributeSet; import android.view.View; +import android.view.ViewAnimationUtils; import android.view.ViewGroup; +import android.view.animation.Interpolator; import io.plaidapp.R; import io.plaidapp.ui.drawable.MorphDrawable; @@ -36,13 +41,15 @@ /** * A transition that morphs a circle into a rectangle, changing it's background color. */ -public class MorphFabToDialog extends ChangeBounds { +public class MorphFabToDialog extends Transition { - private static final String PROPERTY_COLOR = "plaid:circleMorph:color"; - private static final String PROPERTY_CORNER_RADIUS = "plaid:circleMorph:cornerRadius"; + private static final String PROPNAME_COLOR = "plaid:fabMorph:color"; + private static final String PROPNAME_BOUNDS = "plaid:fabMorph:bounds"; + private static final String PROBNAME_CORNER_RADIUS = "plaid:fabMorph:cornerRadius"; private static final String[] TRANSITION_PROPERTIES = { - PROPERTY_COLOR, - PROPERTY_CORNER_RADIUS + PROPNAME_COLOR, + PROPNAME_BOUNDS, + PROBNAME_CORNER_RADIUS }; private @ColorInt int startColor = Color.TRANSPARENT; private int endCornerRadius; @@ -82,48 +89,51 @@ public String[] getTransitionProperties() { @Override public void captureStartValues(TransitionValues transitionValues) { - super.captureStartValues(transitionValues); final View view = transitionValues.view; if (view.getWidth() <= 0 || view.getHeight() <= 0) { return; } - transitionValues.values.put(PROPERTY_COLOR, startColor); - transitionValues.values.put(PROPERTY_CORNER_RADIUS, + transitionValues.values.put(PROPNAME_COLOR, startColor); + transitionValues.values.put(PROPNAME_BOUNDS, new Rect(view.getLeft(), view.getTop(), + view.getRight(), view.getBottom())); + transitionValues.values.put(PROBNAME_CORNER_RADIUS, startCornerRadius >= 0 ? startCornerRadius : view.getHeight() / 2); } @Override public void captureEndValues(TransitionValues transitionValues) { - super.captureEndValues(transitionValues); final View view = transitionValues.view; if (view.getWidth() <= 0 || view.getHeight() <= 0) { return; } - transitionValues.values.put(PROPERTY_COLOR, + transitionValues.values.put(PROPNAME_COLOR, ContextCompat.getColor(view.getContext(), R.color.background_light)); - transitionValues.values.put(PROPERTY_CORNER_RADIUS, endCornerRadius); + transitionValues.values.put(PROPNAME_BOUNDS, new Rect(view.getLeft(), view.getTop(), + view.getRight(), view.getBottom())); + transitionValues.values.put(PROBNAME_CORNER_RADIUS, endCornerRadius); } @Override public Animator createAnimator(final ViewGroup sceneRoot, TransitionValues startValues, final TransitionValues endValues) { - Animator changeBounds = super.createAnimator(sceneRoot, startValues, endValues); - if (startValues == null || endValues == null || changeBounds == null) { + if (startValues == null || endValues == null) { return null; } - Integer startColor = (Integer) startValues.values.get(PROPERTY_COLOR); - Integer startCornerRadius = (Integer) startValues.values.get(PROPERTY_CORNER_RADIUS); - Integer endColor = (Integer) endValues.values.get(PROPERTY_COLOR); - Integer endCornerRadius = (Integer) endValues.values.get(PROPERTY_CORNER_RADIUS); + + + Integer startColor = (Integer) startValues.values.get(PROPNAME_COLOR); + Integer startCornerRadius = (Integer) startValues.values.get(PROBNAME_CORNER_RADIUS); + Integer endColor = (Integer) endValues.values.get(PROPNAME_COLOR); + Integer endCornerRadius = (Integer) endValues.values.get(PROBNAME_CORNER_RADIUS); if (startColor == null || startCornerRadius == null || endColor == null || endCornerRadius == null) { return null; } - MorphDrawable background = new MorphDrawable(startColor, startCornerRadius); + /*MorphDrawable background = new MorphDrawable(startColor, startCornerRadius); endValues.view.setBackground(background); Animator color = ObjectAnimator.ofArgb(background, background.COLOR, endColor); @@ -152,7 +162,40 @@ public Animator createAnimator(final ViewGroup sceneRoot, transition.playTogether(changeBounds, corners, color); transition.setDuration(300); transition.setInterpolator(AnimUtils.getFastOutSlowInInterpolator(sceneRoot.getContext())); - return transition; + return transition;*/ + + Rect startBounds = (Rect) startValues.values.get(PROPNAME_BOUNDS); + Rect endBounds = (Rect) endValues.values.get(PROPNAME_BOUNDS); + + final int translationX = startBounds.centerX() - endBounds.centerX(); + final int translationY = startBounds.centerY() - endBounds.centerY(); + endValues.view.setTranslationX(translationX); + endValues.view.setTranslationY(translationY); + ColorDrawable colorOverlay = new ColorDrawable(startColor); + colorOverlay.setBounds(0, 0, endBounds.width(), endBounds.height()); + endValues.view.getOverlay().add(colorOverlay); + + final Animator circularReveal = ViewAnimationUtils.createCircularReveal(endValues.view, + (endValues.view.getRight() - endValues.view.getLeft()) / 2, + (endValues.view.getBottom() - endValues.view.getTop()) / 2, + startBounds.width() / 2, + (float) Math.hypot(endBounds.width() / 2, endBounds.width() / 2)); + + ArcMotion arc = new ArcMotion(); + arc.setMaximumAngle(50f); + final Animator translate = ObjectAnimator.ofFloat( + endValues.view, + View.TRANSLATION_X, + View.TRANSLATION_Y, + arc.getPath(translationX, translationY, 0, 0)); + + final Animator color = ObjectAnimator.ofArgb(colorOverlay, "color", Color.TRANSPARENT); + + AnimatorSet transition = new AnimatorSet(); + transition.playTogether(circularReveal, translate, color); + transition.setDuration(600L); + transition.setInterpolator(AnimUtils.getFastOutSlowInInterpolator(sceneRoot.getContext())); + return new AnimUtils.NoPauseAnimator(transition); } } diff --git a/app/src/main/res/values/attrs_fab_dialog_reveal.xml b/app/src/main/res/values/attrs_fab_dialog_reveal.xml new file mode 100644 index 000000000..89ad7917b --- /dev/null +++ b/app/src/main/res/values/attrs_fab_dialog_reveal.xml @@ -0,0 +1,25 @@ + + + + + + + + + + +