天天看點

安卓自定義控件之餅圖

自定義控件最重要的是需要重寫三個構造方法,分别是一個參數、連個參數以及三個參數的,這是最重要的三個。接下來,想要自定義顯示餅圖就要重寫onMeasure以及onDraw方法。
   onMeasure方法的主要作用就是重新計算控件的寬高并設定,因為要顯示的是餅狀圖,而餅狀圖是圓形的相對于控件來說餅狀圖就是正方形的,是以就要通過這個方法來進行限制,使控件的寬高保持所設定的最小的寬高。
           
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    int height = getDefaultSize(getSuggestedMinimumHeight(),
            heightMeasureSpec);
    int width = getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec);
    minMargin = Math.min(width, height);//取邊框的最小值來顯示圖表
    setMeasuredDimension(minMargin, minMargin);
    rect = new RectF(,,minMargin,minMargin);//按照所設定的寬高中的最小值來設定範圍
}
           
onDraw方法的主要作用就是渲染繪制視圖,也就是人們肉眼所看到的部分的效果了,其實畫餅狀圖追主要用到的就是畫布的drawArc方法,他的主要作用就是花扇形圖;
           
@Override
    protected void onDraw(Canvas canvas) {
        if(backgroundBitmap == null) {
            //顯示是百分比餅圖,動畫以及非動畫
            showPercentCircle(canvas);
        }else {
            canvas.drawBitmap(getCroppedRoundBitmap(backgroundBitmap,getWidth()/),,,null);
        }

        //畫空心圓
        if(strokeWidth != null) {
            Paint paintStroke = new Paint();
            paintStroke.setColor(Color.WHITE);
            canvas.drawCircle(getWidth() /  , getHeight() /  ,strokeWidth * minMargin, paintStroke);
        }

        //繪制除百分比以外文字
        if(showText != null){
            if(showTextCenterX == null || showTextBaselineY == null) {
                showTextPaint.setTextSize(showTextSize);
                showTextPaint.setColor(showTextColor);
                setTextLocation();
            }
            if(showTextCenterX != null && showTextBaselineY != null) {
                showTextPaint.setTextSize(showTextSize);
                showTextPaint.setColor(showTextColor);
                canvas.drawText(showText, showTextCenterX, showTextBaselineY, showTextPaint);
            }
        }
    }
           
接下來就是各個資料的初始化方法了:
           
/**
     *  設定要顯示的内容,主要是,名稱顔色以及百分比,此時的百分比是10的整數倍形式
     * @param list
     */
    public void setShowList(List<Map<String,Object>> list,Boolean isShowAnim){
        int percent = ;
        for(int i =  ; i < list.size() ; i++){
            percent += (int) list.get(i).get(KEY_PERCENT);
        }
        if(percent > ){
            try {
                throw new Exception();
            }catch (Exception e){
                LogUtils.logE(getClass().getName(),"百分比總和不能大于100");
                return;
            }
        }else if(percent < ){
            HashMap<String, Object> map = new HashMap<>();
            map.put(KEY_NAME,"");
            map.put(KEY_PERCENT, - percent);
            map.put(KEY_COLOR, Color.WHITE);
            list.add(map);
        }//判斷一下目前傳遞的百分比的總和是否大于100,大于則抛出異常,否則繼續判斷是否小于100,小于100的部分則用白色補齊,是因為背景不一定會為非白色背景
        this.list = list;
        this.isShowAnim = (isShowAnim == null) || isShowAnim == false ? false : true;//進行是否顯示動畫的初始化
        rotationAngle = ;//動畫顯示中正在旋轉的角度
        oldRotationAngle = ;
        postInvalidate();
        initViewSetting();
        if(isShowAnim != null && isShowAnim){
            startAnimation(anim);
        }
    }

    /**
     * 設定餅狀圖空心以及空心大小
     * @param strokeWidth 空心圓半徑是父背景半徑的百分比
     */
    public void setStrokeWidth(Float strokeWidth){
        this.strokeWidth = (strokeWidth == null) ?  : strokeWidth / ;
        if(strokeWidth > ){
            try {
                throw new Exception();
            }catch (Exception e){
                LogUtils.logE(getClass().getName(),"百分比不能大于1");
            }
        }
        postInvalidate();
    }

    /**
     * 設定文字以及中心文字大小
     * @param showText 文字
     * @param showTextColor 文字顔色
     * @param showTextSize 文字大小
     * @param showTextAlign 文字方位
     */
    public void setShowText(String showText , Integer showTextColor ,Integer showTextSize,Integer showTextAlign){
        this.showText = showText;
        this.showTextColor = showTextColor;
        this.showTextSize = showTextSize;
        this.showTextAlign = showTextAlign;
        postInvalidate();
    }

    /**
     * 設定餅狀圖背景圖檔,注意,如果設定餅狀圖背景圖檔則百分比的餅狀圖失效
     * @param backgroundBitmap
     */
    public void setBackgroundImage(Bitmap backgroundBitmap){
        this.backgroundBitmap = backgroundBitmap;
        postInvalidate();
    }
           

