安卓自定义控件之饼图

   自定义控件最重要的是需要重写三个构造方法,分别是一个参数、连个参数以及三个参数的,这是最重要的三个。接下来,想要自定义显示饼图就要重写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(0,0,minMargin,minMargin);//按照所设置的宽高中的最小值来设置范围
}
   onDraw方法的主要作用就是渲染绘制视图,也就是人们肉眼所看到的部分的效果了,其实画饼状图追主要用到的就是画布的drawArc方法,他的主要作用就是花扇形图;
 @Override
    protected void onDraw(Canvas canvas) {
        if(backgroundBitmap == null) {
            //显示是百分比饼图,动画以及非动画
            showPercentCircle(canvas);
        }else {
            canvas.drawBitmap(getCroppedRoundBitmap(backgroundBitmap,getWidth()/2),0,0,null);
        }

        //画空心圆
        if(strokeWidth != null) {
            Paint paintStroke = new Paint();
            paintStroke.setColor(Color.WHITE);
            canvas.drawCircle(getWidth() / 2 , getHeight() / 2 ,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> list,Boolean isShowAnim){
        int percent = 0;
        for(int i = 0 ; i < list.size() ; i++){
            percent += (int) list.get(i).get(KEY_PERCENT);
        }
        if(percent > 100){
            try {
                throw new Exception();
            }catch (Exception e){
                LogUtils.logE(getClass().getName(),"百分比总和不能大于100");
                return;
            }
        }else if(percent < 100){
            HashMap map = new HashMap<>();
            map.put(KEY_NAME,"");
            map.put(KEY_PERCENT,100 - 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 = 0;//动画显示中正在旋转的角度
        oldRotationAngle = 0;
        postInvalidate();
        initViewSetting();
        if(isShowAnim != null && isShowAnim){
            startAnimation(anim);
        }
    }

    /**
     * 设置饼状图空心以及空心大小
     * @param strokeWidth 空心圆半径是父背景半径的百分比
     */
    public void setStrokeWidth(Float strokeWidth){
        this.strokeWidth = (strokeWidth == null) ? 0 : strokeWidth / 2;
        if(strokeWidth > 1){
            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 = 0 ; i < list.size() ; i++){
                paint.setColor((int) list.get(i).get(KEY_COLOR));    //设置画笔颜色
                int percent = 0;
                if( i == 0) {//0的时候单独设置,因为需要从0开始
                    canvas.drawArc(rect, 0, (float) (rotationAngle * (int) list.get(i).get(KEY_PERCENT) * 1.0 /100), true, paint);
                }else {
                    for (int j = 0; j < i; j++) {
                        percent += (int) list.get(j).get(KEY_PERCENT);
                    }
                    percent = (int) (percent * 1.0 / 100 * maxRotationAngle);//计算已经显示过的百分比所旋转的角度
                    canvas.drawArc(rect,percent,
                            (float) (rotationAngle * (int) list.get(i).get(KEY_PERCENT) * 1.0 /100 + 1)
                            ,true,paint);//非零状态下的动画绘制需要从该条的起点开始绘制,每次绘制需要加1,防止四舍五入产生误差
                }
            }
        }else {
            oldRotationAngle = 0;
            for(int i = 0 ; i < (list != null ? list.size() : 0) ; i++){//(list != null ? list.size() : 0)
                paint.setColor((int)list.get(i).get(KEY_COLOR));    //设置画笔颜色
                rotationAngle = (int) ((int)list.get(i).get(KEY_PERCENT) * 1.0 / 100 * maxRotationAngle);
                canvas.drawArc(rect,oldRotationAngle, rotationAngle, true, paint);
                oldRotationAngle = oldRotationAngle + rotationAngle;
            }
        }

        if(rotationAngle == maxRotationAngle || !isShowAnim){
            for(int j = 0 ; j < (list != null ? list.size() : 0) ; j++){
                int percent = 0;
                for (int k = 0; k < j; k++) {
                    percent += (int) list.get(k).get(KEY_PERCENT);
                }
                percent = (int) (percent * 1.0 / 100 * maxRotationAngle);
                paint.setColor(Color.GREEN);    //设置画笔颜色
                if(minMargin != 0 ) {
                    xChartCalcPercentText = new XChartCalc();
                    xChartCalcPercentText.CalcArcEndPointXY(minMargin / 2 , minMargin / 2);
                    canvas.drawText((String) list.get(j).get(KEY_NAME)
                            ,xChartCalcPercentText.setCirAngle(minMargin / 2 - minMargin / 6,percent + (float) ((int) list.get(j).get(KEY_PERCENT) * 0.5 /100) * maxRotationAngle)
                                    .getPosX()
                            ,xChartCalcPercentText.setCirAngle(minMargin / 2 - minMargin / 6,percent + (float) ((int) list.get(j).get(KEY_PERCENT) * 0.5 /100) * maxRotationAngle)
                                    .getPosY()
                            ,paint);//文字在扇形的中间线上的某一点显示
                }
            }
        }
    }

    /**
     * 定位文本绘制的位置
     */
    private void setTextLocation() {
        fm = showTextPaint.getFontMetrics();
        //文本的宽度
        float textWidth = showTextPaint.measureText(showText);
        float textHeight = fm.descent - fm.ascent;
        float textCenterVerticalBaselineY = viewHeight / 2 - fm.descent + (fm.descent - fm.ascent) / 2;
        switch (showTextAlign) {
            case TEXT_ALIGN_CENTER://居中
                showTextCenterX = (float)viewWidth / 2;
                showTextBaselineY = textCenterVerticalBaselineY;
                break;
            case TEXT_ALIGN_LEFT_CENTER://居中靠最左
                showTextCenterX = textWidth / 2;
                showTextBaselineY = textCenterVerticalBaselineY;
                break;
            case TEXT_ALIGN_RIGHT_CENTER://居中靠最右
                showTextCenterX = viewWidth - textWidth / 2;
                showTextBaselineY = textCenterVerticalBaselineY;
                break;
            case TEXT_ALIGN_CENTER_HORIZONTAL_BOTTOM://居中靠最下
                showTextCenterX = Float.valueOf(viewWidth / 2);
                showTextBaselineY = viewHeight - fm.bottom;
                break;
            case TEXT_ALIGN_CENTER_HORIZONTAL_TOP://居中靠最上
                showTextCenterX = Float.valueOf(viewWidth / 2);
                showTextBaselineY = -fm.ascent;
                break;
            case TEXT_ALIGN_LEFT_TOP://左上
                showTextCenterX = textWidth / 2;
                showTextBaselineY =  -fm.ascent;
                break;
            case TEXT_ALIGN_LEFT_BOTTOM://左下
                showTextCenterX = textWidth / 2;//
                showTextBaselineY = viewHeight - fm.bottom;
                break;
            case TEXT_ALIGN_RIGHT_TOP://右上
                showTextCenterX = viewWidth - textWidth / 2;
                showTextBaselineY = -fm.ascent;
                break;
            case TEXT_ALIGN_RIGHT_BOTTOM://右下
                showTextCenterX = viewWidth - textWidth / 2;
                showTextBaselineY = viewHeight - fm.bottom;
                break;
            case TEXT_ALIGN_CENTER_TOP://居中靠上
                showTextCenterX = (float)viewWidth / 2;
                showTextBaselineY = textCenterVerticalBaselineY - textHeight / 2;
                break;
            case TEXT_ALIGN_CENTER_BOTTOM:/** 居中靠下显示  */
                showTextCenterX = (float)viewWidth / 2;
                showTextBaselineY = textCenterVerticalBaselineY + textHeight / 2;
                break;
            case TEXT_ALIGN_CENTER_LEFT:/** 居中靠左显示  */
                showTextCenterX = (float)viewWidth / 2 - textWidth / 2 ;
                showTextBaselineY = textCenterVerticalBaselineY;
                break;
            case TEXT_ALIGN_CENTER_RIGHT:/** 居中靠右显示  */
                showTextCenterX = (float)viewWidth / 2 + textWidth / 2 ;
                showTextBaselineY = textCenterVerticalBaselineY;
                break;
            case TEXT_ALIGN_CENTER_LEFT_TOP:/** 居中靠左上显示  */
                showTextCenterX = (float)viewWidth / 2 - textWidth / 2 ;
                showTextBaselineY = textCenterVerticalBaselineY - textHeight / 2 ;
                break;
            case TEXT_ALIGN_CENTER_LEFT_BOTTOM:/** 居中靠左下显示  */
                showTextCenterX = (float)viewWidth / 2 - textWidth / 2 ;
                showTextBaselineY = textCenterVerticalBaselineY + textHeight / 2 ;
                break;
            case TEXT_ALIGN_CENTER_RIGHT_TOP:/** 居中靠右上显示  */
                showTextCenterX = (float)viewWidth / 2 + textWidth / 2 ;
                showTextBaselineY = textCenterVerticalBaselineY - textHeight / 2 ;
                break;
            case TEXT_ALIGN_CENTER_RIGHT_BOTTOM:/** 居中靠右下显示  */
                showTextCenterX = (float)viewWidth / 2 + textWidth / 2 ;
                showTextBaselineY = textCenterVerticalBaselineY + textHeight / 2 ;
                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 map = new HashMap<>();
        map.put(PieChartView.KEY_NAME,"red");
        map.put(PieChartView.KEY_PERCENT,40);
        map.put(PieChartView.KEY_COLOR, Color.RED);
        list.add(map);
        map = new HashMap<>();
        map.put(PieChartView.KEY_NAME,"gray");
        map.put(PieChartView.KEY_PERCENT,30);
        map.put(PieChartView.KEY_COLOR, Color.GRAY);
        list.add(map);
        map = new HashMap<>();
        map.put(PieChartView.KEY_NAME,"blue");
        map.put(PieChartView.KEY_PERCENT,10);
        map.put(PieChartView.KEY_COLOR, Color.BLUE);
        list.add(map);
        map = new HashMap<>();
        map.put(PieChartView.KEY_NAME,"black");
        map.put(PieChartView.KEY_PERCENT,20);
        map.put(PieChartView.KEY_COLOR, Color.BLACK);
        list.add(map);

源码下载链接:http://download.csdn.net/detail/cmwly/9591797

你可能感兴趣的:(自定义控件)