[置顶] Android_自定义View之跳动的loading

这篇文章记录一下Android自定义UI的知识,一个小小的跳动的loading,特别感谢Nil的指点…废话不多,先来效果

SoHOT链接和star地址:SoHOT源码地址,在文章最后最后有github开源地址,别错过
如果您还没有去给SoHOT一颗star而直接看这个项目,那简直是有点损失,希望您点上面的链接,去star和下载体验一下SoHoT,捡起那个大西瓜再来捡这个小芝麻项目,你懂的!!

[置顶] Android_自定义View之跳动的loading_第1张图片

凑乎看吧,录制的效果不如真机上平滑…凑乎看吧.

  • 1,自定义View的自定义属性提取
  • 2,View中动画的实现

我们一步步分解这个loading来看,我们顶住一个小球来分析,
这一个小球走的路线有没有是一个正弦函数的路线.

如果三个小球全部一起运动的话就是重叠的,那我们就在刷新view的时候分别给每个小球来个间隔不就行了.这样就有这种错乱的赶脚了…

好的首先我们先定义我们的自定义属性,
为了更好的扩展,我们需要尽可能抽取出来三个小球的颜色,
小球的大小等,

然后就是书写我们的自定义view,
我们在构造函数中,初始化我们需要的各种属性和参数
[置顶] Android_自定义View之跳动的loading_第2张图片

然后我们在onSizeChanged中把小球的初始x 和y值 计算出来,让小球在我们设定的移动范围垂直距离中间开始执行正弦轨迹,

[置顶] Android_自定义View之跳动的loading_第3张图片

计算好小球的初始值位置坐标后,就是初始化我们的动画类,
这里用ValueAnimaiton来完成的正弦的参数变化,

[置顶] Android_自定义View之跳动的loading_第4张图片
这里我们拿到一个ValueAnimator 传入他的变化值范围,在他的变化监听中根据正弦的函数,根据x值得到y值,这样小球每时每刻的坐标就计算出来了,然后调用invalidate方法,通知ondraw方法,来绘制
这样小球的曲线路径就展现出来了.

延时的代码如下,[置顶] Android_自定义View之跳动的loading_第5张图片

然后就是看我们的ondraw中无非就是根据x y 的值分别绘制此时小球的位置,给人感官上的感觉就是动画执行起来了.
是的就是这个道理!

我把这个控件的完整代码放到最后,有兴趣的可以运行下,工程就不上传了..

