您的位置:首页 > 移动开发 > Android开发

Windows风格的Loading动画Android实现(3)

2015-08-29 17:02 363 查看

前言

最近在学习开源弹幕引擎源码,其中对重绘的控制的很好,值得学习,我把一部分技术应用到我的这个动画里面。第一,在这个弹幕引擎里面,各个弹幕相互独立,独立计算独立绘制。第二,不过度绘制,这种情况是丢帧的反面。简单来说就是不要在16ms内绘制两次,因为当fps大于60之后,人眼就看不出差别来了。

效果图



改进

面向对象,让每个点自己计算显示位置,自己绘制

每个点的运动轨迹是一样的,只是启动时间,启动位置不同,而这些可以通过时间偏移、位置偏移实现。独立之后逻辑变的简单。

参照系的问题

所有的点都一直有一个平移速度

不再16ms内重绘两次

计算两次更新绘制参数之间的时间间隔,如果小于16ms则让工作线程休眠,得到间隔到了16ms再更新绘制参数。

上篇博客中的uiHander没有必要,用postInvalidate()即可

Demo

Github

源码

[code]public class LoadingView extends View {

    /**
     * The cy of every point is the same.
     */
    private float cy;

    /**
     * The radius of every point is the same.
     */
    private float radius;

    /**
     * Used in animation.
     */
    private long startMillis = -1;
    private long lastMills = -1;

    /**
     * Used to make translation more smooth
     */
    private Interpolator enterInterpolator, exitInterpolator;

    /**
     * The moving velocity of the point which is not entering or exiting
     */
    private float v;

    /**
     * The number of points
     */
    private int pointNum;

    private HandlerThread workerThread;
    private Handler workerHandler;

    private long enterDuration = 600;

    private long moveDuration = 1800;

    private long exitDuraion = 600;

    private long cycle;

    private float enterDistance;
    private float exitDistance;
    private float moveVelocity;

    private float offsetBetweenPoint;
    private List<LoadingPoint> points;

    private float uniformCxOffset;

    public LoadingView(Context context) {
        super(context);
        init();
    }

    public LoadingView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public LoadingView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        pointNum = 4;

        enterInterpolator = new DecelerateInterpolator(1.6F);
        exitInterpolator = new AccelerateInterpolator(1.2F);

        cycle = enterDuration + moveDuration + exitDuraion + enterDuration * (pointNum - 1);

        points = new ArrayList<>(pointNum);
        for (int i = 0; i < pointNum; i++) {
            LoadingPoint point = new LoadingPoint(i);
            points.add(point);
        }

        workerThread = new HandlerThread("workerThread");
        workerThread.start();
        workerHandler = new Handler(workerThread.getLooper(), new Handler.Callback() {
            @Override
            public boolean handleMessage(Message message) {
                updateDrawParams();
                postInvalidate();
                return true;
            }
        });
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        initSize();
    }

    private void initSize() {
        cy = getMeasuredHeight() / 2;
        radius = getMeasuredHeight() / 3;

        enterDistance = getMeasuredWidth() * 0.425F;
        float moveDistance = getMeasuredWidth() * 0.15F;
        exitDistance = getMeasuredWidth() * 0.425F;

        moveVelocity = moveDistance / moveDuration;

        uniformCxOffset = - moveDistance * 0.5F;

        offsetBetweenPoint = moveDistance / 3;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        for (int i = 0; i < points.size(); i++){
            points.get(i).draw(canvas);
        }
        workerHandler.sendEmptyMessage(0);//update draw params on worker thread
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        workerThread.quit();
    }

    private void updateDrawParams() {
        long currentMillis = System.currentTimeMillis();
        if (startMillis == -1) {
            startMillis = currentMillis;
        }

        if (lastMills == -1) {
            lastMills = currentMillis;
        } else {
            long timeDelta = currentMillis - lastMills;
            if (timeDelta < 16) {
                try {
                    Thread.sleep(16 - timeDelta);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }

        //acquire current millis again, because this thread may sleep for not invalidating too frequently
        currentMillis = System.currentTimeMillis();

        long passMills = (currentMillis - startMillis) % cycle;

        for (int i = 0; i < points.size(); i++){
            points.get(i).update(passMills);
        }

        lastMills = currentMillis;
    }

    private class LoadingPoint {

        private int index;
        private float translateX;
        private boolean visible;

        private float cx;
        private Paint paint;

        public LoadingPoint(int i) {
            index = i;
            translateX = i * offsetBetweenPoint;
            paint = new Paint();
            paint.setDither(true);
            paint.setAntiAlias(true);
            paint.setColor(Color.parseColor("#455A64"));
            paint.setAlpha(0);
        }

        public void update(long passMills) {
            //做时间偏移
            passMills = passMills - index * enterDuration;

            //还没有出现
            if (passMills < 0) {
                visible = false;
                return;
            }
            visible = true;

            float enterX = 0;
            float exitX  = 0;

            if (passMills < enterDuration) {
                //enter
                float enterFraction = ((float) passMills) / enterDuration;
                float interpolatedEnterFraction = enterInterpolator.getInterpolation(enterFraction);
                enterX = interpolatedEnterFraction * enterDistance;

                exitX = 0;

                paint.setAlpha((int) (255 * interpolatedEnterFraction));
            } else if (passMills < enterDuration + moveDuration) {
                enterX = enterDistance;

                exitX = 0;

                paint.setAlpha(255);
            } else {
                enterX = enterDistance;

                float exitFraction = ((float) (passMills - enterDuration - moveDuration)) / exitDuraion;
                float interpolatedExitFraction = exitInterpolator.getInterpolation(exitFraction);
                exitX = interpolatedExitFraction * exitDistance;

                paint.setAlpha((int) (255 * (1 - interpolatedExitFraction)));
            }

            //move
            float moveX = passMills * moveVelocity;

            cx = enterX + moveX + exitX;
        }

        public void draw(Canvas canvas) {
            if (visible) {
                canvas.save();
                //做位置偏移
                canvas.translate(translateX + uniformCxOffset, 0);
                canvas.drawCircle(cx, cy, radius, paint);
                canvas.restore();
            }
        }
    }
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: