天天看點

安卓開發之自定義動畫控件BatteryView(電池加載動畫)

前幾天在網頁上看到一個不錯的加載動畫View後,想将它在安卓上實作一遍。效果如下,很簡單。

安卓開發之自定義動畫控件BatteryView(電池加載動畫)

因為控件實作動畫時,更新視圖的頻率會很高。為了減少記憶體的占用,決定使用SurfaceView來實作。

一、對控件的測量

使用的規則是:當控件的寬/高不是固定時,寬/高的大小為預設的寬高+padding值。反之,使用傳遞過來的寬高。

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

    int sizeWidth = MeasureSpec.getSize(widthMeasureSpec);
    int sizeHeight = MeasureSpec.getSize(heightMeasureSpec);
    int modeWidth = MeasureSpec.getMode(widthMeasureSpec);
    int modeHeight = MeasureSpec.getMode(heightMeasureSpec);

    if(modeWidth == MeasureSpec.AT_MOST || modeWidth == MeasureSpec.UNSPECIFIED ){
        sizeWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,bwidth,getContext().getResources().getDisplayMetrics());
        sizeWidth += getPaddingLeft()+getPaddingRight();
    }
    if(modeHeight == MeasureSpec.AT_MOST || modeHeight == MeasureSpec.UNSPECIFIED ){
        sizeHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,bheight,getContext().getResources().getDisplayMetrics());
        sizeHeight += getPaddingBottom()+getPaddingTop();
    }

    setMeasuredDimension(sizeWidth,sizeHeight);

}
           

二、對控件的繪制

private void drawView(Canvas canvas){

    // 繪制背景圖
    canvas.drawColor(backgroundColor);

    // 繪制電池框
    canvas.drawRoundRect(SrectF,bRadius,bRadius,paintS);

    // 繪制電池的小點
    canvas.drawRect(centerX+bwidth/2-distance,centerY-bheight/4,centerX+bwidth/2,centerY+bheight/4,paintF);

    // 更新電池填充的右邊大小
    FrectF.right =  FrectF.left+varyInnerWidth;

    // 繪制電池的填充
    canvas.drawRoundRect(FrectF,bRadius,bRadius,paintF);

    // 根據不同情況,修改電池填充的右邊大小
    if(SrectF.left+varyInnerWidth>centerX+bwidth/2-distance*2){
        // 超過範圍時
        varyInnerWidth = centerX+bwidth/2-distance*2;
    }else if(SrectF.left+varyInnerWidth==centerX+bwidth/2-distance*2){
        // 等于範圍時:
        varyInnerWidth = 0;
    }else {
        // 增長時:
        varyInnerWidth +=2;
    }


}
           

三、異步更新控件視圖

