在这篇博客中,将实现混合柱状图

android 统计图控件 安卓 图表控件_android 统计图控件

在上一篇博客中讲到过,所有的图表类都只要重载
abstract protected void startContent(Canvas canvas);//绘制图表内容
abstract protected void drawXaxis(Canvas canvas);//绘制X轴
abstract protected void drawYaxis(Canvas canvas);//绘制Y轴
接下来就看看柱状图的这三个方法的具体实现

@Override
    public void drawXaxis(Canvas canvas) {
        XaxisData xaxisData=dataManager.getxData();//从管理器中取出描述X轴的实体类
        int width=canvas.getWidth();
        Paint paint=getPaint(xaxisData.getLineWidth(),xaxisData.getLineColor());
        canvas.drawLine(originPoint.x,originPoint.y,width,originPoint.y,paint);//从原点开始绘制一条直线作为X轴
        Paint textpaint=getPaint(3,xaxisData.getLineColor());
        textpaint.setTextSize(xaxisData.getTextsize());
        List<Integer> xvaluesRelaticeX=getXvaluesRelativeX(xaxisData,canvas,textpaint);//取得每个柱状图中心点的X坐标
          //接下来是绘制每个柱状图在X轴上的描述,就是“1月”,“2月”。。。那些文字描述
        Paint rectPaint=new Paint();
        rectPaint.setStrokeJoin(Paint.Join.ROUND);
        rectPaint.setTextSize(xaxisData.getTextsize());
        for(int i=0;i<xvaluesRelaticeX.size();i++){
        //先绘制一个圆角矩形,再绘制文字
            textpaint.setColor(xaxisData.getXdataList().get(i).getColor());
            rectPaint.setColor(xaxisData.getXdataList().get(i).getBackgroundColor());
         //   矩形两边留出10,作为矩形的padding
            int left=xvaluesRelaticeX.get(i)-10;
           //头部偏移20是矩形和X轴之间留出空隙
            int top=originPoint.y+20;
            int right= (int) (xvaluesRelaticeX.get(i)+textpaint.measureText(xaxisData.getXdataList().get(i).getText())+10);
            int bottom=top+xaxisData.getTextsize()+15;
            canvas.drawRoundRect(new RectF(left,top,right,bottom),10,10,rectPaint);
             //文字居中Y坐标计算参考http://blog.csdn.net/doctorzhong/article/details/53079511
            int centerY= (int) ((top+bottom)/2-(textpaint.ascent()+textpaint.descent())/2);
            canvas.drawText(xaxisData.getXdataList().get(i).getText(),xvaluesRelaticeX.get(i),centerY,textpaint);
        }
    }

Y轴的绘制函数

@Override
    public void drawYaxis(Canvas canvas) {

        int rangNum=dataManager.getRangeNum();     //获取Y轴要分成几段
        int maxValue=dataManager.getMaxValues();   //Y轴能表示的最大数据    
        int yaxisLength=getYaxisLength();         //Y轴的长度

        Paint yaxisLinePait=getPaint(dataManager.getYaxisLineWidth(),dataManager.getYaxisLineColor());
        canvas.drawLine(originPoint.x,originPoint.y,originPoint.x,originPoint.y-yaxisLength-30,yaxisLinePait);//绘制Y轴

        Paint paint=getPaint(20, Color.GRAY);

         int rangeHeight=yaxisLength/rangNum; //计算Y轴分段后每一段的高度
         int rangeValues=maxValue/rangNum;    //计算每一段表示的数值大小 
          yaxisLinePait.setTextSize(dataManager.getYaxisTextsize());
        yaxisLinePait.setColor(dataManager.getYaxisTextColor());
        int xaxisLength=getXaxisLength(canvas);
        Paint xuxianPaint=new Paint();//绘制虚线的画笔
        xuxianPaint.setColor(Color.GRAY);
        xuxianPaint.setStyle(Paint.Style.STROKE);//一定要设置这个属性,不然绘制不出虚线
        for(int i=0;i<=rangNum;i++){
              int yvalues=i*rangeValues;
              int yaxis=i*rangeHeight;
              int textwidth= (int) yaxisLinePait.measureText(yvalues+"");
              int x=originPoint.x-textwidth-10;
              int y=originPoint.y-yaxis;
              canvas.drawText(yvalues+"",x,y,yaxisLinePait);
               if(i!=0){
                   Path path = new Path();
                   path.moveTo(originPoint.x, y);
                   path.lineTo(originPoint.x+xaxisLength,y);
                   PathEffect effects = new DashPathEffect(new float[]{5,5},5);
                   xuxianPaint.setPathEffect(effects);
                   canvas.drawPath(path, xuxianPaint);
               }
          }
    }

