Windows風格的Loading動畫Android實現(2)

前言

衆所周知,爲了不ANR,不可以在UI線程上執行耗時的操作。所以爲了效率,也爲了通用,我把計算繪製參數的操作放到工作線程中去了。參考了這篇文章

首先創建一個工作線程(workerThread),一個與workerThread關聯的Handler(workerHandler),還有一個和UI線程關聯的Handler(uiHandler)。在onDraw()執行結束之後,會通過workerHander發消息,workerThread收到消息會去重新計算繪製參數。完成之後,會通過uiHandler發消息,UI線程收到消息之後調用invalidate(),會導致onDraw()被執行。進入下一次循環。

這樣就實現了,重新計算繪製參數在workerThread執行,invalidate()在UI線程執行。在這裏,handler充當信使的作用,它可以在其他線程向自己關聯的線程發消息、post runnable等等。

HandlerThread知識

HandlerThread類的註釋:

Handy class for starting a new thread that has a looper. The looper can then be  used to create handler classes. Note that start() must still be called.

Handler類的註釋

/**
* A Handler allows you to send and process {@link Message} and Runnable
* objects associated with a thread's {@link MessageQueue}.  Each Handler
* instance is associated with a single thread and that thread's message
* queue.  When you create a new Handler, it is bound to the thread /
* message queue of the thread that is creating it -- from that point on,
* it will deliver messages and runnables to that message queue and execute
* them as they come out of the message queue.
* 
 * <p>There are two main uses for a Handler: (1) to schedule messages and
* runnables to be executed as some point in the future; and (2) to enqueue
* an action to be performed on a different thread than your own.......

源碼

public class LoadingView extends View {

    /**
     * A list that contains the cx of each point.
     */
    private List<Float> cx;

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

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

    /**
     * The paints that used to draw each point.
     */
    private List<Paint> paints;

    /**
     * The length that point transfer to enter and exit.
     */
    private float dx;

    private List<Float> dxs;

    /**
     * The offset between each point.
     */
    private float offset;

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

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

    /**
     * The time one point enter or exit
     */
    private long duration;

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

    /**
     * The number of points
     */
    private int pointNum;
    private float cxOffest;
    private Handler uiHandler;
    private HandlerThread workerThread;
    private Handler workerHandler;

    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;
        cx = new ArrayList<Float>(Collections.nCopies(pointNum, 0.0F));

        Paint paint = new Paint();
        paint.setDither(true);
        paint.setAntiAlias(true);
        paint.setColor(Color.parseColor("#00BCD4"));
        paint.setAlpha(0);

        paints = new ArrayList<Paint>();
        for (int i = 0; i < pointNum; i++) {
            paints.add(new Paint(paint));
        }

        enterInterpolator = new DecelerateInterpolator(1.5F);
        exitInterpolator = new AccelerateInterpolator(2.0F);

        duration = 600;

        uiHandler = new Handler(new Handler.Callback() {
            @Override
            public boolean handleMessage(Message message) {
                invalidate();
                return true;
            }
        });

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

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

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

        cxOffest = (getMeasuredWidth() - 2*dx - v*duration*3) * 0.5F;

        offset = radius * 3;

    }

    @Override
    protected void onDraw(Canvas canvas) {
        for (int i = 0; i < cx.size(); i++) {
            canvas.drawCircle(cx.get(i), cy, radius, paints.get(i));
        }
        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;
        }

        // (pointNum * 2 * duration) is a cycle
        long passMills = (currentMillis - startMillis) % (pointNum * 2 * duration);

        // updateDrawParams each point's cx and alpha
        for (int i = 0; i < cx.size(); i++) {
            long step = passMills / duration;

            float animationFraction = (passMills % duration) * 1.0F / duration;

            float enterX, transX, exitX;

            if (step < 4) { // entering half
                if (i < step) {
                    enterX = dx - i*offset + i*duration*v;
                    transX = (passMills - (i + 1) * duration) * v;
                    exitX = 0;
                    paints.get(i).setAlpha(255);
                } else if (i == step) {
                    float interpolatedFraction = enterInterpolator.getInterpolation(animationFraction);
                    enterX = interpolatedFraction*dx - i*offset + i*duration*v;
                    transX = 0;
                    exitX = 0;
                    paints.get(i).setAlpha((int) (255*interpolatedFraction));
                } else {
                    enterX = 0;
                    transX = 0;
                    exitX =0;
                    paints.get(i).setAlpha(0);
                }
            } else { // exiting half
                if (i < step-4){
                    enterX = dx - i*offset + i*duration*v;
                    transX = (passMills - (i + 1) * duration) * v;
                    exitX = dx;
                    paints.get(i).setAlpha(0);
                } else if (i == step-4){
                    float interpolatedFraction = exitInterpolator.getInterpolation(animationFraction);
                    enterX = dx - i*offset + i*duration*v;
                    transX = (passMills - (i + 1) * duration) * v;
                    exitX = interpolatedFraction * dx;
                    paints.get(i).setAlpha((int) (255*(1-interpolatedFraction)));
                } else {
                    enterX = dx - i*offset + i*duration*v;
                    transX = (passMills - (i + 1) * duration) * v;
                    exitX = 0;
                    paints.get(i).setAlpha(255);
                }
            }
            cx.set(i, cxOffest + enterX + transX + exitX);
        }
    }
}
發佈了66 篇原創文章 · 獲贊 13 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章