Android 线路径变形动画

Android line path morphing animation

我基本上有两条线,由我 canvas 上的两条路径绘制。每个人只有两个点,一个应该保持静止,另一个应该移动。我只是通过创建一个 Path 然后调用 moveTo(x1, y1) 和 lineTo(x2, y2).

来实现这一点

(x1, y1) 不会随时间变化,想实现(x2, y2) 变化时的动画

这是我现在的代码:

public class ProgressDownload extends View {

private static final String LOG_TAG = ProgressDownload.class.getSimpleName();

private static final int STROKE_WIDTH = 10;
private static final int PADDING = 50;
private static final String BACKGROUND_COLOR = "#EC5745";

private int mWidth, mHeight;
private int mProgress;
private Path mPathBlack, mPathWhite;
private Paint mPaintBlack, mPaintWhite;
private PathEffect mPathBlackEffect, mPathWhiteEffect;

public ProgressDownload(Context context, AttributeSet attrs) {
    super(context, attrs);

    setBackgroundColor(Color.parseColor(BACKGROUND_COLOR));
    setPadding(PADDING, 0, 50, PADDING);

    mPaintBlack = new Paint(Paint.ANTI_ALIAS_FLAG);
    mPaintBlack.setStyle(Paint.Style.STROKE);
    mPaintBlack.setStrokeWidth(STROKE_WIDTH);
    mPaintBlack.setColor(Color.BLACK);

    mPaintWhite = new Paint(Paint.ANTI_ALIAS_FLAG);
    mPaintWhite.setStyle(Paint.Style.STROKE);
    mPaintWhite.setStrokeWidth(STROKE_WIDTH);
    mPaintWhite.setColor(Color.WHITE);

    mPathBlackEffect = new CornerPathEffect(10);
    mPathWhiteEffect = new CornerPathEffect(10);
}

@Override
protected void onDraw(Canvas canvas) {
    if(mPathWhite != null && mPathBlack != null) {
        mPaintBlack.setPathEffect(mPathBlackEffect);
        mPaintWhite.setPathEffect(mPathWhiteEffect);

        canvas.drawPath(mPathBlack, mPaintBlack);
        canvas.drawPath(mPathWhite, mPaintWhite);
    }
}

@Override
protected void onSizeChanged(int xNew, int yNew, int xOld, int yOld) {
    super.onSizeChanged(xNew, yNew, xOld, yOld);
    mWidth = xNew - getPaddingRight();
    mHeight = yNew;
    Log.d(LOG_TAG, String.format("width and height measured are %d and %d", mWidth, mHeight));

    setPercentage(0);
}

private Path makePathBlack() {
    Path p =  new Path();

    p.moveTo(Math.max(getPaddingLeft(), mProgress*mWidth/100), mHeight/2 + calculatedeltaY());
    p.lineTo(mWidth, mHeight/2);

    return p;
}

private Path makePathWhite() {
    Path p = new Path();

    p.moveTo(getPaddingLeft(), mHeight / 2);
    p.lineTo(Math.max(getPaddingLeft(), mProgress*mWidth/100), mHeight/2 + calculatedeltaY());

    return p;
}

private int calculatedeltaY() {
    if(mProgress <= 50) {
        return  (mProgress * mWidth/6)/50;
    } else {
        return  ((100-mProgress) * mWidth/6)/50;
    }
}

public void setPercentage(int percentage) {
    if(percentage < 0 || percentage > 100)
        throw new IllegalArgumentException("setPercentage not between 0 and 100");
    mProgress = percentage;
    mPathBlack = makePathBlack();
    mPathWhite = makePathWhite();
    invalidate();
}
}

我看到了路径动画的多种可能性,我想我需要一些东西来将我当前的路径更新为新路径。

setPercentage() 这里根据传递的值更新两个路径。目前,新旧路径之间的转换有点 "brutal"。对此有何建议?