最后是绘制图表的内容,就是那些柱状矩形,提示的泡泡

@Override
    public void startContent(Canvas canvas) {
           allRectangel.clear();
        List<YaxisData> ydataSet=dataManager.getyDataSet();//获取Y轴数据集合
        List<Integer> xrelativeValue=getXvaluesRelativeCenterX(dataManager.getxData(),canvas);//获取每个柱状图中心点的坐标
        for(int i=0;i<ydataSet.size();i++){
            YaxisData yaxisData=ydataSet.get(i);
            List<Integer> yrelativeValus=getYvaluesRelativeHeight(ydataSet.get(i));    //获取某个种类每个Y轴坐标对应的高度
            Paint rectPaint=getPaint(5,yaxisData.getColor());
            for(int j=0;j<xrelativeValue.size();j++){
                int x=xrelativeValue.get(j);
                int y=yrelativeValus.get(j);
                int left=x-rectangleWidth/2;
                int top=y;
                int right=x+rectangleWidth/2;
                int bottom=originPoint.y;
                Rect r=new Rect(left,top,right,bottom);
                canvas.drawRect(r,rectPaint);
                if(i==0){
                    allRectangel.add(r);//把每个最高的矩形存起来,后面有大作用,我们公司的业务定好了第一层数据就是最大的,所以取i==0时的矩形就行
                }
            }
        }
    }

到这里这个柱状图就绘制完了,还差一步,点击某个柱状图的时候,矩形顶部出现该柱状图详情数据的泡泡
先看看触摸事件这里的处理

@Override
    public boolean onTouchEvent(MotionEvent event) {

        switch (event.getAction()){
            case MotionEvent.ACTION_DOWN:
              selectValues=drawUtil.getInformationByEventMotion(event);//获取触摸点的信息,selectValues封装了摸出到的矩形位置,该矩形的顶部坐标等信息,一会再详细介绍这个函数,如果触摸到矩形的话,selectValues就不为null,然后就是更新数据了,重新绘制View
                 if(selectValues!=null){
                     XaxisData xaxisData=chartDataManager.getxData();
                     int size=xaxisData.getXdataList().size();
                     for(int i=0;i<size;i++){
                         chartDataManager.getxData().getXdataList().get(i).setColor(Color.BLACK);
                         chartDataManager.getxData().getXdataList().get(i).setBackgroundColor(Color.WHITE);
                     }
                     chartDataManager.getxData().getXdataList().get(selectValues.getPosition()).setColor(Color.WHITE);
                     chartDataManager.getxData().getXdataList().get(selectValues.getPosition()).setBackgroundColor(Color.GREEN);
                     invalidate();
                 }
                break;
        }
        return false;
    }

这里再介绍一下getInformationByEventMotion(event)方法

