AndroidCharts为折线图表添加y坐标

来源:互联网 发布:java 字符编码转换 编辑:程序博客网 时间:2024/05/01 15:57

        AndroidCharts 是一款轻量级的图表显示控件,对比起Android-Charts和AChartEngine来说简单和活泼了很多,适合数据展示不需要太过详细专业的场合,它支持简单且带动画的折线图,柱状图和饼状图。

Line ChartBar ChartClock Pie Chart

        其中的linechart不支持y坐标显示,我们可以自己修改添加上去,修改后类LineView的代码如下,其中YCOORD_TEXT_LEFT_MARGIN为图标向右的偏移量,用来空出y坐标文字的空间:

package com.nekocode.xuedao.utils;import android.content.Context;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.graphics.Point;import android.graphics.Rect;import android.graphics.drawable.NinePatchDrawable;import android.util.AttributeSet;import android.view.View;import java.util.ArrayList;import com.nekocode.xuedao.R;/** * Created by Dacer on 11/4/13. */public class LineView extends View {    private int mViewHeight;    //drawBackground    private boolean autoSetDataOfGird = true;    private boolean autoSetGridWidth = true;    private int dataOfAGird = 10;    private int bottomTextHeight = 0;    private ArrayList<String> bottomTextList;    private ArrayList<Integer> dataList;    private ArrayList<Integer> xCoordinateList = new ArrayList<Integer>();    private ArrayList<Integer> yCoordinateList = new ArrayList<Integer>();    private ArrayList<Dot> drawDotList = new ArrayList<Dot>();;    private Paint bottomTextPaint = new Paint();    private Paint ycoordTextPaint = new Paint();    private int bottomTextDescent;    //popup    private Paint popupTextPaint = new Paint();    private final int bottomTriangleHeight = 12;    //private Dot selectedDot;    private boolean mShowYCoordinate = true;    private int topLineLength = MyUtils.dip2px(getContext(), 12);; // | | 鈫this                                                                   //-+-+-    private int sideLineLength = MyUtils.dip2px(getContext(),45)/3*2;// --+--+--+--+--+--+--                                                               //  鈫this           鈫    private int backgroundGridWidth = MyUtils.dip2px(getContext(),45);    //Constants    private final int popupTopPadding = MyUtils.dip2px(getContext(),2);    private final int popupBottomMargin = MyUtils.dip2px(getContext(),5);    private final int bottomTextTopMargin = MyUtils.sp2px(getContext(),5);    private final int bottomLineLength = MyUtils.sp2px(getContext(), 22);    private final int DOT_INNER_CIR_RADIUS = MyUtils.dip2px(getContext(), 2);    private final int DOT_OUTER_CIR_RADIUS = MyUtils.dip2px(getContext(),5);    private final int MIN_TOP_LINE_LENGTH = MyUtils.dip2px(getContext(),12);    private final int MIN_VERTICAL_GRID_NUM = 4;    private final int MIN_HORIZONTAL_GRID_NUM = 1;    private final int BACKGROUND_LINE_COLOR = Color.parseColor("#EEEEEE");    private final int BOTTOM_TEXT_COLOR = Color.parseColor("#9B9A9B");    private final int YCOORD_TEXT_LEFT_MARGIN = MyUtils.dip2px(getContext(), 10);        class YCoordData {    private int y;    private int data;public int getY() {return y;}public void setY(int y) {this.y = y;}public int getData() {return data;}public void setData(int data) {this.data = data;}    }    private Runnable animator = new Runnable() {        @Override        public void run() {            boolean needNewFrame = false;            for(Dot dot : drawDotList){                dot.update();                if(!dot.isAtRest()){                    needNewFrame = true;                }            }            if (needNewFrame) {                postDelayed(this, 0);            }            invalidate();        }    };    public LineView(Context context){        this(context,null);    }    public LineView(Context context, AttributeSet attrs){        super(context, attrs);        popupTextPaint.setAntiAlias(true);        popupTextPaint.setColor(Color.WHITE);        popupTextPaint.setTextSize(MyUtils.sp2px(getContext(), 13));        popupTextPaint.setStrokeWidth(5);        popupTextPaint.setTextAlign(Paint.Align.CENTER);        bottomTextPaint.setAntiAlias(true);        bottomTextPaint.setTextSize(MyUtils.sp2px(getContext(),12));        bottomTextPaint.setTextAlign(Paint.Align.CENTER);        bottomTextPaint.setStyle(Paint.Style.FILL);        bottomTextPaint.setColor(BOTTOM_TEXT_COLOR);                ycoordTextPaint.setAntiAlias(true);        ycoordTextPaint.setTextSize(MyUtils.sp2px(getContext(),12));        ycoordTextPaint.setTextAlign(Paint.Align.LEFT);        ycoordTextPaint.setStyle(Paint.Style.FILL);        ycoordTextPaint.setColor(BOTTOM_TEXT_COLOR);    }    /**     * dataList will be reset when called is method.     * @param bottomTextList The String ArrayList in the bottom.     */    public void setBottomTextList(ArrayList<String> bottomTextList){        this.dataList = null;        this.bottomTextList = bottomTextList;        Rect r = new Rect();        int longestWidth = 0;        String longestStr = "";        bottomTextDescent = 0;        for(String s:bottomTextList){            bottomTextPaint.getTextBounds(s,0,s.length(),r);            if(bottomTextHeight<r.height()){                bottomTextHeight = r.height();            }            if(autoSetGridWidth&&(longestWidth<r.width())){                longestWidth = r.width();                longestStr = s;            }            if(bottomTextDescent<(Math.abs(r.bottom))){                bottomTextDescent = Math.abs(r.bottom);            }        }        if(autoSetGridWidth){            if(backgroundGridWidth<longestWidth){                backgroundGridWidth = longestWidth+(int)bottomTextPaint.measureText(longestStr,0,1);            }            if(sideLineLength<longestWidth/2){                sideLineLength = longestWidth/2;            }        }        refreshXCoordinateList(getHorizontalGridNum());    }    /**     *     * @param dataList The Integer ArrayList for showing,     *                 dataList.size() must < bottomTextList.size()     */    public void setDataList(ArrayList<Integer> dataList){        this.dataList = dataList;        if(dataList.size() > bottomTextList.size()){            throw new RuntimeException("dacer.LineView error:" +                    " dataList.size() > bottomTextList.size() !!!");        }        if(autoSetDataOfGird){            int biggestData = 0;            for(Integer i:dataList){                if(biggestData<i){                    biggestData = i;                }            }            dataOfAGird = 1;            while(biggestData/10 > dataOfAGird){                dataOfAGird *= 10;            }        }        refreshAfterDataChanged();        setMinimumWidth(0); // It can help the LineView reset the Width,                                // I don't know the better way..        postInvalidate();    }        public void setShowYCoordinate(boolean showYCoordinate) {    mShowYCoordinate = showYCoordinate;    }    private void refreshAfterDataChanged(){        int verticalGridNum = getVerticalGridlNum();        refreshTopLineLength(verticalGridNum);        refreshYCoordinateList(verticalGridNum);        refreshDrawDotList(verticalGridNum);    }    private int getVerticalGridlNum(){        int verticalGridNum = MIN_VERTICAL_GRID_NUM;        if(dataList != null && !dataList.isEmpty()){            for(Integer integer:dataList){                if(verticalGridNum<(integer+1)){                    verticalGridNum = integer+1;                }            }        }        return verticalGridNum;    }    private int getHorizontalGridNum(){        int horizontalGridNum = bottomTextList.size()-1;        if(horizontalGridNum<MIN_HORIZONTAL_GRID_NUM){            horizontalGridNum = MIN_HORIZONTAL_GRID_NUM;        }        return horizontalGridNum;    }    private void refreshXCoordinateList(int horizontalGridNum){        xCoordinateList.clear();        for(int i=0;i<(horizontalGridNum+1);i++){xCoordinateList.add(sideLineLength + backgroundGridWidth*i);        }    }    private void refreshYCoordinateList(int verticalGridNum){        yCoordinateList.clear();        for(int i=0;i<(verticalGridNum+1);i++){            yCoordinateList.add(topLineLength +            ((mViewHeight-topLineLength-bottomTextHeight-bottomTextTopMargin-            bottomLineLength-bottomTextDescent)*i/(verticalGridNum)));        }    }    private void refreshDrawDotList(int verticalGridNum){        if(dataList != null && !dataList.isEmpty()){            int drawDotSize = drawDotList.isEmpty()? 0:drawDotList.size();            for(int i=0;i<dataList.size();i++){                int x = xCoordinateList.get(i);                int y = yCoordinateList.get(verticalGridNum - dataList.get(i));                if(i>drawDotSize-1){                    drawDotList.add(new Dot(x, 0, x, y, dataList.get(i)));                }else{                    drawDotList.set(i, drawDotList.get(i).setTargetData(x,y,dataList.get(i)));                }            }            int temp = drawDotList.size() - dataList.size();            for(int i=0; i<temp; i++){                drawDotList.remove(drawDotList.size()-1);            }        }        removeCallbacks(animator);        post(animator);    }    private void refreshTopLineLength(int verticalGridNum){        // For prevent popup can't be completely showed when backgroundGridHeight is too small.        // But this code not so good.        if((mViewHeight-topLineLength-bottomTextHeight-bottomTextTopMargin)/                (verticalGridNum+2)<getPopupHeight()){            topLineLength = getPopupHeight()+DOT_OUTER_CIR_RADIUS+DOT_INNER_CIR_RADIUS+2;        }else{            topLineLength = MIN_TOP_LINE_LENGTH;        }    }    @Override    protected void onDraw(Canvas canvas) {        drawBackgroundLines(canvas);        drawLines(canvas);        drawDots(canvas);        for(Dot dot : drawDotList){        drawPopup(canvas,                    String.valueOf(dot.data),                    dot.getPoint());        }        /*        if(showPopup && selectedDot != null){            drawPopup(canvas,                    String.valueOf(selectedDot.data),                    selectedDot.getPoint());        }*/    }    /**     *     * @param canvas  The canvas you need to draw on.     * @param point   The Point consists of the x y coordinates from left bottom to right top.     *                Like is              3     *                2     *                1     *                0 1 2 3 4 5     */    private void drawPopup(Canvas canvas,String num, Point point){        boolean singularNum = (num.length() == 1);        int sidePadding = MyUtils.dip2px(getContext(),singularNum? 8:5);        int x = point.x;        if(mShowYCoordinate == true) x += YCOORD_TEXT_LEFT_MARGIN;        int y = point.y-MyUtils.dip2px(getContext(),5);        Rect popupTextRect = new Rect();        popupTextPaint.getTextBounds(num,0,num.length(),popupTextRect);        Rect r = new Rect(x-popupTextRect.width()/2-sidePadding,                y - popupTextRect.height()-bottomTriangleHeight-popupTopPadding*2-popupBottomMargin,                x + popupTextRect.width()/2+sidePadding,                y+popupTopPadding-popupBottomMargin);        Bitmap bmp = BitmapFactory.decodeResource(getResources(), R.drawable.popup_red);        byte chunk[] = bmp.getNinePatchChunk();        NinePatchDrawable popup = new NinePatchDrawable(bmp, chunk, new Rect(), null);        popup.setBounds(r);        popup.draw(canvas);        canvas.drawText(num, x, y-bottomTriangleHeight-popupBottomMargin, popupTextPaint);    }    private int getPopupHeight(){        Rect popupTextRect = new Rect();        popupTextPaint.getTextBounds("9",0,1,popupTextRect);        Rect r = new Rect(-popupTextRect.width()/2,                 - popupTextRect.height()-bottomTriangleHeight-popupTopPadding*2-popupBottomMargin,                 + popupTextRect.width()/2,                +popupTopPadding-popupBottomMargin);        return r.height();    }    private void drawDots(Canvas canvas){        Paint bigCirPaint = new Paint();        bigCirPaint.setAntiAlias(true);        bigCirPaint.setColor(Color.parseColor("#FF0033"));        Paint smallCirPaint = new Paint(bigCirPaint);        smallCirPaint.setColor(Color.parseColor("#FFFFFF"));        if(drawDotList!=null && !drawDotList.isEmpty()){            for(Dot dot : drawDotList){            int x = dot.x;            if(mShowYCoordinate == true) x += YCOORD_TEXT_LEFT_MARGIN;                canvas.drawCircle(x,dot.y,DOT_OUTER_CIR_RADIUS,bigCirPaint);                canvas.drawCircle(x,dot.y,DOT_INNER_CIR_RADIUS,smallCirPaint);            }        }    }    private void drawLines(Canvas canvas){        Paint linePaint = new Paint();        linePaint.setAntiAlias(true);        linePaint.setColor(Color.parseColor("#FF0033"));        linePaint.setStrokeWidth(MyUtils.dip2px(getContext(), 2));        for(int i=0; i<drawDotList.size()-1; i++){        int x1 = drawDotList.get(i).x;        int x2 = drawDotList.get(i+1).x;        if(mShowYCoordinate == true) {        x1 += YCOORD_TEXT_LEFT_MARGIN;        x2 += YCOORD_TEXT_LEFT_MARGIN;        }            canvas.drawLine(x1,                    drawDotList.get(i).y,                    x2,                    drawDotList.get(i+1).y,                    linePaint);        }    }    private void drawBackgroundLines(Canvas canvas){        Paint paint = new Paint();        paint.setStyle(Paint.Style.STROKE);        paint.setStrokeWidth(MyUtils.dip2px(getContext(),1f));        paint.setColor(BACKGROUND_LINE_COLOR);        //draw vertical lines        for(int i=0;i<xCoordinateList.size();i++){        int x = xCoordinateList.get(i);        if(mShowYCoordinate == true) x += YCOORD_TEXT_LEFT_MARGIN;            canvas.drawLine(x, 0, x,                    mViewHeight - bottomTextTopMargin - bottomTextHeight-bottomTextDescent,                    paint);        }        for(int i=0;i<yCoordinateList.size();i++){            if((yCoordinateList.size()-1-i)%dataOfAGird == 0){            int y = yCoordinateList.get(i);                canvas.drawLine(0, y, getWidth(), y, paint);                                if(mShowYCoordinate == true)                canvas.drawText(String.valueOf(yCoordinateList.size()-i-1), 0, y, ycoordTextPaint);            }        }        //draw bottom text        if(bottomTextList != null){            for(int i=0;i<bottomTextList.size();i++){            int x = sideLineLength+backgroundGridWidth*i;            if(mShowYCoordinate == true) x += YCOORD_TEXT_LEFT_MARGIN;                canvas.drawText(bottomTextList.get(i), x,                        mViewHeight-bottomTextDescent, bottomTextPaint);            }        }    }    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        int mViewWidth = measureWidth(widthMeasureSpec);        mViewHeight = measureHeight(heightMeasureSpec);        refreshAfterDataChanged();        setMeasuredDimension(mViewWidth,mViewHeight);    }    private int measureWidth(int measureSpec){        int horizontalGridNum = getHorizontalGridNum();        int preferred = backgroundGridWidth*horizontalGridNum+sideLineLength*2;        return getMeasurement(measureSpec, preferred);    }    private int measureHeight(int measureSpec){        int preferred = 0;        return getMeasurement(measureSpec, preferred);    }    private int getMeasurement(int measureSpec, int preferred){        int specSize = MeasureSpec.getSize(measureSpec);        int measurement;        switch(MeasureSpec.getMode(measureSpec)){            case MeasureSpec.EXACTLY:                measurement = specSize;                break;            case MeasureSpec.AT_MOST:                measurement = Math.min(preferred, specSize);                break;            default:                measurement = preferred;                break;        }        return measurement;    }    /*    @Override    public boolean onTouchEvent(MotionEvent event) {        Point point = new Point();        point.x = (int) event.getX();        point.y = (int) event.getY();        Region r = new Region();        int width = backgroundGridWidth/2;        if(drawDotList != null || !drawDotList.isEmpty()){            for(Dot dot : drawDotList){                r.set(dot.x-width,dot.y-width,dot.x+width,dot.y+width);                if (r.contains(point.x,point.y) && event.getAction() == MotionEvent.ACTION_DOWN){                    selectedDot = dot;                }else if (event.getAction() == MotionEvent.ACTION_UP){                    if (r.contains(point.x,point.y)){                        showPopup = true;                    }                }            }        }        if (event.getAction() == MotionEvent.ACTION_DOWN ||                event.getAction() == MotionEvent.ACTION_UP){            postInvalidate();        }        return true;    }//*/        private int updateSelf(int origin, int target, int velocity){        if (origin < target) {            origin += velocity;        } else if (origin > target){            origin-= velocity;        }        if(Math.abs(target-origin)<velocity){            origin = target;        }        return origin;    }        class Dot{        int x;        int y;        int data;        int targetX;        int targetY;        int velocity = MyUtils.dip2px(getContext(),20);        Dot(int x,int y,int targetX,int targetY,Integer data){            this.x = x;            this.y = y;            setTargetData(targetX, targetY,data);        }        Point getPoint(){            return new Point(x,y);        }        Dot setTargetData(int targetX,int targetY,Integer data){            this.targetX = targetX;            this.targetY = targetY;            this.data = data;            return this;        }        boolean isAtRest(){            return (x==targetX)&&(y==targetY);        }        void update(){            x = updateSelf(x, targetX, velocity);            y = updateSelf(y, targetY, velocity);        }    }}

        修改后我们可以通过增加的setShowYCoordinate方法设置是否显示y坐标文字。


2 1