我突然开始研究对象动画师。解决方案是使用一个从当前百分比到新百分比的动画。结果正是我想要的。这是更新后的视图:

public class ProgressDownload extends View {

private static final String LOG_TAG = ProgressDownload.class.getSimpleName();

private static final int STROKE_WIDTH = 10;
private static final int PADDING = 50;
private static final long ANIMATION_DURATION = 1000;
private static final String BACKGROUND_COLOR = "#EC5745";

private int mWidth, mHeight;
private int mProgress = 0;
private Path mPathBlack, mPathWhite;
private Paint mPaintBlack, mPaintWhite;
private PathEffect mPathBlackEffect, mPathWhiteEffect;

public ProgressDownload(Context context, AttributeSet attrs) {
    super(context, attrs);

    setBackgroundColor(Color.parseColor(BACKGROUND_COLOR));
    setPadding(PADDING, 0, 50, PADDING);

    mPaintBlack = new Paint(Paint.ANTI_ALIAS_FLAG);
    mPaintBlack.setStyle(Paint.Style.STROKE);
    mPaintBlack.setStrokeWidth(STROKE_WIDTH);
    mPaintBlack.setColor(Color.BLACK);

    mPaintWhite = new Paint(Paint.ANTI_ALIAS_FLAG);
    mPaintWhite.setStyle(Paint.Style.STROKE);
    mPaintWhite.setStrokeWidth(STROKE_WIDTH);
    mPaintWhite.setColor(Color.WHITE);

    mPathBlackEffect = new CornerPathEffect(10);
    mPathWhiteEffect = new CornerPathEffect(10);
}

@Override
protected void onDraw(Canvas canvas) {
    if(mPathWhite != null && mPathBlack != null) {
        mPaintBlack.setPathEffect(mPathBlackEffect);
        mPaintWhite.setPathEffect(mPathWhiteEffect);

        canvas.drawPath(mPathBlack, mPaintBlack);
        canvas.drawPath(mPathWhite, mPaintWhite);
    }
}

@Override
protected void onSizeChanged(int xNew, int yNew, int xOld, int yOld) {
    super.onSizeChanged(xNew, yNew, xOld, yOld);
    mWidth = xNew - getPaddingRight();
    mHeight = yNew;
    Log.d(LOG_TAG, String.format("width and height measured are %d and %d", mWidth, mHeight));

    setPercentage(mProgress);
}

private Path makePathBlack() {

    if(mPathBlack ==null) {
        mPathBlack = new Path();
    }

    Path p =  new Path();
    p.moveTo(Math.max(getPaddingLeft(), mProgress*mWidth/100), mHeight/2 + calculatedeltaY());
    p.lineTo(mWidth, mHeight/2);

    mPathBlack.set(p);

    return p;
}

private void makePathWhite() {

    if(mPathWhite ==null) {
        mPathWhite = new Path();
    }

    Path p = new Path();
    p.moveTo(getPaddingLeft(), mHeight / 2);
    p.lineTo(Math.max(getPaddingLeft(), mProgress * mWidth / 100), mHeight / 2 + calculatedeltaY());

    mPathWhite.set(p);
}

private int calculatedeltaY() {
    if(mProgress <= 50) {
        return  (mProgress * mWidth/6)/50;
    } else {
        return  ((100-mProgress) * mWidth/6)/50;
    }
}

public void setPercentage(int newProgress) {
    if(newProgress < 0 || newProgress > 100)
        throw new IllegalArgumentException("setPercentage not between 0 and 100");

    ObjectAnimator anim = ObjectAnimator.ofInt(this, "progress", getProgress(), newProgress);
    anim.setDuration(ANIMATION_DURATION);
    anim.setInterpolator(new LinearInterpolator());
    anim.start();
}

public void setProgress(int progress) {
    mProgress = progress;
    makePathBlack();
    makePathWhite();
    invalidate();
}

public int getProgress() {
    return mProgress;
}
}