public class ColorBallView extends View {
    private static final int STRETCHING_X = 150;
    private static final int STRETCHING_Y = 80;
    private static final float BALL_MAX = 1 / 10F;
    private static final float DE_VIEW_SIZE = 120F;
    private static final long ANIMATION_TIME = 1200;
    private static final long INTERVAL_TIME = 400;
    private final int DE_BALL_SIZE = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 20, getResources().getDisplayMetrics());
    private Paint mPaint;
    private int mFirstBallColor;
    private int mSecondBallColor;
    private int mThirdBallColor;
    private int mBallRadius;

    private int mWidth;
    private int mHeight;

    private int cx, cy, cx1, cy1, cx2, cy2;
    private int offsetY, offsetY1, offsetY2;
    private boolean isDrawSecond, isDrawThird;

    public ColorBallView(Context context) {
        this(context, null);
    }

    public ColorBallView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public ColorBallView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        TypedArray typedArray = context.getTheme().obtainStyledAttributes(attrs, R.styleable.ColorBallView, defStyleAttr, 0);
        int indexCount = typedArray.getIndexCount();
        for (int i = 0; i < indexCount; i++) {
            int attr = typedArray.getIndex(i);
            switch (attr) {
                case R.styleable.ColorBallView_ballSize:
                    mBallRadius = typedArray.getDimensionPixelSize(attr, DE_BALL_SIZE);
                    break;
                case R.styleable.ColorBallView_firstBallColor:
                    mFirstBallColor = typedArray.getColor(attr, Color.BLACK);
                    break;
                case R.styleable.ColorBallView_secondColor:
                    mSecondBallColor = typedArray.getColor(attr, Color.GREEN);
                    break;
                case R.styleable.ColorBallView_thirdBallColor:
                    mThirdBallColor = typedArray.getColor(attr, Color.YELLOW);
                    break;
            }
        }
        typedArray.recycle();
        init();
        startAllBallAnimation();
    }

    private void startAllBallAnimation() {
        Observable.interval(INTERVAL_TIME, INTERVAL_TIME, TimeUnit.MILLISECONDS)
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Subscriber<Long>() {
                    @Override
                    public void onCompleted() {

                    }

                    @Override
                    public void onError(Throwable e) {

                    }

                    @Override
                    public void onNext(Long aLong) {
                        int i = aLong.intValue();
                        if (i == 0) {
                            initAnimation();

                        } else if (i == 1) {
                            initAnimation1();
                        } else if (i == 2) {
                            initAnimation2();
                            this.unsubscribe();
                            onCompleted();
                        }
                    }
                });
    }

    private void init() {
        mPaint = new Paint();
        mPaint.setDither(true);
        mPaint.setAntiAlias(true);
        mPaint.setStrokeCap(Paint.Cap.ROUND);
        mPaint.setStrokeJoin(Paint.Join.ROUND);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);

        int widthSize;
        int heightSize;

        if (widthMode == MeasureSpec.AT_MOST || widthMode == MeasureSpec.UNSPECIFIED) {
            widthSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, DE_VIEW_SIZE, getResources().getDisplayMetrics());
            widthMeasureSpec = MeasureSpec.makeMeasureSpec(widthSize, MeasureSpec.EXACTLY);
        }

        if (heightMode == MeasureSpec.AT_MOST || heightMode == MeasureSpec.UNSPECIFIED) {
            heightSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, DE_VIEW_SIZE, getResources().getDisplayMetrics());
            heightMeasureSpec = MeasureSpec.makeMeasureSpec(heightSize, MeasureSpec.EXACTLY);
        }

        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mWidth = w;
        mHeight = h;
        mBallRadius = (int) ((int) (Math.min(Math.min(w, h) * BALL_MAX, mBallRadius)) * .5F);

        cx = mBallRadius;
        cy = mHeight / 2;
        cx1 = cx;
        cx2 = cx;
        cy1 = cy;
        cy2 = cy;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        drawBall(canvas);
    }

    private void drawBall(Canvas canvas) {
        mPaint.setColor(mFirstBallColor);
        canvas.drawCircle(cx, cy + offsetY, mBallRadius, mPaint);
        if (isDrawSecond) {
            mPaint.setColor(mSecondBallColor);
            canvas.drawCircle(cx1, cy1 + offsetY1, mBallRadius, mPaint);
        }
        if (isDrawThird) {
            mPaint.setColor(mThirdBallColor);
            canvas.drawCircle(cx2, cy2 + offsetY2, mBallRadius, mPaint);
        }

    }

    private void initAnimation() {
        ValueAnimator valueAnimator = getValueAni();
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                Float aFloat = Float.valueOf(animation.getAnimatedValue().toString());
                cx = (int) (aFloat * STRETCHING_X) + mBallRadius;
                offsetY = (int) ((float) Math.sin(2 * Math.PI * aFloat) * STRETCHING_Y);
                invalidate();
            }
        });
        valueAnimator.start();
    }


    private void initAnimation1() {
        ValueAnimator valueAnimator = getValueAni();
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                Float aFloat = Float.valueOf(animation.getAnimatedValue().toString());
                cx1 = (int) (aFloat * STRETCHING_X) + mBallRadius;
                offsetY1 = (int) ((float) Math.sin(2 * Math.PI * aFloat) * STRETCHING_Y);
                isDrawSecond = true;
                invalidate();
            }
        });
        valueAnimator.start();
    }


    private void initAnimation2() {
        ValueAnimator valueAnimator = getValueAni();
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                Float aFloat = Float.valueOf(animation.getAnimatedValue().toString());
                cx2 = (int) (aFloat * STRETCHING_X) + mBallRadius;
                offsetY2 = (int) ((float) Math.sin(2 * Math.PI * aFloat) * STRETCHING_Y);
                isDrawThird = true;
                invalidate();
            }
        });
        valueAnimator.start();
    }

    private ValueAnimator getValueAni() {
        ValueAnimator valueAnimator = ValueAnimator.ofFloat(0f, 1.1f);
        valueAnimator.setDuration(ANIMATION_TIME);
        valueAnimator.setInterpolator(new LinearInterpolator());
        valueAnimator.setRepeatCount(ValueAnimator.INFINITE);
        valueAnimator.setRepeatMode(ValueAnimator.REVERSE);

        return valueAnimator;
    }
}

再来点福利,基于最新百度地图API开发的项目已开源,求star基于百度地图API开源项目

你可能感兴趣的:(android,动画,自定义view,自定义Loading,正弦函数)