public RectTouchInformation getInformationByEventMotion(MotionEvent event){
        RectTouchInformation selectYvalues=new RectTouchInformation();
        int position=0;
        boolean flag=false;
        //遍历上面绘制图表矩形时存起来的矩形集合,判断手指触摸的是哪个矩形
        for(int i=0;i<allRectangel.size();i++){
            Rect r=allRectangel.get(i);
           if(r.contains((int)event.getX(),(int)event.getY())){
               position=i;
               flag=true;
               break;
           }
        }

        if(!flag){
            return  null;
        }
        //获取选中的每个柱状图的信息
      selectYvalues.setPosition(position);
       int size=dataManager.getyDataSet().size();
        List<YaxisData> allYaxisData=dataManager.getyDataSet();
        List<Yvalues> selectYpoint=new ArrayList<>();
        for(int i=0;i<size;i++){
            YaxisData yaxisData=allYaxisData.get(i);
            Yvalues yvalues=yaxisData.getYdataList().get(position);
            yvalues.setDescription(yaxisData.getDescription());
            selectYpoint.add(yvalues);
        }
    //把选中矩形顶部中心的坐标封装进selectValues
      Rect rect=allRectangel.get(position);
      Point point=new Point(rect.left+rect.width()/2,rect.top);
        selectYvalues.setRectTopLineCenter(point);
        selectYvalues.setSelectValues(selectYpoint);
        return  selectYvalues;

    }

接下来是看看如何把泡泡绘制出来的

public void drawSelectImage(Context context,Canvas canvas, RectTouchInformation selectRectInfor){

        Point point=selectRectInfor.getRectTopLineCenter();

        LinearLayout layout=new LinearLayout(context);
        layout.setBackgroundColor(Color.WHITE);
        layout.setOrientation(LinearLayout.VERTICAL);
        int size=selectRectInfor.getSelectValues().size();
        for(int i=0;i<size;i++){
            Yvalues yvalues=selectRectInfor.getSelectValues().get(i);
            TextView textView=new TextView(context);
            textView.setText(yvalues.getDescription()+":"+yvalues.getValue());
            LinearLayout.LayoutParams params=new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
            textView.setPadding(10,2,10,2);
            textView.setTextSize(10);
            textView.setLayoutParams(params);
            layout.addView(textView);
        }
       //泡泡的内容首先是把它展示在View上,然后从这个View上获取到bitmap,然后绘制这个Bitmap就行,如果还是使用绘制矩形和文字的方法来实在是太麻烦了,绘制一行一行的文字就愁死人了
       Bitmap selectBitmap=convertViewToBitmap(layout);

        int width=selectBitmap.getWidth();
        int height=selectBitmap.getHeight();
        int x=point.x-width/2;
        int y=point.y-height-20;
        Paint paint=new Paint();
        paint.setAntiAlias(true);
        paint.setDither(true);
        paint.setColor(dataManager.getyDataSet().get(0).getColor());
        paint.setStrokeJoin(Paint.Join.ROUND);
        paint.setStrokeWidth(2);
        paint.setStyle(Paint.Style.STROKE);
        RectF rectF=new RectF(x-2,y-2,x+width+2,y+height+2);
        //这里是给泡泡设置偏移量,以防泡泡过长超出控件了
        if(rectF.left<originPoint.x){
            int offset= (int) (originPoint.x-rectF.left+30);
            x+=offset;
            rectF.left=rectF.left+offset;
            rectF.right=rectF.right+offset;
        }else if(rectF.right>canvas.getWidth()){
            int offset= (int) (rectF.right-canvas.getWidth()+30);
            x-=offset;
            rectF.left=rectF.left-offset;
            rectF.right=rectF.right-offset;
        }
        //最后是绘制出指示箭头
        canvas.drawRoundRect(rectF,10,10,paint);
        canvas.drawLine(point.x,point.y,point.x-10,rectF.bottom,paint);
        canvas.drawLine(point.x,point.y,point.x+10,rectF.bottom,paint);
        paint.setColor(Color.WHITE);
        canvas.drawLine(point.x,rectF.bottom,point.x-10,rectF.bottom,paint);
        canvas.drawLine(point.x,rectF.bottom,point.x+10,rectF.bottom,paint);
        canvas.drawBitmap(selectBitmap,x,y,new Paint());
    }