android 自定義view_圓形進度條

  • 一:概述:
    android的自定義view提供了很多很豐富的畫布操作,這幾天公司的項目需要寫一個圓形進度條,藉着這個機會,說一下畫布的基本操作
  • 二:預期效果
    我要實現的效果如圖:
    這裏寫圖片描述
    這是一個等級效果圖,要求可以根據用戶的等級,動態的改變藍色進度條的停留位置。
    三:實現思路:
    canvas可以畫很多基本的圖形:長方形,圓形,圓角長方形,扇形…
    根據圖片我第一時間想到的就是用畫弧度實現,也就是扇形。
    由於需要有一個劃過的動畫,所以不能一次就畫完,一次畫一點,分多次畫完。
    這裏有兩種思路:
    1.起始位置一樣,每次畫的弧度不同。
    2.每次畫的弧度一樣,起始位置動態計算。
    第一種思路實現起來簡單粗暴,在這裏直接選擇第一種。
    這個自定義view的難點就是在動畫完成的時候計算結束位置有些許的麻煩,用到了一些幾何知識,如果忘了就去翻翻初中的數學課本吧。

四:代碼實現:
我們定義的一些變量,我都寫了註釋:

private RectF rectF;
    private double sweepRadius = 0;//掃過的角度(默認是0)
    boolean isRunning;//運行的控制器
    float radius = 1f;//每一幀畫完之後的角度
    float speedRadius = 10f;//每次畫的角度(可以理解爲速度)
    float starttPoint = 270f;//初始化的角度(默認在中間頂部
    private Paint mPaint;
    private int paintWidth = 2;//畫筆的寬度
    float finalRadius=0;
    double pi =3.14159265358979323846264338327950288419716939937510582097494459230781640628620899;//定義圓周率

首先在初始化的時候,我們做了一些操作

mPaint = new Paint();
mPaint.setColor(0xff1ecfff);//畫筆的顏色
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(paintWidth);//畫筆的寬度
mPaint.setAntiAlias(true);//設置抗鋸齒

在onSizeChanged函數中初始化畫筆的繪畫區域矩形rectF

@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
   super.onSizeChanged(w, h, oldw, oldh);
   rectF = new RectF(paintWidth,paintWidth,getWidth()-paintWidth,getHeight()-paintWidth);
}

下邊就是我們的核心ondraw方法了,裏邊有一些邏輯:我都做了詳細的註釋

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

        canvas.drawColor(Color.WHITE);//畫布背景顏色

        if (sweepRadius==0) return;//如果掃過的角度爲0,則不用畫

        if (finalRadius!=0){
            canvas.drawArc(rectF, starttPoint, finalRadius, false, mPaint);
            return;
        }

        canvas.drawArc(rectF, starttPoint, radius, false, mPaint);

        radius=radius+speedRadius ;
        if (radius >= sweepRadius+1f){
            //畫圓結束
            finalRadius = radius-speedRadius;
            isRunning = false;
            sweepRadius = finalRadius;//重新確切的告訴view掃過的角度
            getStarPos();//計算畫完後的座標的方法
            if (listener!=null){
                listener.onDrawFinish(starX , starY);
            }
        }

        if (!isRunning) return;
        //刷新view,每50毫秒執行一次ondraw()
        postInvalidateDelayed(50);
    }

在畫圈完成後我們有一個計算停留位置的方法getStarPos(),在計算的過程中,我們用到了圓周率,都是一些死方法,代碼如下:

    private double starX;// x座標
    private double starY;// y座標
    /**
     * 得到畫圈後的座標點
     */
    public void getStarPos(){
        double r = (getWidth()-paintWidth*2)/2;//半徑

        if (sweepRadius==0){
            starX = r;
            starY = 0;
        }else if (sweepRadius < 90d){
            starX = r + Math.sin(sweepRadius*pi/180)*r;
            starY = r - Math.cos(sweepRadius*pi/180)*r;
        }else if (sweepRadius == 90d){
            starX = r*2;
            starY = r;
        }else if (sweepRadius < 180d){
            starX = r+Math.cos((sweepRadius-90d)*pi/180)*r;
            starY = r+Math.sin((sweepRadius-90d)*pi/180)*r;
        }else if (sweepRadius == 180d){
            starX = r;
            starY = r*2;
        }else if (sweepRadius < 270d){
            starX = r - Math.sin((sweepRadius-180d)*pi/180)*r;
            starY = r + Math.cos((sweepRadius-180d)*pi/180)*r;
        }else if (sweepRadius == 270d){
            starX = 0;
            starY = r;
        }else if (sweepRadius < 360d){
            starX = r - Math.cos((sweepRadius-270d)*pi/180)*r;
            starY = r - Math.sin((sweepRadius-270d)*pi/180)*r;
        }
    }

爲了把畫完後的座標傳給外界,定義了一個藉口監聽

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

    private OnDrawingFinishListener listener;
    public void setOnDrawingFinishListener(OnDrawingFinishListener onDrawingFinishListener){
        this.listener = onDrawingFinishListener;
    }
    public interface OnDrawingFinishListener{
        void onDrawFinish(double xPos, double yPos);
    }

以上就是我們的全部實現過程了。如有那些不理解或認爲不妥的地方,請在下方給我留言。
下邊是源碼地址,裏邊有詳細的用法。
github下載源碼

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章