Skip to content
This repository has been archived by the owner on Feb 9, 2023. It is now read-only.

Commit

Permalink
Merge pull request #32 from Buzzvil/feature/close_pop
Browse files Browse the repository at this point in the history
[CD-51] Close pop with gesture, add animation
  • Loading branch information
realwind2048 committed Sep 19, 2019
2 parents b84cc2a + 524ac14 commit 1174bf4
Show file tree
Hide file tree
Showing 6 changed files with 344 additions and 48 deletions.
183 changes: 168 additions & 15 deletions hover/src/main/java/io/mattcarroll/hover/ExitView.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,20 @@
*/
package io.mattcarroll.hover;

import android.animation.Animator;
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
import android.content.Context;
import android.graphics.Point;
import android.graphics.Rect;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.view.animation.PathInterpolatorCompat;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Interpolator;
import android.widget.RelativeLayout;

/**
Expand All @@ -32,8 +38,28 @@ class ExitView extends RelativeLayout {

private static final String TAG = "ExitView";

private static final int FADE_DURATION = 250;
private static final int SHOW_HIDE_DURATION = 250;
private static final float EXIT_ICON_DEFAULT_SCALE_X = 1.0f;
private static final float EXIT_ICON_DEFAULT_SCALE_Y = 1.0f;
private static final float EXIT_ICON_TARGET_SCALE_X = 1.2f;
private static final float EXIT_ICON_TARGET_SCALE_Y = 1.2f;
private static final float EXIT_ICON_DEFAULT_ROTATION = 0f;
private static final float EXIT_ICON_TARGET_ROTATION = 90f;
private static final float EXIT_ICON_DEFAULT_ALPHA = 0.6f;
private static final float EXIT_ICON_TARGET_ALPHA = 0.75f;
private static final float EXIT_VIEW_DEFAULT_ALPHA = 0f;
private static final float EXIT_VIEW_TARGET_ALPHA = 1.0f;
private static final float EXIT_VIEW_DEFAULT_Y = 800f;
private static final float EXIT_VIEW_TARGET_Y = 0f;

private int mExitRadiusInPx;
private View mExitIcon;
private View mExitGradient;
private ViewGroup mVgExit;
private ObjectAnimator mShowEnterAnimation = null;
private ObjectAnimator mShowExitAnimation = null;
private boolean mIsShowing = false;

public ExitView(@NonNull Context context) {
this(context, null);
Expand All @@ -48,27 +74,154 @@ private void init() {
LayoutInflater.from(getContext()).inflate(R.layout.view_hover_menu_exit, this, true);

mExitIcon = findViewById(R.id.view_exit);

mVgExit = findViewById(R.id.vg_exit);
mExitGradient = findViewById(R.id.view_exit_gradient);
mExitRadiusInPx = getResources().getDimensionPixelSize(R.dimen.hover_exit_radius);
mExitIcon.setAlpha(EXIT_ICON_DEFAULT_ALPHA);

setAnimations();
}

public boolean isInExitZone(@NonNull Point position) {
Point exitCenter = getExitZoneCenter();
double distanceToExit = calculateDistance(position, exitCenter);
Log.d(TAG, "Drop point: " + position + ", Exit center: " + exitCenter + ", Distance: " + distanceToExit);
return distanceToExit <= mExitRadiusInPx;
private Interpolator getExitViewInterpolator() {
return PathInterpolatorCompat.create(0.75f, 0f, 0.25f, 1f);
}

private Point getExitZoneCenter() {
return new Point(
(int) (mExitIcon.getX() + (mExitIcon.getWidth() / 2)),
(int) (mExitIcon.getY() + (mExitIcon.getHeight() / 2))
);
private void setAnimations() {
PropertyValuesHolder showEnterAnimationScaleX = PropertyValuesHolder.ofFloat("scaleX", EXIT_ICON_DEFAULT_SCALE_X, EXIT_ICON_TARGET_SCALE_X);
PropertyValuesHolder showEnterAnimationScaleY = PropertyValuesHolder.ofFloat("scaleY", EXIT_ICON_DEFAULT_SCALE_Y, EXIT_ICON_TARGET_SCALE_Y);
PropertyValuesHolder showEnterAnimationRotate = PropertyValuesHolder.ofFloat("rotation", EXIT_ICON_DEFAULT_ROTATION, EXIT_ICON_TARGET_ROTATION);
PropertyValuesHolder showEnterAnimationAlpha = PropertyValuesHolder.ofFloat("alpha", EXIT_ICON_DEFAULT_ALPHA, EXIT_ICON_TARGET_ALPHA);
mShowEnterAnimation = ObjectAnimator.ofPropertyValuesHolder(mExitIcon, showEnterAnimationScaleX, showEnterAnimationScaleY, showEnterAnimationRotate, showEnterAnimationAlpha);
mShowEnterAnimation.setDuration(SHOW_HIDE_DURATION);
mShowEnterAnimation.setInterpolator(getExitViewInterpolator());
mShowEnterAnimation.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animator) {
initExitIconViewStatus();
}

@Override
public void onAnimationEnd(Animator animator) {
}

@Override
public void onAnimationCancel(Animator animator) {
initExitIconViewStatus();
}

@Override
public void onAnimationRepeat(Animator animator) {
}
});

PropertyValuesHolder showExitAnimationScaleX = PropertyValuesHolder.ofFloat("scaleX", EXIT_ICON_TARGET_SCALE_X, EXIT_ICON_DEFAULT_SCALE_X);
PropertyValuesHolder showExitAnimationScaleY = PropertyValuesHolder.ofFloat("scaleY", EXIT_ICON_TARGET_SCALE_Y, EXIT_ICON_DEFAULT_SCALE_Y);
PropertyValuesHolder showExitAnimationRotate = PropertyValuesHolder.ofFloat("rotation", EXIT_ICON_TARGET_ROTATION, EXIT_ICON_DEFAULT_ROTATION);
PropertyValuesHolder showExitAnimationAlpha = PropertyValuesHolder.ofFloat("alpha", EXIT_ICON_TARGET_ALPHA, EXIT_ICON_DEFAULT_ALPHA);
mShowExitAnimation = ObjectAnimator.ofPropertyValuesHolder(mExitIcon, showExitAnimationScaleX, showExitAnimationScaleY, showExitAnimationRotate, showExitAnimationAlpha);
mShowExitAnimation.setDuration(SHOW_HIDE_DURATION);
mShowExitAnimation.setInterpolator(getExitViewInterpolator());
}

private double calculateDistance(@NonNull Point p1, @NonNull Point p2) {
return Math.sqrt(
Math.pow(p2.x - p1.x, 2) + Math.pow(p2.y - p1.y, 2)
private void initExitIconViewStatus() {
mExitIcon.setScaleY(EXIT_ICON_DEFAULT_SCALE_Y);
mExitIcon.setScaleX(EXIT_ICON_DEFAULT_SCALE_X);
mExitIcon.setRotation(EXIT_ICON_DEFAULT_ROTATION);
}

public boolean isInExitZone(@NonNull Point position, @NonNull Point screenSize) {
int exitXExcludeThresholdLeft = screenSize.x / 10;
int exitXExcludeThresholdRight = screenSize.x * 9 / 10;

Rect exitArea = new Rect(
0 - mExitIcon.getWidth(),
screenSize.y * 4 / 6,
screenSize.x + mExitIcon.getWidth(),
screenSize.y + mExitIcon.getHeight()
);

Rect excludedXExitAreaLeft = new Rect(
0 - mExitIcon.getWidth(),
screenSize.y * 4 / 6,
exitXExcludeThresholdLeft,
screenSize.y - mExitIcon.getHeight() / 2
);

Rect excludedXExitAreaRight = new Rect(
exitXExcludeThresholdRight,
screenSize.y * 4 / 6,
screenSize.x + mExitIcon.getWidth(),
screenSize.y - mExitIcon.getHeight() / 2
);

return exitArea.contains(position.x, position.y)
&& !excludedXExitAreaLeft.contains(position.x, position.y)
&& !excludedXExitAreaRight.contains(position.x, position.y);
}

public void showEnterAnimation() {
if (mShowEnterAnimation != null && !mShowEnterAnimation.isRunning() && !mIsShowing) {
mShowEnterAnimation.start();
mIsShowing = true;
}
}

public void showExitAnimation() {
if (mShowExitAnimation != null && !mShowExitAnimation.isRunning() && mIsShowing) {
mShowExitAnimation.start();
mIsShowing = false;
}
}

public void show() {
resetExitButtonAnimation();

ObjectAnimator exitGradientAnimator = ObjectAnimator.ofFloat(mExitGradient, "alpha", EXIT_VIEW_TARGET_ALPHA);
exitGradientAnimator.setDuration(FADE_DURATION);
exitGradientAnimator.setInterpolator(getExitViewInterpolator());
exitGradientAnimator.start();

ObjectAnimator vgExitAnimator = ObjectAnimator.ofFloat(mVgExit, "y", EXIT_VIEW_DEFAULT_Y, EXIT_VIEW_TARGET_Y);
vgExitAnimator.setDuration(FADE_DURATION);
vgExitAnimator.setInterpolator(getExitViewInterpolator());
vgExitAnimator.start();

setVisibility(VISIBLE);
}

public void resetExitButtonAnimation() {
mIsShowing = false;
initExitIconViewStatus();
}

public void hide() {
ObjectAnimator vgExitAnimator = ObjectAnimator.ofFloat(mVgExit, "y", EXIT_VIEW_TARGET_Y, EXIT_VIEW_DEFAULT_Y);
vgExitAnimator.setDuration(FADE_DURATION);
vgExitAnimator.setInterpolator(getExitViewInterpolator());
vgExitAnimator.start();

ObjectAnimator exitGradientAnimator = ObjectAnimator.ofFloat(mExitGradient, "alpha", EXIT_VIEW_DEFAULT_ALPHA);
exitGradientAnimator.setDuration(FADE_DURATION);
exitGradientAnimator.setInterpolator(getExitViewInterpolator());
exitGradientAnimator.start();

exitGradientAnimator.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
}

@Override
public void onAnimationEnd(Animator animation) {
setVisibility(GONE);
}

@Override
public void onAnimationCancel(Animator animation) {
}

@Override
public void onAnimationRepeat(Animator animation) {
}
});
}
}
36 changes: 36 additions & 0 deletions hover/src/main/java/io/mattcarroll/hover/FloatingTab.java
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,42 @@ public void onAnimationUpdate(ValueAnimator animation) {
});
}

public void closeAnimation(Point targetPosition, @Nullable final Runnable onDocked) {
Point destinationCornerPosition = convertCenterToCorner(targetPosition);
Log.d(TAG, "Docking to destination point: " + destinationCornerPosition);

ObjectAnimator xAnimation = ObjectAnimator.ofFloat(this, "x", targetPosition.x);
xAnimation.setDuration(500);
xAnimation.setInterpolator(new OvershootInterpolator());
ObjectAnimator yAnimation = ObjectAnimator.ofFloat(this, "y", targetPosition.y);
yAnimation.setDuration(500);
yAnimation.setInterpolator(new OvershootInterpolator());
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.play(xAnimation).with(yAnimation);
animatorSet.start();

animatorSet.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
}

@Override
public void onAnimationEnd(Animator animation) {
if (null != onDocked) {
onDocked.run();
}
}

@Override
public void onAnimationCancel(Animator animation) {
}

@Override
public void onAnimationRepeat(Animator animation) {
}
});
}

public void dockImmediately() {
moveCenterTo(mDock.position());
}
Expand Down
Loading

0 comments on commit 1174bf4

Please sign in to comment.