@Override
public void run() {
    while (isThreadRunning) {
        // 擷取視圖,鎖定視圖
        canvas = surfaceHolder.lockCanvas();
        if(canvas!=null){
            drawView(canvas);
            // 更新視圖
            surfaceHolder.unlockCanvasAndPost(canvas);
        }
        try {
            Thread.sleep(40); // 相當于幀頻,數值越小畫面就越流暢
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}
           

四、自定義控件屬性

隻提取了一些簡單的屬性。

<declare-styleable name="BatteryLoadingView">
    <attr name="outBWidth" format="dimension"/>
    <attr name="outBHeight" format="dimension"/>
    <attr name="bRadius" format="float"/>
    <attr name="backgroundColor" format="color"/>
    <attr name="bColor" format="color"/>
</declare-styleable>
           

五、完整BatteryView代碼

package com.cxmscb.cxm.videosplash;

import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.RectF;
import android.os.Build;
import android.util.AttributeSet;
import android.util.Log;
import android.util.TypedValue;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

/**
 * Created by cxm on 2016/9/22.
 */
public class BatteryView extends SurfaceView implements SurfaceHolder.Callback,Runnable{

    private Paint paintS,paintF;
    private int bwidth;
    private int bheight;
    int varyInnerWidth;
    private int bColor,backgroundColor;
    private float bRadius;
    private RectF SrectF,FrectF;
    private Thread thread;
    private SurfaceHolder surfaceHolder;
    private Canvas canvas;
    private boolean isThreadRunning = true;
    private int distance;
    private int centerX,centerY;

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

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

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public BatteryView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        init(attrs);
    }

    private void init(AttributeSet attrs) {
        surfaceHolder = getHolder();
        surfaceHolder.addCallback(this);
        SrectF = new RectF();
        FrectF = new RectF();
        distance = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,4,getContext().getResources().getDisplayMetrics());

        TypedArray attrArray = getContext().obtainStyledAttributes(attrs, R.styleable.BatteryLoadingView);
        if (attrArray != null) {

            bwidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,80,getContext().getResources().getDisplayMetrics());
            bheight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,30,getContext().getResources().getDisplayMetrics());

            bwidth = attrArray.getDimensionPixelSize(R.styleable.BatteryLoadingView_outBWidth, bwidth);
            bheight = attrArray.getDimensionPixelSize(R.styleable.BatteryLoadingView_outBHeight, bheight);
            bColor = attrArray.getColor(R.styleable.BatteryLoadingView_bColor,Color.GREEN);
            backgroundColor = attrArray.getColor(R.styleable.BatteryLoadingView_backgroundColor,Color.WHITE);
            bRadius = attrArray.getFloat(R.styleable.BatteryLoadingView_bRadius,9f);
            attrArray.recycle();
        }
        initPaint();


    }

    private void initPaint() {
        paintS = new Paint();
        paintF = new Paint();
        paintS.setColor(bColor);
        paintF.setColor(bColor);
        paintS.setStyle(Paint.Style.STROKE);
        paintS.setStrokeWidth(6);
    }


    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        thread = new Thread(this);
        thread.start();
        isThreadRunning = true;
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

        varyInnerWidth = 0;

        centerX = getMeasuredWidth()/2;
        centerY = getMeasuredHeight()/2 ;

        SrectF.left = centerX-bwidth/2 ;
        SrectF.top = centerY-bheight/2;
        SrectF.right =  centerX+bwidth/2-distance*2;
        SrectF.bottom = centerY+bheight/2;

        FrectF.left = SrectF.left ;
        FrectF.top = SrectF.top;
        FrectF.right =  SrectF.left;
        FrectF.bottom = SrectF.bottom;

    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        isThreadRunning = false;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int sizeWidth = MeasureSpec.getSize(widthMeasureSpec);
        int sizeHeight = MeasureSpec.getSize(heightMeasureSpec);
        int modeWidth = MeasureSpec.getMode(widthMeasureSpec);
        int modeHeight = MeasureSpec.getMode(heightMeasureSpec);

        if(modeWidth == MeasureSpec.AT_MOST || modeWidth == MeasureSpec.UNSPECIFIED ){
            sizeWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,bwidth,getContext().getResources().getDisplayMetrics());
            sizeWidth += getPaddingLeft()+getPaddingRight();
        }
        if(modeHeight == MeasureSpec.AT_MOST || modeHeight == MeasureSpec.UNSPECIFIED ){
            sizeHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,bheight,getContext().getResources().getDisplayMetrics());
            sizeHeight += getPaddingBottom()+getPaddingTop();
        }

        setMeasuredDimension(sizeWidth,sizeHeight);

    }


    private void drawView(Canvas canvas){

        canvas.drawColor(backgroundColor);

        canvas.drawRoundRect(SrectF,bRadius,bRadius,paintS);

        canvas.drawRect(centerX+bwidth/2-distance,centerY-bheight/4,centerX+bwidth/2,centerY+bheight/4,paintF);

        FrectF.right =  FrectF.left+varyInnerWidth;

        canvas.drawRoundRect(FrectF,bRadius,bRadius,paintF);


        if(SrectF.left+varyInnerWidth>centerX+bwidth/2-distance*2){
            varyInnerWidth = centerX+bwidth/2-distance*2;
        }else if(SrectF.left+varyInnerWidth==centerX+bwidth/2-distance*2){
            varyInnerWidth = 0;
        }else {
            varyInnerWidth +=2;
        }


    }


    @Override
    public void run() {
        while (isThreadRunning) {
            canvas = surfaceHolder.lockCanvas();
            if(canvas!=null){
                drawView(canvas);
                surfaceHolder.unlockCanvasAndPost(canvas);
            }
            try {
                Thread.sleep(40); // 相當于幀頻了,數值越小畫面就越流暢
            } catch (Exception e) {
                e.printStackTrace();
            }

        }
    }

}
           

效果圖Demo : Github

繼續閱讀