Add animation to Slider

This commit is contained in:
seven332 committed 2015-08-11 22:40:13 +08:00
1 parent b4d3c430a0
commit a1c11a4472
5 files changed
+260 -14

No files matched your search

@@ -16,6 +16,7 @@
package com.hippo.ehviewer.gallery.anim;
import android.os.SystemClock;
import android.view.animation.Interpolator;
import com.hippo.yorozuya.MathUtils;
@@ -72,6 +73,7 @@ abstract public class Animation {
private int mRepeatMode;
private int mRunnedCount;
private long mLastFrameTime;
public void setInterpolator(Interpolator interpolator) {
mInterpolator = interpolator;
@@ -89,13 +91,32 @@ abstract public class Animation {
mRepeatMode = repeatMode;
}
public boolean isRunning() {
return mStartTime > 0;
}
public long getLastFrameTime() {
return mLastFrameTime;
}
public void start() {
if (mStartTime == NO_ANIMATION) {
mStartTime = ANIMATION_START;
mRunnedCount = 0;
mLastFrameTime = 0;
}
}
public void startAt(long time) {
start();
setStartTime(time);
}
public void startNow() {
start();
setStartTime(SystemClock.uptimeMillis());
}
public void setStartTime(long time) {
mStartTime = time;
}
@@ -111,6 +132,7 @@ abstract public class Animation {
public void forceReset() {
mStartTime = ANIMATION_START;
mRunnedCount = 0;
mLastFrameTime = 0;
}
// TODO mRepeatMode
@@ -121,6 +143,7 @@ abstract public class Animation {
if (mStartTime == ANIMATION_START) {
mStartTime = currentTimeMillis;
}
mLastFrameTime = currentTimeMillis;
long elapse = currentTimeMillis - mStartTime;
float x = MathUtils.clamp(mDuration == 0.0f ? 1.0f : (float) elapse / mDuration, 0.0f, 1.0f); // Avoid NaN
@@ -165,7 +165,7 @@ public abstract class UploadedTexture extends BasicTexture {
protected abstract void onFreeBitmap(Bitmap bitmap);
protected void invalidateContent() {
public void invalidateContent() {
if (mBitmap != null) freeBitmap();
mContentValid = false;
mWidth = UNSPECIFIED;
@@ -17,41 +17,126 @@
package com.hippo.ehviewer.gallery.ui;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.Rect;
import android.graphics.Typeface;
import android.view.MotionEvent;
import android.view.ViewConfiguration;
import com.hippo.ehviewer.R;
import com.hippo.ehviewer.gallery.anim.FloatAnimation;
import com.hippo.ehviewer.gallery.glrenderer.GLCanvas;
import com.hippo.ehviewer.gallery.glrenderer.TextTexture;
import com.hippo.ehviewer.gallery.glrenderer.XmlResourceTexture;
import com.hippo.util.AnimationUtils;
import com.hippo.yorozuya.LayoutUtils;
import com.hippo.yorozuya.MathUtils;
import com.hippo.yorozuya.SimpleHandler;
public class Slider extends GLView {
private int mStart;
private int mEnd;
private int mProgress;
// 0.0f to 1.0f
private float mDrawProgress = 0.0f;
private float mPercent;
private int mDrawProgress;
private float mDrawPercent;
private int mTargetProgress;
private float mTargetPercent;
private boolean mReverse = false;
private int m2dp;
private int m5dp;
private int m6dp;
private int m8dp;
private int m12dp;
private int mColor = Color.WHITE;
private XmlResourceTexture mBubble;
private TextTexture mTextTexture;
private boolean mShowBubble;
private float mDrawBubbleScale = 0f;
private FloatAnimation mProgressAnimation;
private FloatAnimation mBubbleScaleAnimation;
private OnSetProgressListener mListener;
private CheckForShowBubble mCheckForShowBubble;
public Slider(Context context) {
m2dp = LayoutUtils.dp2pix(context, 2);
m5dp = LayoutUtils.dp2pix(context, 5);
m6dp = LayoutUtils.dp2pix(context, 6);
m8dp = LayoutUtils.dp2pix(context, 8);
m12dp = LayoutUtils.dp2pix(context, 12);
setMinimumHeight(m12dp);
setMinimumWidth(512);
Resources resources = context.getResources();
mBubble = new XmlResourceTexture(context, R.drawable.slider_bubble);
mTextTexture = TextTexture.create(
Typeface.createFromAsset(resources.getAssets(), "fonts/gallery_progress.ttf"),
resources.getDimensionPixelOffset(R.dimen.text_super_small),
resources.getColor(R.color.primary_text_dark),
new char[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'});
mProgressAnimation = new FloatAnimation() {
@Override
protected void onCalculate(float progress) {
super.onCalculate(progress);
mDrawPercent = get();
mDrawProgress = Math.round(MathUtils.lerp((float) mStart, mEnd, mDrawPercent));
}
};
mProgressAnimation.setInterpolator(AnimationUtils.FAST_SLOW_INTERPOLATOR);
mBubbleScaleAnimation = new FloatAnimation() {
@Override
protected void onCalculate(float progress) {
super.onCalculate(progress);
mDrawBubbleScale = get();
}
};
}
private void startProgressAnimation(float percent) {
boolean running = mProgressAnimation.isRunning();
mProgressAnimation.forceStop();
mProgressAnimation.setRange(mDrawPercent, percent);
mProgressAnimation.setDuration(Math.min(500, (long) (10 * getWidth() * Math.abs(mDrawPercent - percent))));
// Avoid fast swipe to block changing
long startTime = mProgressAnimation.getLastFrameTime();
if (running && startTime > 0) {
mProgressAnimation.startAt(startTime);
} else {
mProgressAnimation.startNow();
}
invalidate();
}
private void startShowBubbleAnimation() {
mBubbleScaleAnimation.forceStop();
mBubbleScaleAnimation.setRange(mDrawBubbleScale, 1.0f);
mBubbleScaleAnimation.setInterpolator(AnimationUtils.FAST_SLOW_INTERPOLATOR);
mBubbleScaleAnimation.setDuration((long) (300 * Math.abs(mDrawBubbleScale - 1.0f)));
mBubbleScaleAnimation.start();
invalidate();
}
private void startHideBubbleAnimation() {
mBubbleScaleAnimation.forceStop();
mBubbleScaleAnimation.setRange(mDrawBubbleScale, 0.0f);
mBubbleScaleAnimation.setInterpolator(AnimationUtils.SLOW_FAST_INTERPOLATOR);
mBubbleScaleAnimation.setDuration((long) (300 * Math.abs(mDrawBubbleScale - 0.0f)));
mBubbleScaleAnimation.start();
invalidate();
}
public void setColor(int color) {
@@ -62,6 +147,15 @@ public class Slider extends GLView {
mStart = start;
mEnd = end;
setProgress(mProgress);
// Fix bubble width
float maxWidth = mTextTexture.getMaxWidth() * Integer.toString(end).length() + m8dp;
int bubbleWidth = mBubble.getWidth();
int bubbleHeight = mBubble.getHeight();
if (maxWidth > bubbleWidth) {
mBubble.invalidateContent();
mBubble.setBound((int) maxWidth, bubbleHeight);
}
}
public void setProgress(int progress) {
@@ -69,7 +163,11 @@ public class Slider extends GLView {
if (mProgress != progress) {
int oldProgress = mProgress;
mProgress = progress;
mDrawProgress = MathUtils.delerp(mStart, mEnd, mProgress);
mPercent = MathUtils.delerp(mStart, mEnd, mProgress);
mTargetProgress = progress;
mTargetPercent = mPercent;
startProgressAnimation(mPercent);
if (mListener != null) {
mListener.onSetProgress(progress, oldProgress, false);
}
@@ -98,9 +196,20 @@ public class Slider extends GLView {
getDefaultSize(getSuggestedMinimumHeight(), heightSpec));
}
private void update() {
boolean invalidate = false;
invalidate |= mProgressAnimation.calculate(AnimationTime.get());
invalidate |= mBubbleScaleAnimation.calculate(AnimationTime.get());
if (invalidate) {
invalidate();
}
}
@Override
protected void onRender(GLCanvas canvas) {
super.onRender(canvas);
update();
int width = getWidth();
if (width < m12dp * 2) {
@@ -111,10 +220,55 @@ public class Slider extends GLView {
// Draw bar
canvas.fillRect(padding.left + dp6, padding.top + m5dp,
width - dp6 - dp6 - padding.left - padding.right, m2dp, mColor);
float drawProgressX = padding.left + dp6 + (width - dp6 - dp6 - padding.left - padding.right) *
(mReverse ? (1.0f - mDrawPercent) : mDrawPercent);
// Draw bubble
float startX = drawProgressX;
float startY = padding.top + dp6;
float scale = mDrawBubbleScale;
if (scale != 0.0f) {
canvas.save();
canvas.translate(startX, startY);
canvas.scale(scale, scale, 1.0f);
canvas.translate(-startX, -startY);
String str = Integer.toString(mDrawProgress);
float strWidth = mTextTexture.getTextWidth(str);
int bubbleY = padding.top -m2dp - mBubble.getHeight();
int bubbleX = (int) drawProgressX - mBubble.getWidth() / 2;
mBubble.draw(canvas, bubbleX, bubbleY);
mTextTexture.drawText(canvas, str, (int) (drawProgressX - strWidth / 2), bubbleY + m8dp);
canvas.restore();
}
// Draw controllor
canvas.fillOval(padding.left + dp6 + (width - dp6 - dp6 - padding.left - padding.right) *
(mReverse ? (1.0f - mDrawProgress) : mDrawProgress),
padding.top + dp6, dp6, dp6, mColor);
scale = 1.0f - mDrawBubbleScale;
if (scale != 0.0f) {
canvas.save();
canvas.translate(startX, startY);
canvas.scale(scale, scale, 1.0f);
canvas.translate(-startX, -startY);
canvas.fillOval(drawProgressX, padding.top + dp6, dp6, dp6, mColor);
canvas.restore();
}
}
}
private void setShowBubble(boolean showBubble) {
if (mShowBubble != showBubble) {
mShowBubble = showBubble;
if (showBubble) {
startShowBubbleAnimation();
} else {
startHideBubbleAnimation();
}
}
}
@@ -133,22 +287,41 @@ public class Slider extends GLView {
int progress = Math.round(MathUtils.lerp((float) mStart, (float) mEnd,
MathUtils.clamp((mReverse ? (getWidth() - padding.right - dp6 - x) : (x - dp6 - padding.left)) /
(getWidth() - dp6 - dp6 - padding.left - padding.right), 0.0f, 1.0f)));
float oldDrawProgress = mDrawProgress;
mDrawProgress = MathUtils.delerp(mStart, mEnd, progress);
if (mDrawProgress != oldDrawProgress) {
invalidate();
float percent = MathUtils.delerp(mStart, mEnd, progress);
// ACTION_CANCEL not changed
if (action == MotionEvent.ACTION_CANCEL) {
progress = mProgress;
percent = mPercent;
}
if (mTargetProgress != progress) {
mTargetProgress = progress;
mTargetPercent = percent;
startProgressAnimation(percent);
}
if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
if (progress != mProgress) {
SimpleHandler.getInstance().removeCallbacks(mCheckForShowBubble);
setShowBubble(false);
} else if (action == MotionEvent.ACTION_DOWN) {
if (mCheckForShowBubble == null) {
mCheckForShowBubble = new CheckForShowBubble();
}
SimpleHandler.getInstance().postDelayed(mCheckForShowBubble, ViewConfiguration.getTapTimeout());
}
if (action == MotionEvent.ACTION_UP) {
if (mProgress != progress) {
int oldProgress = mProgress;
mProgress = progress;
mPercent = mDrawPercent;
if (mListener != null) {
mListener.onSetProgress(progress, oldProgress, true);
}
}
}
break;
}
@@ -159,4 +332,12 @@ public class Slider extends GLView {
void onSetProgress(int newProgress, int oldProgress, boolean byUser);
}
private final class CheckForShowBubble implements Runnable {
@Override
public void run() {
setShowBubble(true);
}
}
}