ondraw方法内部的調用方法是:

/**
     * //顯示是百分比餅圖,動畫以及非動畫
     * @param canvas
     */
    private void showPercentCircle(Canvas canvas){
        if(isShowAnim){
            for(int i =  ; i < list.size() ; i++){
                paint.setColor((int) list.get(i).get(KEY_COLOR));    //設定畫筆顔色
                int percent = ;
                if( i == ) {//0的時候單獨設定,因為需要從0開始
                    canvas.drawArc(rect, , (float) (rotationAngle * (int) list.get(i).get(KEY_PERCENT) *  /), true, paint);
                }else {
                    for (int j = ; j < i; j++) {
                        percent += (int) list.get(j).get(KEY_PERCENT);
                    }
                    percent = (int) (percent *  /  * maxRotationAngle);//計算已經顯示過的百分比所旋轉的角度
                    canvas.drawArc(rect,percent,
                            (float) (rotationAngle * (int) list.get(i).get(KEY_PERCENT) *  / + )
                            ,true,paint);//非零狀态下的動畫繪制需要從該條的起點開始繪制,每次繪制需要加1,防止四舍五入産生誤差
                }
            }
        }else {
            oldRotationAngle = ;
            for(int i =  ; i < (list != null ? list.size() : ) ; i++){//(list != null ? list.size() : 0)
                paint.setColor((int)list.get(i).get(KEY_COLOR));    //設定畫筆顔色
                rotationAngle = (int) ((int)list.get(i).get(KEY_PERCENT) *  /  * maxRotationAngle);
                canvas.drawArc(rect,oldRotationAngle, rotationAngle, true, paint);
                oldRotationAngle = oldRotationAngle + rotationAngle;
            }
        }

        if(rotationAngle == maxRotationAngle || !isShowAnim){
            for(int j =  ; j < (list != null ? list.size() : ) ; j++){
                int percent = ;
                for (int k = ; k < j; k++) {
                    percent += (int) list.get(k).get(KEY_PERCENT);
                }
                percent = (int) (percent *  /  * maxRotationAngle);
                paint.setColor(Color.GREEN);    //設定畫筆顔色
                if(minMargin !=  ) {
                    xChartCalcPercentText = new XChartCalc();
                    xChartCalcPercentText.CalcArcEndPointXY(minMargin /  , minMargin / );
                    canvas.drawText((String) list.get(j).get(KEY_NAME)
                            ,xChartCalcPercentText.setCirAngle(minMargin /  - minMargin / ,percent + (float) ((int) list.get(j).get(KEY_PERCENT) *  /) * maxRotationAngle)
                                    .getPosX()
                            ,xChartCalcPercentText.setCirAngle(minMargin /  - minMargin / ,percent + (float) ((int) list.get(j).get(KEY_PERCENT) *  /) * maxRotationAngle)
                                    .getPosY()
                            ,paint);//文字在扇形的中間線上的某一點顯示
                }
            }
        }
    }

    /**
     * 定位文本繪制的位置
     */
    private void setTextLocation() {
        fm = showTextPaint.getFontMetrics();
        //文本的寬度
        float textWidth = showTextPaint.measureText(showText);
        float textHeight = fm.descent - fm.ascent;
        float textCenterVerticalBaselineY = viewHeight /  - fm.descent + (fm.descent - fm.ascent) / ;
        switch (showTextAlign) {
            case TEXT_ALIGN_CENTER://居中
                showTextCenterX = (float)viewWidth / ;
                showTextBaselineY = textCenterVerticalBaselineY;
                break;
            case TEXT_ALIGN_LEFT_CENTER://居中靠最左
                showTextCenterX = textWidth / ;
                showTextBaselineY = textCenterVerticalBaselineY;
                break;
            case TEXT_ALIGN_RIGHT_CENTER://居中靠最右
                showTextCenterX = viewWidth - textWidth / ;
                showTextBaselineY = textCenterVerticalBaselineY;
                break;
            case TEXT_ALIGN_CENTER_HORIZONTAL_BOTTOM://居中靠最下
                showTextCenterX = Float.valueOf(viewWidth / );
                showTextBaselineY = viewHeight - fm.bottom;
                break;
            case TEXT_ALIGN_CENTER_HORIZONTAL_TOP://居中靠最上
                showTextCenterX = Float.valueOf(viewWidth / );
                showTextBaselineY = -fm.ascent;
                break;
            case TEXT_ALIGN_LEFT_TOP://左上
                showTextCenterX = textWidth / ;
                showTextBaselineY =  -fm.ascent;
                break;
            case TEXT_ALIGN_LEFT_BOTTOM://左下
                showTextCenterX = textWidth / ;//
                showTextBaselineY = viewHeight - fm.bottom;
                break;
            case TEXT_ALIGN_RIGHT_TOP://右上
                showTextCenterX = viewWidth - textWidth / ;
                showTextBaselineY = -fm.ascent;
                break;
            case TEXT_ALIGN_RIGHT_BOTTOM://右下
                showTextCenterX = viewWidth - textWidth / ;
                showTextBaselineY = viewHeight - fm.bottom;
                break;
            case TEXT_ALIGN_CENTER_TOP://居中靠上
                showTextCenterX = (float)viewWidth / ;
                showTextBaselineY = textCenterVerticalBaselineY - textHeight / ;
                break;
            case TEXT_ALIGN_CENTER_BOTTOM:/** 居中靠下顯示  */
                showTextCenterX = (float)viewWidth / ;
                showTextBaselineY = textCenterVerticalBaselineY + textHeight / ;
                break;
            case TEXT_ALIGN_CENTER_LEFT:/** 居中靠左顯示  */
                showTextCenterX = (float)viewWidth /  - textWidth /  ;
                showTextBaselineY = textCenterVerticalBaselineY;
                break;
            case TEXT_ALIGN_CENTER_RIGHT:/** 居中靠右顯示  */
                showTextCenterX = (float)viewWidth /  + textWidth /  ;
                showTextBaselineY = textCenterVerticalBaselineY;
                break;
            case TEXT_ALIGN_CENTER_LEFT_TOP:/** 居中靠左上顯示  */
                showTextCenterX = (float)viewWidth /  - textWidth /  ;
                showTextBaselineY = textCenterVerticalBaselineY - textHeight /  ;
                break;
            case TEXT_ALIGN_CENTER_LEFT_BOTTOM:/** 居中靠左下顯示  */
                showTextCenterX = (float)viewWidth /  - textWidth /  ;
                showTextBaselineY = textCenterVerticalBaselineY + textHeight /  ;
                break;
            case TEXT_ALIGN_CENTER_RIGHT_TOP:/** 居中靠右上顯示  */
                showTextCenterX = (float)viewWidth /  + textWidth /  ;
                showTextBaselineY = textCenterVerticalBaselineY - textHeight /  ;
                break;
            case TEXT_ALIGN_CENTER_RIGHT_BOTTOM:/** 居中靠右下顯示  */
                showTextCenterX = (float)viewWidth /  + textWidth /  ;
                showTextBaselineY = textCenterVerticalBaselineY + textHeight /  ;
                break;
        }
    }
           
