Skip to content

Commit

Permalink
Change from y-valued bouncing to x-bouncing
Browse files Browse the repository at this point in the history
  • Loading branch information
christiandeange committed Jul 17, 2015
1 parent f838343 commit ccd499c
Show file tree
Hide file tree
Showing 5 changed files with 48 additions and 41 deletions.
Expand Up @@ -12,7 +12,6 @@
import android.graphics.Path;
import android.graphics.Rect;
import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Looper;
import android.util.AttributeSet;
Expand Down Expand Up @@ -44,9 +43,10 @@ public class RopeProgressBar extends View {

private static final Interpolator INTERPOLATOR = new DampingInterpolator(5);
private ValueAnimator mAnimator;
private float mSlackBounce;
private float mBounceX;
private int mStartProgress;
private boolean mDeferred;
private boolean mSlackSetByUser;

private final Runnable mRequestLayoutRunnable = new Runnable() {
@Override
Expand Down Expand Up @@ -104,6 +104,7 @@ public RopeProgressBar(
width = a.getDimension(R.styleable.RopeProgressBar_strokeWidth, width);
dynamicLayout = a.getBoolean(R.styleable.RopeProgressBar_dynamicLayout, false);

mSlackSetByUser = a.hasValue(R.styleable.RopeProgressBar_slack);
a.recycle();
}

Expand Down Expand Up @@ -134,7 +135,7 @@ private void dynamicRequestLayout() {
if (mDynamicLayout) {
// We need to calculate our new height, since the progress affect the slack
if (Looper.getMainLooper() == Looper.myLooper()) {
requestLayout();
mRequestLayoutRunnable.run();
} else {
post(mRequestLayoutRunnable);
}
Expand All @@ -144,19 +145,23 @@ private void dynamicRequestLayout() {
@Override
protected synchronized void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {

if (!mSlackSetByUser) {
// Slack is unset, default it to 10% of the width of the view
mSlack = MeasureSpec.getSize(widthMeasureSpec) * 0.1f;
}

// Recalculate how tall the text needs to be, width is ignored
final String progress = getBubbleText();
mTextPaint.getTextBounds(progress, 0, progress.length(), mBounds);

final int bubbleHeight = (int) Math.ceil(getBubbleVerticalDisplacement());
final float slack = mDynamicLayout ? getCurrentSlackHeight() : getSlack() + getSlackBounce();
final float slack = (mDynamicLayout) ? getCurrentSlackHeight() : getSlack();

final float strokeWidth = getStrokeWidth();
final int dw = (int) Math.ceil(getPaddingLeft() + getPaddingRight() + strokeWidth);
final int dh = (int) Math.ceil(getPaddingTop() + getPaddingBottom() + strokeWidth + slack);

setMeasuredDimension(
resolveSizeAndState(dw, widthMeasureSpec, 0),
getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
resolveSizeAndState(dh + bubbleHeight, heightMeasureSpec, 0));

// Make the triangle Path
Expand All @@ -181,7 +186,8 @@ protected synchronized void onDraw(final Canvas canvas) {
final float max = getMax();
final float offset = (max == 0) ? 0 : (getProgress() / max);
final float slackHeight = getCurrentSlackHeight();
final float progressEnd = lerp(left, end, offset);
final float progressEnd =
clamp(lerp(left, end, offset) + (mBounceX * perp(offset)), left, end);

// Draw the secondary background line
mLinesPaint.setColor(mSecondaryColor);
Expand All @@ -208,21 +214,23 @@ protected synchronized void onDraw(final Canvas canvas) {
mBubble.reset();
mBubble.addRect(0, 0, bubbleWidth, bubbleHeight, Path.Direction.CW);

final float bubbleLeft = Math.min(
getWidth() - bubbleWidth, Math.max(
0, progressEnd - (bubbleWidth / 2)));
final float bubbleTop = Math.max(slackHeight, 0);
final float bubbleLeft = clamp(
progressEnd - (bubbleWidth / 2),
0,
getWidth() - bubbleWidth);

final int saveCount = canvas.save();
canvas.translate(bubbleLeft, bubbleTop);

canvas.drawPath(mBubble, mBubblePaint);

// Draw the triangle part of the bubble
final float triangleLeft = Math.min(
getWidth() - getTriangleWidth(),
Math.max(0, progressEnd - (getTriangleWidth() / 2) - bubbleLeft));
final float triangleTop = bubbleHeight;
final float triangleLeft = clamp(
progressEnd - (getTriangleWidth() / 2) - bubbleLeft,
0,
getWidth() - getTriangleWidth());

mTriangle.offset(triangleLeft, triangleTop);
canvas.drawPath(mTriangle, mBubblePaint);
Expand All @@ -240,7 +248,7 @@ protected synchronized void onDraw(final Canvas canvas) {
private float getCurrentSlackHeight() {
final float max = getMax();
final float offset = (max == 0) ? 0 : (getProgress() / max);
return perp(offset) * (getSlack() + mSlackBounce);
return getSlack() * perp(offset);
}

private float getBubbleVerticalDisplacement() {
Expand All @@ -267,10 +275,6 @@ public float getTriangleHeight() {
return dips(6);
}

public float getSlackBounce() {
return dips(4);
}

public String getBubbleText() {
if (mFormatter != null) {
return mFormatter.getFormattedText(getProgress(), getMax());
Expand All @@ -296,7 +300,7 @@ public void endDefer() {
}

public synchronized void setProgress(int progress) {
progress = Math.max(0, Math.min(getMax(), progress));
progress = (int) clamp(progress, 0, getMax());
if (progress == mProgress) {
return;
}
Expand All @@ -310,14 +314,15 @@ public synchronized void setProgress(int progress) {
postInvalidate();
}

public void animateProgress(int progress) {
public void animateProgress(final int progress) {
// Speed of animation is interpolated from 0 --> MAX in 2s
// Minimum time duration is 500ms because anything faster than that is waaaay too quick
progress = Math.max(0, Math.min(getMax(), progress));
final int diff = Math.abs(getProgress() - progress);
final int startProgress = getProgress();
final int endProgress = (int) clamp(progress, 0, getMax());
final int diff = Math.abs(getProgress() - endProgress);
final long duration = Math.max(500L, (long) (2000L * (diff / (float) getMax())));

final ValueAnimator animator = ValueAnimator.ofInt(getProgress(), progress);
final ValueAnimator animator = ValueAnimator.ofInt(getProgress(), endProgress);
animator.setDuration(duration);
animator.setInterpolator(new AccelerateInterpolator());
animator.addListener(new AnimatorListenerAdapter() {
Expand All @@ -328,6 +333,7 @@ public void onAnimationStart(final Animator animation) {

@Override
public void onAnimationEnd(final Animator animation) {
bounceAnimation(startProgress);
endDefer();
}
});
Expand All @@ -351,14 +357,13 @@ private void bounceAnimation(final int startProgress) {
mAnimator.cancel();
}

mAnimator = ValueAnimator.ofFloat(0, diffPercent * getSlackBounce());
mAnimator = ValueAnimator.ofFloat(0, diffPercent * getTriangleWidth());
mAnimator.setInterpolator(INTERPOLATOR);
mAnimator.setDuration(1000L);
mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(final ValueAnimator animation) {
mSlackBounce = (float) animation.getAnimatedValue();
dynamicRequestLayout();
mBounceX = (Float) animation.getAnimatedValue();
invalidate();
}
});
Expand All @@ -376,11 +381,11 @@ public void setMax(int max) {

dynamicRequestLayout();
mMax = max;
postInvalidate();

if (mProgress > max) {
mProgress = max;
}

postInvalidate();
}
}

Expand Down Expand Up @@ -460,6 +465,10 @@ public Paint getTextPaint() {
return new Paint(mTextPaint);
}

private float clamp(final float value, final float min, final float max) {
return Math.max(min, Math.min(max, value));
}

private float perp(float t) {
// eh, could be more mathematically accurate to use a catenary function,
// but the max difference between the two is only 0.005
Expand Down
5 changes: 2 additions & 3 deletions sample/src/main/res/layout/activity_dynamic.xml
Expand Up @@ -4,13 +4,12 @@
android:layout_width="match_parent"
android:layout_height="match_parent">

<LinearLayout
<FrameLayout
android:id="@+id/progress_bars_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:padding="8dp"
android:orientation="vertical"
android:background="@drawable/bounding_box">

<com.deange.ropeprogressview.RopeProgressBar
Expand All @@ -19,7 +18,7 @@
app:dynamicLayout="true"
app:max="@integer/progress_max"/>

</LinearLayout>
</FrameLayout>

<include layout="@layout/toolbar"/>

Expand Down
5 changes: 2 additions & 3 deletions sample/src/main/res/layout/activity_static.xml
Expand Up @@ -4,21 +4,20 @@
android:layout_width="match_parent"
android:layout_height="match_parent">

<LinearLayout
<FrameLayout
android:id="@+id/progress_bars_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:padding="8dp"
android:orientation="vertical"
android:background="@drawable/bounding_box">

<com.deange.ropeprogressview.RopeProgressBar
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:max="@integer/progress_max"/>

</LinearLayout>
</FrameLayout>

<include layout="@layout/toolbar"/>

Expand Down
7 changes: 3 additions & 4 deletions sample/src/main/res/layout/activity_themed.xml
Expand Up @@ -4,25 +4,24 @@
android:layout_width="match_parent"
android:layout_height="match_parent">

<LinearLayout
<FrameLayout
android:id="@+id/progress_bars_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:padding="8dp"
android:orientation="vertical"
android:background="@drawable/bounding_box">

<com.deange.ropeprogressview.RopeProgressBar
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:primaryColor="@android:color/black"
app:secondaryColor="@android:color/darker_gray"
app:strokeWidth="16dp"
app:strokeWidth="2dp"
app:slack="64dp"
app:max="@integer/progress_max"/>

</LinearLayout>
</FrameLayout>

<include layout="@layout/toolbar"/>

Expand Down
7 changes: 4 additions & 3 deletions sample/src/main/res/values/strings.xml
@@ -1,10 +1,11 @@
<resources>
<string name="app_name">RopeProgressBar</string>
<string name="text_animate_to">Animate to:</string>
<string name="default_progress">0</string>
<string name="button_go">Go!</string>
<string name="activity_static">Static Height</string>
<string name="activity_dynamic">Dynamic Height</string>
<string name="activity_themed">Themed</string>
<string name="activity_custom">Custom Text</string>

<string name="text_animate_to">Animate to:</string>
<string name="default_progress">0</string>
<string name="button_go">Go!</string>
</resources>

0 comments on commit ccd499c

Please sign in to comment.