這些是最初的的一些基礎代碼,其中空心圓的實作是在實心圓的外層添加一個小于實心圓半徑的白色或者說是背景色的實心圓,這樣在最終繪制出來的圖檔的效果上就會是顯示出空心圓。
   動畫的顯示是使用的Animation的計時器,因為想要實作動畫的效果必須要開子線程,但是所開的子線程銷毀又是一個問題,是以使用動畫來計時,在計時之後他會自動将自己本身的線程銷毀,而之是以在計時的時候對圖像進行繪制是使用了Animation的applyTransformation方法,他會在動畫顯示的時候進行不定時的回調,其中他的參數interpolatedTime是表示動畫已經執行的時間的百分比,其範圍是“0-1”,每次回調該方法的時候都使用view.postInvalidate();方法來對視圖進行重繪:
           
@Override
    protected void applyTransformation(float interpolatedTime, Transformation t) {
        super.applyTransformation(interpolatedTime, t);
        view.postInvalidate();
        setListener.percent(interpolatedTime);
    }
           
在使用這個控件的時候需要向該控件傳遞百分比等資料,然後在調用控件的公開方法傳遞,其格式如下:
           
list = new ArrayList<>();
        Map<String,Object> map = new HashMap<>();
        map.put(PieChartView.KEY_NAME,"red");
        map.put(PieChartView.KEY_PERCENT,);
        map.put(PieChartView.KEY_COLOR, Color.RED);
        list.add(map);
        map = new HashMap<>();
        map.put(PieChartView.KEY_NAME,"gray");
        map.put(PieChartView.KEY_PERCENT,);
        map.put(PieChartView.KEY_COLOR, Color.GRAY);
        list.add(map);
        map = new HashMap<>();
        map.put(PieChartView.KEY_NAME,"blue");
        map.put(PieChartView.KEY_PERCENT,);
        map.put(PieChartView.KEY_COLOR, Color.BLUE);
        list.add(map);
        map = new HashMap<>();
        map.put(PieChartView.KEY_NAME,"black");
        map.put(PieChartView.KEY_PERCENT,);
        map.put(PieChartView.KEY_COLOR, Color.BLACK);
        list.add(map);
           

源碼下載下傳連結:http://download.csdn.net/detail/cmwly/9591797